skip to Main Content

I’ve been using this code below to remove attributes added by a 3rd party library from my webapp on runtime.

document.addEventListener('DOMNodeInserted', () => {
  const elements = document.querySelectorAll('[aria-owns]');
  elements.forEach(element => {
    element.removeAttribute('aria-owns')
  })
})

However recently, we’ve been getting this error in the console:

[Deprecation] Listener added for a ‘DOMNodeInserted’ mutation event.
Support for this event type has been removed, and this event will no
longer be fired. See https://chromestatus.com/feature/5083947249172480
for more information.

The summary is that DOMNodeInserted shouldn’t be used anymore for performance reasons, and more importantly bc it will stop working in the near future. It links to an article that mentions that using MutationObserver is the viable option.

However, MutationObserver doesn’t expose Elements, just nodes.

This is what I tried:

const observer= new MutationObserver(mutationList =>{
  mutationList.forEach(mutationRecord => {
    const elements= document.querySelectorAll('[aria-owns]');
    elements.forEach(element => 
      element.removeAttribute('aria-owns')
    )
  })
});
observer.observe(document, {
  childList: true, 
  subtree: true, 
  attributeFilter: ['aria-owns']
};

but if I understand correctly how MutationObserver works, then it feels like an overkill to get all the elements in the document with document.querySelectorAll('[aria-owns]') then iterate over them one by one to remove the attribute, if mutationRecod already yields a collection of the nodes that have just mutated and contain the attribute I am looking for.

is there a way to access the element from the nodes yielded by the MutationObserver?
or what is the correct way to edit the attributes of the nodes with a MutationObserver?

2

Answers


  1. Chosen as BEST ANSWER

    Posting my own answer as I've found this works, however I am not sure this is the right way to do this, but it somehow looks better than what I was trying before (I am working on typescript):

    const observer= new MutationObserver(mutationList =>
      mutationList.forEach(mutationRecord=> 
        (mutationRecord.target as Element).removeAttribute('aria-owns')
      )
    );
    observer.observe(document, {
      childList: true, 
      subtree: true, 
      attributeFilter: ['aria-owns']
    };
    

  2. TypeScript offers a feature called a type guard function that uses a runtime check to narrow an existing type to a subtype. You can use this feature to discriminate whether or not a Node is an Element:

    Ref: Node: nodeType property

    TS Playground

    function isElement(node: Node): node is Element {
      return node.nodeType === Node.ELEMENT_NODE;
    }
    
    declare const node: Node;
    
    if (isElement(node)) {
      node
      //^? const node: Element
    } else {
      node
      //^? const node: Node
    }
    

    You can use that type guard to inform the compiler while writing the code that runs in the MutationObserver callback. Here’s a functional example:

    TS Playground

    function observeAndRemove(): MutationObserver {
      const attributeName = "aria-owns";
    
      function isElement(node: Node): node is Element {
        return node.nodeType === Node.ELEMENT_NODE;
      }
    
      function removeAttr(node: Node): void {
        if (isElement(node) && node.hasAttribute(attributeName)) {
          node.removeAttribute(attributeName);
        }
      }
    
      function handleMutationRecord(mutationRecord: MutationRecord): void {
        switch (mutationRecord.type) {
          case "attributes": {
            if (mutationRecord.attributeName === attributeName) {
              removeAttr(mutationRecord.target);
            }
            break;
          }
          case "childList": {
            for (const node of mutationRecord.addedNodes) {
              removeAttr(node);
            }
            break;
          }
        }
      }
    
      const observer = new MutationObserver((mutationRecords) => {
        for (const mutationRecord of mutationRecords) {
          handleMutationRecord(mutationRecord);
        }
      });
    
      observer.observe(document, {
        childList: true,
        subtree: true,
        attributeFilter: [attributeName],
      });
    
      return observer;
    }
    
    const observer = observeAndRemove();
    // Maybe later…
    // if (condition) observer.disconnect();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search