MutationObserver的DOM上下文丢失,因为它触发太晚了

Dwa*_*yne 14 javascript dom mutation-observers

我的代码的简化版本:

<div id="d">text<br><hr>text</div>

<script>
    // Called when DOM changes.
    function mutationCallback(mutations) {
        // assert(mutations.length === 3);
        var insertImg = mutations[0];
        console.log(insertImg.previousSibling.parentNode);  // Null!
        console.log(insertImg.nextSibling.parentNode); // Null!
        // Can't determine where img was inserted!
    }
  
    // Setup
    var div = document.getElementById('d');
    var br = div.childNodes[1];
    var hr = div.childNodes[2];
    var observer = new MutationObserver(mutationCallback);
    observer.observe(div, {childList: true, subtree: true});

    // Trigger DOM Changes.
    var img = document.createElement('img');
    div.insertBefore(img, hr);
    div.removeChild(hr);
    div.removeChild(br); // mutationCallback() is first called after this line.
</script>
Run Code Online (Sandbox Code Playgroud)

我正在使用Mutation Observers捕获DOM更改,以便在另一个文档实例发生更改时更新.因为在<img>的前一个和下一个兄弟被删除之后才调用变异观察器函数,所以mutationCallback函数无法分辨它的插入位置.在Chrome,FF和IE11中重现.

另一种方法是遍历整个文档以查找更改,但这会否定使用Mutation Observers的性能优势.

log*_*yth 5

mutations数组是特定目标发生的突变的完整列表。这意味着,对于任意元素,了解突变时父元素是什么的唯一方法是,您必须查看后来的突变以了解父元素何时发生突变,例如

var target = mutations[0].target
var parentRemoveMutation = mutations
 .slice(1)
 .find(mutation => mutation.removedNodes.indexOf(target) !== -1);
var parentNode = parentRemoveMutation 
  ? parentRemoveMutation.target // If the node was removed, that target is the parent
  : target.parentNode; // Otherwise the existing parent is still accurate.
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这是针对第一个突变进行硬编码的,您可能必须一次对列表中的每个项目执行它。这不会很好地扩展,因为它必须进行线性搜索。您还可以运行完整的突变列表来首先构建该元数据。

综上所述,问题的核心似乎是在理想世界中你真的不应该关心父母。例如,如果您要同步两个文档,则可以考虑使用 来WeakMap跟踪元素,因此对于每个可能的元素,都有一个从正在突变的文档到同步文档中每个元素的映射。然后,当发生突变时,您可以使用 Map 在原始文档中查找相应的元素,并在原始文档上重现更改,而根本不需要查看父文档。