首页 > 解决方案 > 将 jQuery.on(event,selector,func) 转换为 .addEventListener(event, func) 尤其是 DOM 树之后的选择器部分和事件气泡

问题描述

我想将我项目中的代码从jQuery.onto转换为.addEventListener并删除对 j​​Query 的依赖。

主要问题:有没有办法用相对较少的代码来做到这一点,或者我是否需要像 jQuery 那样编写自定义事件处理?

------------------------------------------
|                   a                    |
|                                        |
|     -----------------------------      |
|     |             b             |      |
|     |                           |      |
|     |      ---------------      |      |
|     |      |      c      |      |      |
|     |      |             |      |      |
|     |      |             |      |      |
|     |      |             |      |      |
|     |      |             |      |      |
|     |      |             |      |      |
|     |      ---------------      |      |
|     |                           |      |
|     |                           |      |
|     -----------------------------      |
|                                        |
|                                        |
------------------------------------------

点击c应该返回 c 的处理程序然后是b然后是 a

我已经阅读了多个 stackoverflow 问题*和答案,它们让我对两者之间的差异有一个基本的了解(或者主要是 jQuery 添加的附加逻辑)。在这个小提琴中,您可以看到一个基本示例,我想以 jQuery 的工作方式工作。

我很div.c清楚div.a,当事件从目标就像 jQuery 一样。div.a

b是粗体,因为它不会被触发,尽管这应该是期望的行为。它没有被触发的原因是 e.target( div.c) 与 querySelector 不匹配.b

对我来说,当从.addEventHandler. 即使监听器是从一个共同的父级触发的,它似乎也考虑了 DOM 嵌套。考虑到 DOM 嵌套的最后一部分是我希望使用 'vanilla' js 的内容。

使用$._data(element, 'events')我们可以访问 jQuery 内部存储的事件以查看它们。这里的guid属性似乎表明了事件被触发的顺序。订单是如何确定/触发的,但是我找不到。

非常感谢任何帮助!

*相关问题

jQuery .on(); 与 JavaScript .addEventListener();

jQuery 事件处理程序总是按照它们被绑定的顺序执行——有什么办法吗?

jQuery on() stopPropagation 不起作用?

标签: javascriptjqueryaddeventlistenerdom-eventsjquery-events

解决方案


您可以遍历 target 的所有祖先并检查它们是否匹配特定的选择器并为每个匹配的实例调用回调

它们触发的顺序仍然是它们添加到根元素的顺序

HTMLElement.prototype.delegate = function(evtName, selector, callback) {
  var rootElement = this;


  this.addEventListener(evtName, function(e) {
    var matchingAncestors = [], parent = e.target.parentElement;
    // TODO: Abort if target is root element

    if (e.target.matches(selector)) {
       console.log('selector', selector,'matches target')
      callback(e)
    } else {
      // we know it's not the target so work way up ancestors finding selector matches     
      while (parent && parent !== rootElement) {
        if (parent.matches(selector)) {
          matchingAncestors.push(parent)
        }
        parent = parent.parentElement
      }

      // call the callback for each instance
      matchingAncestors.forEach(function(el) {
        console.log(el, ' is matching ancestor of target')
        callback(e)
      });
    }
  })

}
   
var a = document.querySelector(".a");

a.addEventListener("click", function(e) {
  console.log('*********************************')
  console.log("Vanilla a\n");
});
a.delegate('click', '.b', function(e) {
  console.log("Vanilla-delegate b\n");
})

a.delegate('click', '.c', function(e) {
  console.log("Vanilla-delegate c\n");
})
div {padding-left:30px; border:1px solid #ccc}
<div class="a">
  a
  <div class="b">
    b
    <div class="c">
      c<br>(click me)
    </div>
  </div>
</div>

请注意,这在生产中未经测试


推荐阅读