0

For example, I have social tab in my header which I want to also add to footer. Said tab consist of multiple buttons with SVG images on top, eventlisteners connected to buttons id's and CSS formatting.

Obviously I can rewrite links from js script into html file and just copypaste elements. Will work for links, will be problems with something more complex.

I can also add new IDs for second pair of elements and copypaste the script changing IDs to new ones. Or just use classes instead and collect elements in array with querySelector and adding script with forEach loop.

But I want to ask is there some better solution instead? To just repeat the element at certain place retaining everything from original? Having the same IDs and eventlisteners.

Maks111
  • 3
  • 2

1 Answers1

1

For cloning a node, use the Node.cloneNode() method. This allows you to clone a node and optionally all its child nodes as well.

This is effectively as if you copied its representing HTML, so only inline listeners, attributes etc. are copied.

Since the listeners are not copied, you would have to explicitely attach them to the clones 'again'.

Note: Make sure your DOM tree won't have duplicate IDs after cloning. Assign different IDs to the clones, or consider using a class instead.


By using event delegation, you could clone and add the elements without having to attach the listeners directly; they use the listener from their ancestor.

Here is an example of how this could look like:

const commonAncestor = document.getElementById("common-ancestor");
const topTablist = document.getElementById("top-tablist");
const bottomTablist = document.getElementById("bottom-tablist");

// Clone from top to bottom tablist; will use same listener from common ancestor
const clonedTablist = topTablist.cloneNode(true);
bottomTablist.replaceChildren(...clonedTablist.children);

// Event delegation: One listener on a common ancestor for multiple targets.
commonAncestor.addEventListener("click", evt => {
  // Make sure a relevant element (here: `[role=tab]`) is the target.
  const tab = evt.target.closest("[role=tab]");
  if (tab === null) {
    return;
  }
  
  selectPanel(tab.getAttribute("aria-controls"));
});

function selectPanel(panelId) {
  const tabs = document.querySelectorAll("[role=tab]");
  tabs.forEach(tab => {
    const shouldSelect = tab.getAttribute("aria-controls") === panelId;
    tab.ariaSelected = String(shouldSelect);
  });
  
  const panels = document.querySelectorAll("[role=tabpanel]");
  panels.forEach(panel => {
    const shouldHide = panel.id !== panelId;
    panel.toggleAttribute("hidden", shouldHide);
  });
}
#tabpanels {
  border: 1px solid black;
  width: 240px;
  height: 160px;
  display: grid;
}
<section id="common-ancestor">
  <div id="top-tablist" role="tablist">
    <button role="tab" aria-selected="true" aria-controls="panel-1">Tab 1</button>
    <button role="tab" aria-selected="false" aria-controls="panel-2">Tab 2</button>
  </div>
  <div id="tabpanels">
    <div id="panel-1" role="tabpanel">Panel 1</div>
    <div id="panel-2" role="tabpanel" hidden>Panel 2</div>
  </div>
  <div id="bottom-tablist" role="tablist">
    <!--Empty, will be filled via JS-->
  </div>
</section>
Oskar Grosser
  • 2,804
  • 1
  • 7
  • 18
  • Thank you. Didn't know about node cloning. Rewritten my script with classes so everything works now. Is it better practice to use event delegation than adding events with forEach function for every class element? p.s. so by definition if i clone some element i should avoid it having any Id's? – Maks111 Apr 24 '23 at 04:13
  • @Maks111 Here I'd recommend event delegation since you want added elements to be automatically 'enhanced' with similar functionality. But, separate listeners are better to handle a narrow scope (e.g. single targets), when using closures for listeners makes more sense, to allow elements to move with their _attached_ listeners, or when the listening ancestor receives too many events (due to its high DOM position). "Best practices" only apply to certain situations; learn about them, but decide for yourself what works best in every given situation. – Oskar Grosser Apr 24 '23 at 23:44
  • @Maks111 You may clone elements with IDs, but **IDs should be unique** per tree. If a cloned element has an ID, consider changing the clone's ID (e.g. by appending and incremented index like `id`, `id-0`, `id-1`). If you use the same (or a similar) ID on multiple elements, then replacing it with a class usually makes more sense. – Oskar Grosser Apr 24 '23 at 23:48