通过parentNode.removeChild删除元素会引发DOM异常8



我的代码大致如下(我删除了一些不相关的部分):

Library.focus = function(event) {
    var element, paragraph;
    element = event.srcElement;
    paragraph = document.createElement("p");
    paragraph.innerText = element.innerText;
    element.parentNode.insertBefore(paragraph, element);        // Line #1
    element.parentNode.removeChild(element);                    // Line #2
};

我遇到的问题是,我编号为第2行的最后一个呼叫抛出了这个:

Uncaught Error: NOT_FOUND_ERR: DOM Exception 8

请注意,前一行#1运行良好,并在其前面插入段落节点。元素是一个现有元素,Element.parentNode和Element.parentNode.removeChild也存在。

我觉得这不合逻辑,因为元素根据定义是其parentNode的子元素。也许StackOverflow可以帮我,在这里?

来自mdn文档:

如果子节点实际上不是元素节点的子节点,则方法引发异常。如果孩子实际上是调用时元素的子级,但已被事件删除在试图移除元素的过程中调用的处理程序(例如,模糊。)

我可以在jsfiddle 中重现这个错误

基本上,您聚焦元素,从而触发移除,从而触发模糊,从而移动元素,从而使元素不再是父元素。

如果你试图在onblur处理程序中修改你试图在另一个处理程序中删除的同一个节点(例如onfocus、keydown等),那么对removeChild的第一个调用将在实际删除节点之前启动onblr处理程序。如果onblur处理程序随后修改了节点,使其父节点发生更改,则控制将从onblr处理程序返回到onfocus处理程序中的removeChild调用,该调用将尝试删除节点,但会失败,出现您描述的异常。

在onfocus处理程序中调用removeChild之前,任何数量的检查子级是否存在都将是徒劳的,因为这些检查将在onblur处理程序触发之前进行。

除了重新安排事件处理和节点修改之外,最好的办法可能是只处理try-catch块中的异常。

以下代码将显示onblur事件处理程序在onfocus中的removeChild实际删除子级之前是如何运行的。它也在jsfiddle 上

html

<div id="iParent">
    <input id="i">
</div>

js

var input = document.querySelector("#i"),
inputParent = document.querySelector('#iParent');
input.onblur = function() {
    console.log('onblur : input ' + (input.parentNode ? 'has' : 'doesnt have') 
            + ' a parent BEFORE delete');
    try {
        inputParent.removeChild(input);
    } catch (err) {
        console.log('onblur : removeChild failed, input ' 
                + (input.parentNode ? 'has' : 'doesnt have') + ' a parent');
        return false;
    }
    console.log('onblur : child removed');
};
input.onfocus = function() {
    console.log('onfocus : input ' + (input.parentNode ? 'has' : 'doesnt have') 
            + ' a parent BEFORE delete');
    try {
        inputParent.removeChild(input);
    } catch (err) {
        console.log('onfocus : removeChild failed, input ' 
                + (input.parentNode ? 'has' : 'doesnt have') + ' a parent');
        return false;
    }
    console.log('onfocus : child removed');
};​

聚焦输入字段后的控制台输出将是

onfocus : input has a parent BEFORE delete
onblur : input has a parent BEFORE delete
onblur : child removed
onfocus : removeChild failed, input doesnt have a parent 

不要将事件作为Library.focus函数的参数传递。它会起作用的。

Library.focus = function() {
    var element, paragraph;
    element = event.srcElement;
    paragraph = document.createElement("p");
    paragraph.innerText = element.innerText;
    element.parentNode.insertBefore(paragraph, element);        // Line #1
    element.parentNode.removeChild(element);                    // Line #2
};

最新更新