How to copy a DOM node with event listeners?

JavascriptDomDojo

Javascript Problem Overview


I tried

node.cloneNode(true); // deep copy

It doesn't seem to copy the event listeners that I added using node.addEventListener("click", someFunc);.

We use the Dojo library.

Javascript Solutions


Solution 1 - Javascript

cloneNode() does not copy event listeners. In fact, there's no way of getting hold of event listeners via the DOM once they've been attached, so your options are:

  • Add all the event listeners manually to your cloned node

  • Refactor your code to use event delegation so that all event handlers are attached to a node that contains both the original and the clone

  • Use a wrapper function around Node.addEventListener() to keep track of listeners added to each node. This is how jQuery's clone() method is able to copy a node with its event listeners, for example.

Solution 2 - Javascript

This does not answer the question exactly, but if the use case allows for moving the element rather than copying it, you can use removeChild together with appendChild which will preserve the event listeners. For example:

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

Solution 3 - Javascript

Event Delegation example.

After reading Tim Down's answer, I found delegated events are very easy to implement, solving a similar problem I had. I thought I would add a concrete example, although it's in JQuery not Dojo.

I am re-skining an application in Semantic UI, which requires a small piece of JS to make the message close buttons work. However the messages are cloned from an HTML template tag using document.importNode in a library. This meant even if I did attach the event handlers to the template in the new HTML, they are lost during the cloning.

I cannot do Tim's option 1, to simply re-attach them during cloning as the messaging library is front-end framework agnostic. (Interestingly my previous front-end was in Zurb Foundation which uses a "data-closable" attribute, the functionality of which does survive the cloning process).

The normal event handling suggested was like this:

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

The problem being ".message" at app-load only matches the single template, not the actual messages which arrive later over web-sockets.

Making this delegated, meant attaching the event to the container into which the messages get cloned <div id="user-messages">

So it becomes:

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

This worked immediately, saving any complex work like the third option of wrapping the event subs.

The Dojo equivalent looks pretty similar in concept.

Solution 4 - Javascript

This is what @JeromeJ was describing in a comment. Create the initial element using this HTML code.

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

When you clone this element the result will have the same handler, and "this" will point to the cloned element.

It would be great if the ONCLICK handler could easily be added in JavaScript. This approach means that you have to write some of your code in HTML.

Solution 5 - Javascript

I know I'm late to the party but this a solution that worked for me:

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();
});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChakradar RajuView Question on Stackoverflow
Solution 1 - JavascriptTim DownView Answer on Stackoverflow
Solution 2 - JavascriptRichard GreenwoodView Answer on Stackoverflow
Solution 3 - JavascriptscipilotView Answer on Stackoverflow
Solution 4 - JavascriptTrade-Ideas PhilipView Answer on Stackoverflow
Solution 5 - JavascriptObtuseView Answer on Stackoverflow