如何为未来的DOM元素添加事件监听器?

16

文件 partial.html 的内容如下:<button id="test">Hi I am from a partial!</button>

partial.html 是通过 XMLHttpRequest 在页面上动态包含的:

var oReq = new XMLHttpRequest();
oReq.open('get', 'partial.html', true);
oReq.send();
oReq.onload = function() {
  document.querySelector('#pageArea').innerHTML = this.response;
};

我如何添加一个事件监听器,使其适用于将来存在的#test而无需在其内容被加载并插入#pageArea后再执行此操作?

(请勿提供jQuery解决方案!)


1
在浏览器加载并插入DOM结构之前,您可能需要解析HTML。为什么需要在它被加载和插入DOM结构之前这样做? - Adriano Repetti
2个回答

21

点击 这样的事件 bubble,因此您需要将事件处理程序附加到最近的非动态父元素,并在事件处理程序内部检查是否是按钮被点击,方法是查看它是否为事件的目标:

var parent = document.getElementById('pageArea');

if (parent.addEventListener) {
    parent.addEventListener('click', handler, false);
}else if (parent.attachEvent) {
    parent.attachEvent('onclick', handler);
}

function handler(e) {
    if (e.target.id == 'test') {
         // the button was clicked
    }
}

小提琴


2
这种方法会对性能产生影响吗?直接将事件侦听器附加到元素不是更好吗? - subZero
4
@subZero - 你无法给不存在的东西附加事件处理程序,这就是为什么你必须进行委派的原因。 - adeneo
一直想知道如何用纯JS来处理这个问题(需要时使用jQuery .on)。感谢您! - Sundance.101

2
不要仅仅使用 Event.target! 正如我在网上找到的许多答案所错误建议的那样,Event.target可能是一个不需要的子元素! 例如考虑以下情况:<button class="dynamic" type="button">CLICK <i>ME!</i></button> 如果你点击按钮中的 "ME!" 部分- 你的逻辑会失败得很惨。

始终与 .closest() 结合使用 Event.target

document.querySelector("#staticParent").addEventListener("click", evt => {
  if (event.target.closest(".dynamic")) {
    // The desired button (or its child) was clicked!
  }
});

创建自己的on()帮助函数:

对于动态创建的元素,在它们被插入到DOM之前想要分配一个点击事件 - 将事件分配给父级 代理器,然后查询事件 Event.target.closest(selector) 是否匹配所需的动态子级选择器:

// On handler:

const on = (evtName, delegator, ...args) => {
  if (typeof delegator === "string") {
    delegator = document.querySelector(delegator);
  }
  if (typeof args[0] === "string") {
    delegator.addEventListener(evtName, (evt) => {
      if (evt.target.closest(args[0])) {
        args[1](evt);
      }
    });
  } else {
    delegator.addEventListener(evtName, ...args);
  }
};

// Use like:

// Dynamic child elements:

on("click", document.querySelector("#staticParent"), ".dynamicChild", (evt) => {
  console.log(evt.target);        // The dynamic child
  console.log(evt.currentTarget); // The static parent delegator
});

// Static elements:

on("click", document.querySelector("#staticParent"), (evt) => {
  console.log(evt.currentTarget); // The static parent
});

这里有一个更加简洁的版本,包括一些其他有用的工具函数和更多的巧妙示例:
// DOM utility functions:
const el = (sel, parent) => (parent || document).querySelector(sel);
const els = (sel, parent) => (parent || document).querySelectorAll(sel);

// Utility functions:
const isStr = (str) => typeof str === 'string' || str instanceof String;

// On handler:
const on = (evtName, delegator, ...a) => 
  (isStr(delegator) ? el(delegator) : delegator)
    .addEventListener(evtName, ...isStr(a[0]) ? [e => e.target.closest(a[0]) && a[1](e), a[2]] : a);

上述片段可以像这样使用,例如:
// Add event to dynamic or existing child elements of a single static parent

on("click", "#delegatorParent", ".dynamicTarget", (evt) => { 
  console.log(evt.target);        // The dynamic child
  console.log(evt.currentTarget); // The static parent (delegator)
});

// Add event to dynamic or existing child elements of multiple static parents

els(".delegatorParent").forEach(elParent => {
  on("click", elParent, ".dynamicTarget", (evt) => { 
    console.log(evt.target);        // The dynamic child
    console.log(evt.currentTarget); // The static parent (delegator)
  });
});

它也可以用于静态(现有)元素

on("click", "#target", (evt) => {
   console.log(evt.currentTarget);  // The static element
});

on("click", "#target", (evt) => {
   console.log(evt.currentTarget);  // The static element
}, { once: true });

// Add event to multiple Elements:

els(".items").forEach(elItem => {
  on("click", elItem, (evt) => {
    console.log(evt.currentTarget); // One of the static elements
  });
});

这是与上面相同的on()函数,以更易读的形式书写,没有任何依赖性:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接