javascripthtmlmutation-observers

Script Tag Interrupts Mutation Observer for Template Tags


I'm building a library, that interacts with template tags, and watches for new ones that might be added to the page using a MutationObserver.

<script>
  const upgradeNewNodes = (mutationRecords) => {
    mutationRecords.forEach((mutationRecord) => {
      mutationRecord.addedNodes.forEach((newNode) => {
        if (newNode.matches?.('template')) {
          console.log([...newNode.content.childNodes].map(child => child.nodeName))
        }
      });
    });
  };

  const observer = new MutationObserver(upgradeNewNodes);
  observer.observe(document, { subtree: true, childList: true });
</script>

<template>
  <span>Beginning</span>
  <script>
    console.log('foo')
  </script>
  <span>Ending</span>
</template>

However, when my mutation observer picks up this template tag, it only sees up to the script tag.

// [object NodeList] (4)
[#text,<span/>,#text,<script/>]

Codepen Link: https://codepen.io/JRJurman/pen/WNLEaqp?editors=1001

My current hypothesis is that the HTML parser is stopped when it hits the script tag.

In my actual library, I can only process the contents once, so adding another MutationObserver for the content in the template isn't really an option.

Is there a way to know when the template has completely loaded? Are there other ways that I can progressively catch template tags being added to the page (other than MutationObserver)?


Solution

  • One potential option here appears to be watching for nodes right after a template tag - these are almost always guaranteed to exist, because there are always TEXT nodes before, between, and after elements.

    <script>
      const upgradeNewNodes = (mutationRecords) => {
        mutationRecords.forEach((mutationRecord) => {
          mutationRecord.addedNodes.forEach((newNode) => {
            // check if this node is after a template (if it is, process that template)
            // this is almost always guaranteed to exist, and be a TEXT node
            if (newNode.previousSibling?.matches?.('template')) {
              console.log([...newNode.previousSibling.content.childNodes].map(child => child.nodeName))
            }
          });
        });
      };
    
      const observer = new MutationObserver(upgradeNewNodes);
      observer.observe(document, { subtree: true, childList: true });
    </script>
    
    <template>
      <span>Beginning</span>
      <script>
        console.log('foo')
      </script>
      <span>Ending</span>
    </template>

    Results in:

    // [object NodeList] (7)
    [#text,<span/>,#text,<script/>,#text,<span/>,#text]
    

    Codepen Link: https://codepen.io/JRJurman/pen/jOXLQmY?editors=1001