无法在“节点”上执行“removeChild”

IT技术 javascript
2021-03-22 17:13:32

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

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

未捕获的 NotFoundError:尝试在不存在的上下文中引用节点。

// 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);
    }
};

任何人都可以帮助解决这个问题吗?

6个回答

我不得不处理同样的问题,答案令人困惑且违反直觉。好吧,它有其逻辑,但会导致不平凡的行为。

本质上,当一个节点从 DOM 树中移除时,它会触发一个blur事件(并且focusout也在一个事件之前)。

因此,当您调用时removeChild,该blur事件将再次被触发,但这次textarea 仍然parentNode定义了,但textarea不再是其父级的子级!(是的,读两遍。或更多。)

这种情况目前在 Chrome 中发生,尽管 Firefox 已经计划在很长一段时间内这样做

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

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,但此功能也将成为标准addEventListener

textarea.addEventListener('blur', handler, { once: true });
@OCDev 我理解这种感觉,但从语义上讲, atry...catch应该用于我们不想破坏代码的不可预测的行为(或者说,用于特征检测)。尽管听起来很奇怪,但这实际上是一个记录在案的标准案例。
2021-04-23 17:13:32
@scootergrisen 是的,我编辑了我的答案以包含几个解决方案。
2021-05-01 17:13:32
绝对try...catch用于这样的废话。
2021-05-06 17:13:32
谢谢。我不明白,但你知道如何更改代码以避免错误吗?
2021-05-09 17:13:32
救了我的屁股,已经调试了两个小时了!不知道触发了模糊...
2021-05-18 17:13:32

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

谷歌浏览器的内置翻译功能是最常见的罪魁祸首。

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

最小情况:https : //p93xxmr0rq.codesandbox.io/(只需在 Chrome 中手动右键单击“翻译为”并选择日语,然后单击按钮)

解决方法:如何在 chrome 中从 html 禁用谷歌翻译

<meta name="google" content="notranslate">
从用户体验的角度来看,禁用谷歌翻译可能不是您应用的最佳解决方案。然而,从 DX 的角度来看,这是一个很好的解决方案。除了完全禁用谷歌翻译之外,还有一个解决方法github.com/facebook/react/issues/11538#issuecomment-390386520
2021-04-26 17:13:32
我尝试了很多解决方案,最后在阅读您的评论后,我禁用了谷歌自动翻译。现在错误消失了,不再反应崩溃。谢谢。
2021-05-21 17:13:32

我在这里冒险,并假设你们中的一些人发现这个问题是因为你在谷歌上搜索了上面引用的错误:

'Node' 上的 'removeChild':要移除的节点不再是该节点的子节点。也许它是在“模糊”事件处理程序中移动的?

我正在使用 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;
    }
});

如果没有更好的解决方案,我会感到非常惊讶,但希望这对某人有所帮助,并且可能会获得一些建设性的意见。

我如何格式化注释中的代码,即使有 3 个反勾也不允许我
2021-04-23 17:13:32
只是想在这里更新答案,因为其中有错误。在您的事件处理函数中,您将模糊设置为 true,然后设置为 false。这行不通,你只需要像这样在最后将模糊设置为 true:` $('table').on('blur', 'td select', function() { if (!blur) { var cell = $(this).closest('td'); table.cell(cell).data('example data'); blur = true; } });`
2021-04-27 17:13:32
@Fusion - 我明白了,通常我不习惯在 javascript 标记的问题上发布 jQuery 答案。然而,这篇文章的原因是我遇到了完全相同的错误,因为 jQuery和 DataTables。确实,它不是专门解决这个问题,但我正在解决他在我遇到的上下文中提到的错误。由于这是我发现的最高谷歌结果,因此有人到达这里并发现此答案有用的机会与任何人一样好。有时我会为了更多的人而发布答案。
2021-05-02 17:13:32
@jjsquared - 不。
2021-05-10 17:13:32
叮!你就是这样做的!
2021-05-11 17:13:32

尝试使用谷歌浏览器翻译您的网站(右键单击该网站并选择“翻译成英文”。如果您在控制台中看到错误发生,那么您就知道这是由谷歌翻译引起的。

相关 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);
  }
}

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


Shuhei Kagawa 的解决方法

<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

我怀疑这块代码是否能完成这项工作,但确实做到了。
2021-05-04 17:13:32
很难说,对于大多数应用程序来说,这无关紧要。你必须测试看看。
2021-05-09 17:13:32
这是一个救命稻草。从未想过这可能是原因。
2021-05-16 17:13:32

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

    // 设置失去焦点的处理程序
    attachEvent(textarea, 'blur', function(e)
    {
        设置超时(功能(){
            textarea.parentNode.removeChild(textarea);
            removeClass(highlighterDiv, 'source');
        }, 0);
    });
好的!对我来说,这是最简单、最直观的解决方案。
2021-05-10 17:13:32