【问题标题】:Javascript Event Delegation / bubblingJavascript事件委托/冒泡
【发布时间】:2020-01-10 19:16:24
【问题描述】:

我想在 SPA 路由器的正文部分禁用所有具有“nolink”类的链接。为了实现这一点,我使用了事件委托,它不适用于嵌套元素。 (下面的简化代码)。

HTML:

<header id="header">
  <nobr>
    <a href="home" class="nolink">
      <img src="image.png">
      <span>Title</span>
    </a>
  </nobr>
  <ul>
    <li><a href="link1" class="nolink">Link 1</a></li>
    <li><a href="link2" class="nolink">Link 2</a></li>
    <li><a href="link3" class="nolink">Link 3</a></li>
    <li><a href="link4" class="nolink">Link 4</a></li>
  </ul>
</header>

JavaScript:

class Delegator {
  constructor(wrapper) {
    this.wrapper = wrapper || document.body;
  }

  add({ selector, event, callback }) {
    this.wrapper.addEventListener(event, e => {
      const target = e.target;
      if (target.matches(selector) /* bad code: */ || target.parentElement.matches(selector) || target.parentElement.parentElement.matches(selector)) {
        e.preventDefault();
        callback(e);
      }
    })
  }
}

function readyLink(event) {
  let href = event.target.href /* bad code: */ || event.target.parentElement.href || event.target.parentElement.parentElement.href;
  history.pushState(null, null, href);
  event.preventDefault();
  router.fetch(href.split("/").pop());
  router.route(href);
}

const router = new Router(...);
const bodyDelegator = new Delegator();
bodyDelegator.add({
  selector: `a.${nolink}`,
  event: "click",
  callback: readyLinks
});

单击图像或 span 元素时,由于事件路由,我不得不引用 target.parentElements 让我很困扰。我希望它们受到 add-function 的影响,而不必指定一千个 parentElements 以备将来使用。

【问题讨论】:

    标签: javascript event-delegation


    【解决方案1】:

    使用Element.closest() 查找元素本身或其父元素之一是否与选择器匹配:

    class Delegator {
      constructor(wrapper) {
        this.wrapper = wrapper || document.body;
      }
    
      add({ selector, event, callback }) {
        this.wrapper.addEventListener(event, e => {
          const target = e.target;
    
          if (target.closest(selector)) {
            e.preventDefault();
            callback(e);
          }
        })
      }
    }
    

    传递最接近的回调:

    class Delegator {
      constructor(wrapper) {
        this.wrapper = wrapper || document.body;
      }
    
      add({ selector, event, callback }) {
        this.wrapper.addEventListener(event, e => {
          const target = e.target;
    
          const actual = target.closest(selector);
    
          if (actual) {
            e.preventDefault();
            callback(e, actual);
          }
        })
      }
    }
    

    然后 readyLink 可以从实际元素中获取href。我仍然会将事件传递给其他用途(例如调用preventDefault)。

    function readyLink(event, actualTarget) {
      let href = actualTarget.href;
      history.pushState(null, null, href);
      event.preventDefault();
      router.fetch(href.split("/").pop());
      router.route(href);
    }
    

    【讨论】:

    • 非常感谢。 readyLink 函数中的硬编码怎么办?
    • 同样的事情。找到关闭父级并获取其href。或者您可以从事件处理程序中传递它(就像我在答案的第二部分中所做的那样)。
    猜你喜欢
    • 2012-02-26
    • 2012-04-22
    • 2011-08-23
    • 2014-01-20
    • 1970-01-01
    • 2015-08-12
    • 2022-01-16
    • 1970-01-01
    相关资源
    最近更新 更多