如何有效/正确地处理多个嵌套的基于列表的菜单/导航的任何列表项的点击事件?

问题描述 投票:0回答:2

抱歉,如果我的问题确实是针对新手级别的......

我有一个 3 级列表来构建菜单,但我不想使用 href。 我想改用特定的属性并用它的值调用 javascript 函数。

这是我的示例列表的标记:

<ul class="menu">
<li><a href="#" link_value="item 1">Firts level Menu item 1</a></li>
    <ul>
        <li><a href="#" link_value="item 2-1">Second level Menu item 1</a></li>
            <ul>
                <li><a href="#" link_value="item 3-2-1">Third level Menu item 1</a></li>
                <li><a href="#" link_value="item 3-2-2">Third level Menu item 2</a></li>
                <li><a href="#" link_value="item 3-2-3">Third level Menu item 3</a></li>
            </ul>
        <li><a href="#" link_value="item 2-2">Second level Menu item 2</a></li>
        <li><a href="#" link_value="item 2-3">Second level Menu item 3</a></li>
    </ul>
<li><a href="#" link_value="item 2">Firts level Menu item 2</a></li>
<li><a href="#" link_value="item 3">Firts level Menu item 3</a></li>
我怎样才能运行一个javascript函数来为所有项目设置onclick事件以具有类似的内容
<a onclick="my_fynction()">
function my_fonction() {do_something(link_value)}

并且还向活动元素及其父级添加一个“活动”类,并从其他元素中删除活动类?

希望我已经说得足够清楚了? 非常感谢!!!

javascript event-handling dom-events selectors-api event-delegation
2个回答
2
投票

您可以监听

ul.menu
上的点击,并将事件逻辑仅放在该元素上。

document.querySelector('ul.menu').addEventListener('click', event => {
    // event.target is clicked element
    if(event.target.tagName!=="A") return;
    // prevent default behaviour; redirecting
    event.preventDefault();
    // your code...
})

但是,如果您将

<span>
放在
<a>
内,则需要小心。您需要将 CSS
pointer-events: none;
放在这些元素上。


0
投票

关于我的上述两条评论...

@jeromebg ...

link_value
是无效的属性名称。请考虑使用
data-*
全局属性
和 DOM 元素的相关
dataset
属性
... 例如 ...
<a href="#" data-value="item 3"/>
,其中每个值都可以这样读取 ...
 linkElement.dataset.value

@jeromebg ...嵌套的

<ul/>
标记也已损坏/无效...请修复它。

...修复标记后,应该使用(如已建议但未明确命名的)event-delegation

后者不仅意味着在公共(外部)根节点处监听,还意味着针对感兴趣的元素,这些元素(如根节点)也可能具有子元素节点。

因此,人们总是必须查询自己感兴趣的元素。人们主要通过利用

closest
event
元素的
target
方法来实现结果。

关于OP的另一个要求......

...还向活动元素及其父级添加一个“活动”类,并从其他元素中删除活动类?

...处理程序函数会将当前标识的链接元素传递给自定义实现的函数,从而转发这样的特殊任务,而不是自行处理此类内容。

标记当前菜单项的函数必须执行以下操作...

  • 识别菜单项节点...由...完成

    const menuItem = elmLink.closest('li');
    
  • 识别菜单根节点...由...完成

    const menuRoot = menuItem.closest('ul.menu');
    
  • 选择正确的选择器来查询具有链接元素(例如

    'li:has(a[href])'
    )的任何列表项节点,并从每个查询的元素中删除预期的类名称(和/或例如aria属性)。

  • 将预期的类名(和/或例如 aria 属性)添加到已标识为 current 的列表项。

无需将另一个列表项的 current 状态涉及的任何列表项也标记为 current。通过利用像

:has()

这样的函数式 CSS 伪类,可以轻松实现这种状态的视觉表示

function markCurrentMenuItem(elmLink) {
  const menuItem = elmLink.closest('li');
  const menuRoot = menuItem.closest('ul.menu');

  menuRoot
    .querySelectorAll('li:has(a[href])')
    .forEach(elm => {

      // elm.classList.remove('active');
      elm.removeAttribute('aria-current');
    });
  // menuItem.classList.add('active');
  menuItem.setAttribute('aria-current', 'true');
}
document
  .querySelector('ul.menu')
  .addEventListener('click', evt => {
    const elmLink = evt.target.closest('a[href][data-value]')

    if (elmLink) {
      evt.preventDefault();

      markCurrentMenuItem(elmLink);

      console.log({ value: elmLink.dataset.value });
    }
  })
body { margin: 0; }
ul { list-style: none; margin: 0; padding: 0 0 0 20px; }
li { padding: 2px 0 2px 0; }

a span { display: inline-block; padding: 0 20px; background-color: #f2ffa7; }

/* li.active, */li[aria-current="true"] a > span {
  background-color: #b1d000;
}
/* ul:has(> li.active), */ul:has(> li[aria-current="true"]) {
  background-color: #bee8ff;
}
.as-console-wrapper { left: auto!important; width: 50%; min-height: 100%; }
<ul class="menu" role="menu" aria-label="Main Menu">
  <li>
    <a href="#" data-value="item 1">
      <span>First level Menu item 1</span>
    </a>
  </li>
  <li>
    <ul role="menu" aria-label="Second Level Menu">
      <li>
        <a href="#" data-value="item 2-1">
          <span>Second level Menu item 1</span>
        </a>
      </li>
      <li>
        <ul role="menu" aria-label="Third Level Menu">
          <li>
            <a href="#" data-value="item 3-2-1">
              <span>Third level Menu item 1</span>
            </a>
          </li>
          <li>
            <a href="#" data-value="item 3-2-2">
              <span>Third level Menu item 2</span>
            </a>
          </li>
          <li>
            <a href="#" data-value="item 3-2-3">
              <span>Third level Menu item 3</span>
            </a>
          </li>
        </ul>
      </li>
      <li>
        <a href="#" data-value="item 2-2">
          <span>Second level Menu item 2</span>
        </a>
      </li>
      <li>
        <a href="#" data-value="item 2-3">
          <span>Second level Menu item 3</span>
        </a>
      </li>
    </ul>
  </li>
  <li>
    <a href="#" data-value="item 2">
      <span>First level Menu item 2</span>
    </a>
  </li>
  <li>
    <a href="#" data-value="item 3">
      <span>First level Menu item 3</span>
    </a>
  </li>
</ul>

© www.soinside.com 2019 - 2024. All rights reserved.