Skip to content
Astera Technology

Clone markup without assuming its listeners are cloned

Clone a button’s markup and identify the listener that remains with its original node.

Download complete case notes and answer keys ↓

Inspect the fixed example

Register one listener, deep-clone the button and give the copy a unique ID. The script clicks original and copy once each.

Complete body and fixed script
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<button id="original" type="button"><span>Original</span></button><output id="result"></output>
<script>let count=0;const original=document.querySelector('#original');original.addEventListener('click',()=>count++);const copy=original.cloneNode(true);copy.id='copy';copy.querySelector('span').textContent='Copy';original.after(copy);original.click();copy.click();
const result=count;
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
Exact console output
1
Inputs and native results
InspectionSupplied input or actionResult
Original click#originalcount: 1
Copy click#copycount: 1
IDsoriginal / copyDistinct IDs

Follow the native mechanism

cloneNode(true) copies the node’s attributes and descendants. Listeners registered with addEventListener are not copied. Unique IDs require an explicit change; deep cloning does not provide it. This fixture has no inline event-handler attributes, whose copying would be a different boundary.

Compare the material alternative

Leaving the copied ID unchanged creates two matching IDs while the copy still has no registered handler. Appearance and deep=true do not establish behavior.

Fresh counterexample source
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<button id="original" type="button"><span>Original</span></button><output id="result"></output>
<script>let count=0;const original=document.querySelector('#original');original.addEventListener('click',()=>count++);const copy=original.cloneNode(true);original.after(copy);copy.click();
const result={duplicateIds:document.querySelectorAll('#original').length,count};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
Counterexample output
{"duplicateIds":2,"count":0}

Work the boundary

What must be done to give the new copy an independent click handler?

Show the worked answer

Assign its unique ID and explicitly register its handler. The repaired fixed run increments each independent counter once.

Fresh boundary source
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<button id="original" type="button"><span>Original</span></button><output id="result"></output>
<script>let originalCount=0,copyCount=0;const original=document.querySelector('#original');original.addEventListener('click',()=>originalCount++);const copy=original.cloneNode(true);copy.id='copy';copy.addEventListener('click',()=>copyCount++);original.after(copy);original.click();copy.click();
const result={originalCount,copyCount,spanCopied:copy.querySelectorAll('span').length,uniqueIds:new Set([original.id,copy.id]).size};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
Boundary output
{"originalCount":1,"copyCount":1,"spanCopied":1,"uniqueIds":2}

Original example files