Javascript 函数在前几次迭代中有效,然后失败

Javascript function works for first few iterations then fails

提问人:Tom 提问时间:11/14/2023 更新时间:11/15/2023 访问量:67

问:

我们使用 JS 来打开和关闭我们网站上的导航面板。每个导航栏选项都是一个 href 链接,用于未启用 JS 的人访问我们的网站。我们正在使用 JS 拦截链接并通过 CSS 更改打开面板。

该函数在调用的前几次运行良好,然后失败并遵循默认链接。在失败之前似乎没有固定的迭代次数,但 nvbtn3 似乎是它失败最多的一个。

不知道如何找到治疗方法。这几乎就像它超载并退出一样。有什么建议吗?

在导航栏上查找点击的事件委派部分

$('mstrwrap').addEventListener('click', function(e) {
  // set eventlisteners for all A HREF links
  if (e.target.nodeName === 'A') {
    e.preventDefault();

    // check if is navbar link
    if ((e.target.id.includes('nvbtn'))) {
      // determine matching nav panel
      const nvpnl = e.target.id;
      const lk = 'nav' + nvpnl.match(/\d+/g);
      // open nav panel
      openNav(lk, nvpnl);
    }
    // end if navbar link

  // lots more possible targets

  }
}

用于打开导航菜单的JS函数

function openNav(x, y) {
  // if already open, close it
  if ($(x).style.maxHeight === '100vh') {
    // close
    $(x).style.maxHeight = '0';
    $(x).style.zIndex = '';
    $(y).classList.remove('nvopen');
    $('nvbar').classList.remove('nvopen2');
  }

  // close any that are open, then open target
  else {
    // close all pnl, open specified pnl
    const targets = document.querySelectorAll('[id^="nav"]');
    for (let i = targets.length; i--;) {
      targets[i].style.maxHeight = '0';
      targets[i].style.zIndex = '';
    }
    const nvbtns = document.querySelectorAll('[id^="nvbtn"]');
    for (let i = nvbtns.length; i--;) {
      nvbtns[i].classList.remove('nvopen');
    }
    $(x).style.maxHeight = '100vh';
    $(x).style.zIndex = '5';
    $(y).classList.add('nvopen');
    $('nvbar').classList.add('nvopen2');
  }
}

我们使用伪jQuery样式变量

var $ = function (selector) {
  return document.getElementById(selector);
};

导航栏是简单的 href 链接

<a href="https://example.com/menu1.htm" id="nvbtn1">Menu 1</a>
<a href="https://example.com/menu2.htm" id="nvbtn2">Menu 2</a>
<a href="https://example.com/menu3.htm" id="nvbtn3">Menu 3</a>
JavaScript 函数

评论

0赞 Bergi 11/14/2023
菜单链接是否有可能包含嵌套元素,因此不适用?if (e.target.nodeName === 'A')
2赞 Legends 11/14/2023
在我们开始猜测之前,您可以创建一个可重复的工作示例......
1赞 Dave Newton 11/14/2023
无关:让我在阅读代码时思考得比我想要的更多。for (let i = nvbtns.length; i--;)
0赞 Tom 11/14/2023
@Bergi 导航栏中的链接如您所见(当然名称和网址会发生变化)
0赞 Tom 11/14/2023
@Legends网站是链接

答:

2赞 Kaddath 11/14/2023 #1

Bergi 在评论中是对的,这就是为什么我们要求提供更多可能的细节并要求一个可重复的例子,因为在简化问题的代码时,你摆脱了问题(试图找到导致你的问题的最小例子可以帮助你自己解决它,这是一个很好的思考过程)

您的链接不仅仅是文本,它们包含以下元素:

<a class="xp" href="..." id="nvbtn3">Book <span class="nvt">|</span><span class="nvd">Now ✎</span> Ask Us </a>

因此,当您单击文本之间的小图标时,就会发生错误,因为测试失败if (e.target.nodeName === 'A') { }

编辑(可能更简单): 还有另一种解决方案,在链接内的所有元素上使用 CSS,例如 .然后,该事件不应在跨度上触发,而应在父链路上触发。pointer-events: none;<span>

源语言: 为了解决这个问题,您可以使用以下测试,但要小心,因为元素结构的更改可能会弄乱它(一个可靠的解决方案是递归测试父元素,直到你发现你是否在链接中):<a>

if (e.target.nodeName === 'A' || (e.target.nodeName === 'SPAN' && e.target.parentNode.nodeName === 'A')) {
  //your code
}

您可以添加其他测试,例如 的类(如果它适合您的用例)。<span>

评论

0赞 CherryDT 11/14/2023
或者也许只是:if (e.target.closest('a'))
0赞 Kaddath 11/14/2023
@CherryDT我认为实际上更简单的方法是在链接内的所有元素上使用,正如我在编辑中添加的那样。这应该可以通过单个 CSS 规则实现pointer-events: none;
0赞 Tom 11/15/2023
谢谢,指针事件建议就像一个魅力。
0赞 Kaddath 11/15/2023
@Tom不客气,祝你好运!
1赞 traktor 11/14/2023 #2

另一种设计方案:

将 javascript 禁用菜单放在开始和结束标签对中。如果属性仅用于 JS,而不用于样式设置,则可能省略属性。<noscript>...</script>id

<noscript>
  <a href="https://example.com/menu1.htm">Menu 1</a>
  <a href="https://example.com/menu2.htm">Menu 2</a>
  <a href="https://example.com/menu3.htm">Menu 3</a>
</noscript>

将菜单元素放在样式属性设置为的元素中,并设置用于键导航的选项卡索引。navnone

<nav style="display: none">
    <span class="navbtn" id="nvbtn1" tabindex="0">Menu 1</span>
    <span class="navbtn" id="nvbtn2" tabindex="0">Menu 2</span>
    <span class="navbtn" id="nvbtn3" tabindex="0">Menu 3</span>
</nav>

在 JS 启动代码中,解析正文后,从元素中删除样式:display:nonenav

document.querySelectAll('nav').forEach(el=>{
    el.style.display="revert";
}

  • 这种设计应该适用于客户端在关闭 JS 时遇到麻烦的浏览器。它不适用于不支持 CSS 和元素属性的浏览器。style

  • 元素的内容不必是链接,上面的示例显示了旨在由 Click 或键盘事件处理程序管理的构成子元素。实际的子元素将取决于应用程序(对于页面内容中的导航,可以是链接)。navnav

  • 主要的好处是不再需要编写或调试“拦截链接”的代码。