如何使用事件侦听器复制 DOM 节点?

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

我试过了

node.cloneNode(true); // deep copy

它似乎没有复制我使用

node.addEventListener("click", someFunc);
添加的事件监听器。

我们使用 Dojo 库。

javascript dom dojo
7个回答
98
投票

cloneNode()
不复制事件侦听器。事实上,一旦附加了事件监听器,就无法通过 DOM 获取事件监听器,因此您的选择是:

  • 手动将所有事件监听器添加到克隆节点
  • 重构您的代码以使用事件委托,以便所有事件处理程序都附加到包含原始和克隆的节点
  • 使用
    Node.addEventListener()
    的包装函数来跟踪添加到每个节点的侦听器。例如,这就是 jQuery 的
    clone()
    方法能够复制带有事件侦听器的节点的方式。

15
投票

这并不能准确回答问题,但是如果用例允许移动元素而不是复制它,您可以将removeChildappendChild一起使用,这将保留事件监听器。例如:

function relocateElementBySelector(elementSelector, destSelector) {
  let element = document.querySelector(elementSelector);
  let elementParent = element.parentElement;
  let destElement = document.querySelector(destSelector);
  elementParent.removeChild(element);
  destElement.appendChild(element);
}

2
投票

事件委托示例。

看了Tim Down的回答,发现委托事件很容易实现,解决了我遇到的类似问题。我想我会添加一个具体的例子,虽然它在 JQuery 而不是 Dojo 中。

我正在重新设计语义 UI 中的应用程序,这需要一小段 JS 才能使消息关闭按钮起作用。但是,这些消息是从库中使用

document.importNode
的 HTML 模板标签克隆的。这意味着即使我确实将事件处理程序附加到新 HTML 中的模板,它们也会在克隆过程中丢失。

我不能做 Tim 的选项 1,因为消息传递库与前端框架无关,所以在克隆期间简单地重新附加它们。 (有趣的是,我以前的前端是在 Zurb Foundation 中,它使用“数据可关闭”属性,其功能在克隆过程中仍然存在)。

建议的正常事件处理是这样的

$('.message .close').on('click', function() {
    $(this)
    .closest('.message')
    .transition('fade');
});

应用程序加载时的“.message”问题只匹配单个模板,而不匹配稍后通过网络套接字到达的实际消息。

进行委托,意味着将事件附加到消息被克隆到的容器

<div id="user-messages">

于是变成:

$('#user-messages').on('click', '.message .close', function() {
    $(this)
    .closest('.message')
    .transition('fade');
});

这立即起作用,节省了任何复杂的工作,例如包装事件子的第三个选项。

Dojo 等效 在概念上看起来非常相似。


1
投票

这就是 @JeromeJ 在评论中描述的内容。使用此 HTML 代码创建初始元素。

<DIV ONCLICK="doSomething(this)">touch me</DIV>

当您克隆此元素时,结果将具有相同的处理程序,并且“this”将指向克隆的元素。

如果可以在 JavaScript 中轻松添加 ONCLICK 处理程序,那就太好了。这种方法意味着您必须用 HTML 编写一些代码。


1
投票

克隆节点会复制其所有属性及其值,包括内在(内联)侦听器。它不会复制使用 addEventListener() 添加的事件侦听器或分配给元素属性的事件侦听器(例如,node.onclick = someFunction)。此外,对于一个元素,绘制的图像不会被复制。

来源:MDN (https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode).


1
投票

只有内联属性才能在这里工作,因为它们被滥用的程度而被严重禁止。也就是说,您可以让元素绑定到同一个事件侦听器。

使用 Web Components(和影子根)的正确方法看起来像我们想要复制的东西:

onButtonClick(event) {
  console.log('onButtonClick', { event, this: this });
}

/* Or constructor */
connectedCallback() {
  this.shadowRoot.getElementById('button')
    .addEventListener(this.onButtonClick);
}

它是高效的,因为您不会像使用

.addEventListener(() => this.onButtonClick)
那样为每个元素创建函数。 1000 个按钮将附加到相同的功能,而不是为每个按钮创建一个新功能。

将其转换为内联看起来像这样:

<button onclick="this.getRootNode().host.onButtonClick.call(this, event)">

丑吗?是的。但它有效吗?还有,是的。在这种情况下,JS 不需要找到元素并指示浏览器创建事件处理程序。内联

onclick
会为你做到这一点。我会注意到正在为每个元素创建一个新函数,而不是它们都共享一个。


-1
投票

我知道我迟到了,但这是一个对我有用的解决方案:

const originalButtons = original.querySelectorAll<HTMLElement>('button');
const cloneButtons = clone.querySelectorAll<HTMLElement>('button');
originalButtons.forEach((originalButton: HTMLElement, index: number) => {
  cloneButtons[index].after(originalButton);
  cloneButtons[index].remove();
});
© www.soinside.com 2019 - 2024. All rights reserved.