无法在'Node'上执行'removeChild'

时间:2014-02-21 05:33:43

标签: javascript

我正在使用http://alexgorbatchev.com/SyntaxHighlighter/突出显示我网站上的代码,但有时候在我的日志中我会收到像这样的Javascript错误:

  

未捕获NotFoundError:无法在“节点”上执行“removeChild”:要删除的节点不再是此节点的子节点。也许它被移动到'模糊'事件处理程序中?

     

Uncaught NotFoundError:尝试在不存在的上下文中引用Node。

// set up handler for lost focus
attachEvent(textarea, 'blur', function(e)
{
   textarea.parentNode.removeChild(textarea);
   removeClass(highlighterDiv, 'source');
});

这是attachEvent()函数代码:

function attachEvent(obj, type, func, scope)
{
    function handler(e)
    {
        e = e || window.event;

        if (!e.target)
        {
            e.target = e.srcElement;
            e.preventDefault = function()
            {
                this.returnValue = false;
            };
        }

        func.call(scope || window, e);
    };

    if (obj.attachEvent) 
    {
        obj.attachEvent('on' + type, handler);
    }
    else 
    {
        obj.addEventListener(type, handler, false);
    }
};

任何人都可以帮忙解决此问题吗?

11 个答案:

答案 0 :(得分:48)

我不得不处理同样的问题,答案是混乱和反直觉。嗯,它有其逻辑,但会导致非平凡的行为。

基本上,当从DOM树中删除节点时,它会触发blur事件(以及focusout事件之前)。

因此,当您致电removeChild时,blur事件再次被重新启动,但这次textarea 仍然已定义parentNode,但textarea不再是其父母的子女之一! (是的,请阅读两次。或者更多。)

目前在Chrome中发生这种情况,虽然Firefox已经有planned to do the same一段时间了。

作为解决方法,您可以删除附加的事件侦听器:

var onblur = function(e) {
    detachEvent(textarea, 'blur', onblur);
    textarea.parentNode.removeChild(textarea);
    removeClass(highlighterDiv, 'source');
};
attachEvent(textarea, 'blur', onblur);

我假设您有一些detachEvent函数来删除事件侦听器。根据您的需要调整代码。

或者,您可以在侦听器函数中的textarea上设置标志(如属性,属性或更好的范围变量),并在继续删除节点之前检查它:

var removed = false;
attachEvent(textarea, 'blur', function(e) {
    if (removed) return;
    removed = true;
    textarea.parentNode.removeChild(textarea);
    removeClass(highlighterDiv, 'source');
});

您还可以在删除之前检查textarea是否实际上是parentNode的子节点,但这样的测试是非常违反直觉的(至少对我而言)我不建议这样做因为担心将来会改变这种行为。

最后,你总是可以依赖try...catch声明,但是......呃。

2016年更新

自然地,使用像jQuery这样的框架可以为你节省大量使用one附加事件监听器的工作,但是这个功能也将来到standard addEventListener

textarea.addEventListener('blur', handler, { once: true });

答案 1 :(得分:5)

我在这里走出困境并假设有些发现此问题的人在这里,因为你搜索了上面引用的错误:

  

' removeChild之' on'节点':要删除的节点不再是孩子   这个节点。也许它被移动了一个模糊的'事件处理程序?

我正在使用jQuery& DataTables 1.10.6并且这个错误确实出现在我正在更新单元格值的blur()事件中。我的解决方案是在事件中添加一个条件,如下所示:

var blur = false;
$('table').on('blur', 'td select', function() {
    if (!blur) {
        blur = true;
        var cell = $(this).closest('td');
        table.cell(cell).data('example data');
        blur = false;
    }
});

如果没有更好的解决方案,我会感到非常惊讶,但希望这可以帮助某人,也许可以获得一些建设性的评论。

答案 2 :(得分:5)

虽然这个问题的经典答案是它是JS代码中的一个错误,但是React 16存在一个主要错误,这意味着任何修改DOM的浏览器/扩展机制都会破坏React。

Google Chrome内置的翻译功能是最常见的罪魁祸首。

GitHub问题:https://github.com/facebook/react/issues/11538

最小案例:https://p93xxmr0rq.codesandbox.io/(只需在Chrome中右键单击“翻译为”,然后从日语中选择,然后点击按钮)

解决方法:How to disable google translate from html in chrome

<meta name="google" content="notranslate">

答案 3 :(得分:2)

而不是textarea.parentNode.removeChild(textarea);您可以使用textarea.parentNode.innerHTML = ''或类似内容,具体取决于您的具体情况。

答案 4 :(得分:2)

我使用时间延迟,它对我有用。

    // set up handler for lost focus
    attachEvent(textarea, 'blur', function(e)
    {
        setTimeout(function() {
            textarea.parentNode.removeChild(textarea);
            removeClass(highlighterDiv, 'source');
        }, 0);
    });

答案 5 :(得分:1)

我在一个简单的jQuery脚本上发现了相同的错误消息。 当我试图获得元素$('#element')。height()的高度时,我在控制台上看到了同样的错误。

元素存在且$('#element')。长度为1。

使用console.log($('#element'))的元素日志就像一个jQuery对象,但重复了很多方法。

经过大量的调试后,我发现添加一个$(document).on('DOMContentLoaded DOMNodeInserted')事件处理程序给jQuery对象带来了这个奇怪的东西。

希望这有助于某人造成这很难找到。

答案 6 :(得分:1)

我收到了这个错误,上面的任何内容都没有帮助我,最终我的结果很简单:

[onclick*='set']

对我来说非常适合。

答案 7 :(得分:0)

尝试使用Google Chrome浏览器翻译您的网站(右键单击该网站,然后选择“翻译成英语”。如果您在控制台中看到错误,则表明它是由Google翻译引起的。

相关的GitHub问题:https://github.com/facebook/react/issues/11538


Dan Abramov的解决方法:

if (typeof Node === 'function' && Node.prototype) {
  const originalRemoveChild = Node.prototype.removeChild;
  Node.prototype.removeChild = function(child) {
    if (child.parentNode !== this) {
      if (console) {
        console.error('Cannot remove a child from a different parent', child, this);
      }
      return child;
    }
    return originalRemoveChild.apply(this, arguments);
  }

  const originalInsertBefore = Node.prototype.insertBefore;
  Node.prototype.insertBefore = function(newNode, referenceNode) {
    if (referenceNode && referenceNode.parentNode !== this) {
      if (console) {
        console.error('Cannot insert before a reference node from a different parent', referenceNode, this);
      }
      return newNode;
    }
    return originalInsertBefore.apply(this, arguments);
  }
}

在呈现您的应用之前运行此代码。请记住,这会对性能造成轻微影响。


香川修平的解决方法

渲染<span>中的文本。

// Case 1
<div>
  {condition && 'Welcome'}
  <span>Something</span>
</div>

// A workaround for case 1
<div>
  {condition && <span>Welcome</span>}
  <span>Something</span>
</div>

// Case 2
<div>
  {condition && <span>Something</span>}
  Welcome
</div>

// A workaround for case 2
<div>
  {condition && <span>Something</span>}
  <span>Welcome</span>
</div>

可在此处找到详细说明: https://github.com/facebook/react/issues/11538#issuecomment-390386520

答案 8 :(得分:0)

从程序中触发的事件类型可能与 Dom 的默认操作不同。如果您检查类型并使用 if (type === 'typeYouWant') 作为保护子句,则可能会跳过不需要的代码执行。控制台记录事件以检查是否存在可以隔离的“类型”。

   formEditEl.addEventListener('blur', this._cancelEdit.bind(this));

  _cancelEdit(e) {
    if (e.type === "change") document.querySelector('.form__edit').remove();
    };

答案 9 :(得分:-1)

每次执行方法时都需要检查元素是否是给定父元素的子元素。

if(box.parentElement === parentBox) {
    parentBox.removeChild(box);
}

答案 10 :(得分:-2)

为什么不使用jquery:

$("#element_id").remove();