使用MutationObserver时获取当前MutationRecord的新属性值?

tru*_*ktr 7 html javascript dom mutation-observers

我有一些如下代码,

const observer = new MutationObserver(records => {
    for (const record of records) {
        if (record.type !== 'attributes') continue

        handleAttributeChange(
            record.attributeName!,
            record.oldValue,
            record.target.attributes.getNamedItem(record.attributeName).value,
        )
    }
})
Run Code Online (Sandbox Code Playgroud)

其中handleAttributeChange参数是属性名称、旧值和新值。

但是,正如您所知,第三个参数传递给handleAttributeChange

const observer = new MutationObserver(records => {
    for (const record of records) {
        if (record.type !== 'attributes') continue

        handleAttributeChange(
            record.attributeName!,
            record.oldValue,
            record.target.attributes.getNamedItem(record.attributeName).value,
        )
    }
})
Run Code Online (Sandbox Code Playgroud)

始终是最新的属性值,而不是我们正在迭代的特定突变记录的“新值”(即不是突变发生时观察到的“新值”)。

我们如何获得每个突变发生时观察到的“新值”?

tru*_*ktr 7

我们可以这样模拟:

const observer = new MutationObserver(records => {
    let lastAttributeValues = {}
    let name = ''

    // This loop goes through all the records, and for each mutation,
    // it calls handleAttributeChange with old and new attribute values
    // as would have been observed had we been able to react to each 
    // mutation synchronously.
    for (const record of records) {
        if (record.type !== 'attributes') continue

        name = record.attributeName

        if (lastAttributeValues[name] === undefined) {
            lastAttributeValues[name] = record.oldValue
            continue
        }

        handleAttributeChange(name, lastAttributeValues[name], record.oldValue)

        lastAttributeValues[name] = record.oldValue
    }

    let attr

    // This loop calls handleAttributeChange for each attribute that changed
    // with the last oldValue and the current value that's actually in
    // the DOM (the previous loop handled only differences between each
    // mutation of each mutated attribute, but not the difference between
    // the last mutation of a each attribute and the attributes' current
    // values in the DOM).
    for (const name in lastAttributeValues) {
        attr = el.attributes.getNamedItem(name)
        handleAttributeChange(name, lastAttributeValues[name], attr === null ? null : attr.value)
    }
})
Run Code Online (Sandbox Code Playgroud)

它的作用是,因为我们知道我们按顺序获得突变,并且在迭代时我们拥有给定属性的所有属性值,所以它只是跟踪先前和当前的属性值(每个属性名称)并触发每个属性handleAttributeChange值先前值和当前值。


Cer*_*nce 3

我认为这是不可能的MutationObserverMutationRecord中的任何内容都不会公开新值,并且由于 MuationObserver 在微任务中运行,并且在更改后不完全同步,因此可以从观察者回调内的元素检索的值可能触发观察者的更改后已更改,正如你所看到的。

const observer = new MutationObserver(records => {
  for (const record of records) {
    if (record.type !== 'attributes') continue
    console.log(div.dataset.foo);
    console.log(record);
  }
});
observer.observe(div, { attributes: true });

div.setAttribute('data-foo', 'foo');
div.setAttribute('data-foo', 'bar');
Run Code Online (Sandbox Code Playgroud)
<div id="div"></div>
Run Code Online (Sandbox Code Playgroud)

虽然可以使用同步观察器(侦听DOMAttrModified元素上的事件),但它们已被弃用并且速度很慢。

如果您知道该属性将如何更改,您也许可以修补该方法,以便它首先执行您自己的逻辑。例如,使用setAttribute

div.setAttribute = (...args) => {
  console.log(`Setting ${args[0]} from ${div.getAttribute(args[0])} to ${args[1]}`);
  return HTMLElement.prototype.setAttribute.apply(div, args);
};

div.setAttribute('data-foo', 'foo');
div.setAttribute('data-foo', 'bar');
Run Code Online (Sandbox Code Playgroud)
<div id="div"></div>
Run Code Online (Sandbox Code Playgroud)