javascript - hur man tar bort ett attribut från ett element med hjälp av mutationObserver och mutationrecord?


Jag har använt koden nedan för att ta bort attribut som lagts till av ett tredjepartsbibliotek från min webbapp under körning.

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

Men på senare tid har vi fått detta fel i konsolen:

[Avskrivning] Lyssnare tillagd för en 'DOMNodeInserted'-mutationshändelse. Stöd för denna händelsetyp har tagits bort, och denna händelse kommer inte längre att utlösas. Se https://chromestatus.com/feature/5083947249172480 för mer information.

Sammanfattningen är att DOMNodeInserted inte bör användas längre av prestandaskäl, och viktigare eftersom det kommer sluta fungera inom en snar framtid. Den länkar till en artikel som nämner att användning av MutationObserver är det gångbara alternativet.

Dock exponerar MutationObserver inte Elements, bara noder.

Det här är vad jag försökte:

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']
};

men om jag förstår korrekt hur MutationObserver fungerar, så känns det som överdrivet att hämta alla element i dokumentet med document.querySelectorAll('[aria-owns]') och sedan iterera över dem ett och ett för att ta bort attributet, om mutationRecod redan ger en samling av de noder som precis har ändrats och innehåller det attribut jag letar efter.

finns det ett sätt att komma åt element från de noder som returneras av MutationObserver? eller vad är det korrekta sättet att redigera attributen hos noderna med en MutationObserver?

Svar #1

Lägger upp mitt eget svar eftersom jag har upptäckt att detta fungerar, men jag är inte säker på om detta är rätt sätt att göra det på, men det ser på något sätt bättre ut än det jag försökte tidigare (jag arbetar med 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']
};

Svar #2

TypeScript erbjuder en funktion som kallas en typvaktfunktion som använder en körningstidkontroll för att begränsa en befintlig typ till en subtyp. Du kan använda denna funktion för att avgöra om en Node är ett Element:

Ref: Nod: nodeType-egenskap

TS Lekplats

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
}

Du kan använda den typen av skydd för att informera kompilatorn medan du skriver koden som körs i MutationObserver-callbacken. Här är ett funktionellt exempel:

TS Lekplats

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();