Mateus Pereira
Mateus Pereira

Reputation: 33

JavaScript: Listen for DOM changes on specific tag without polling

I want to create a listener that detects changes on a specified tag, when DOM updates have been applied to a new tag. In this case, I have a chat app and I need to get all the new <audio> tags, and apply a class on those new tags only.

How I create a specified listener just for one tag?

For performance reasons, I don't want to create a setInterval. Is there another way to achieve this and how?

Upvotes: 2

Views: 1791

Answers (1)

Dacre Denny
Dacre Denny

Reputation: 30360

If I understand correctly, you'd like to detect when an <audio/> element is added to your document and, on detecting this event, assign a CSS class to that newly added audio element.

One possibility that avoids polling, would be to use a MutationObserver - a simple solution would be to observe the body element for any changes in it's childList or subtree and, for any changes that are detected that are caused by a newly added audio node, add the required class to that audio node:

/* Mutation handler will assign "addedAudio" class to
any newly added audio elements */
function onMutation(mutations) {

  for (const mutation of mutations) {
    if (mutation.type === 'childList') {

      /* This mutation type is a change to tree nodes in dom */
      for (const addedNode of mutation.addedNodes) {
        if (addedNode.nodeName === 'AUDIO') {

          /* The node triggering this mutation is an audio node */
          addedNode.classList.add("addedAudio");
        }
      }
    }
  }
}

/* Create dom mutation observer with callback, and
bind observer to body (eg "root") of your web app
and watch all children and subtrees for any changes */
(new MutationObserver(onMutation))
.observe(document.querySelector("body"), {
  childList: true,
  subtree: true
})

/* For demo purpose only to simulate adding new audio element to document */
document.querySelector('button').addEventListener('click', () => {

  const audio = document.createElement("audio");
  audio.setAttribute('controls', true);
  audio.setAttribute('src', 'https://www.w3schools.com/html/horse.ogg');

  document.body.prepend(audio);
});
.addedAudio {
  border: 1px solid red;
}
<button>Add audio</button>

Note that a more efficient approach would be to observe a DOM node that has fewer children, shallow sub-tree, etc. For instance, if all audio elements are added to a common DOM element, consider observing that element instead, rather than the body.

Hope that's of some user!

Upvotes: 3

Related Questions