-
Notifications
You must be signed in to change notification settings - Fork 191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. #1401
Comments
Any news about this one? |
@sebamza17 do you have any code that modifiers / remove DOM on its own, unknown to Glimmer? A reproduction repo would be most helpful. |
@NullVoxPopuli: For me, this error is logged in sentry from time to time from real world usage by customers (both from Chrome and Edge users) No steps to reproduce yet (the closest I've got is html-next/vertical-collection#397), but a hypothesis is that some users have browser extensions that manipulate the DOM. Anyways, glimmer should be more resilient to such errors IMO. |
Also seeing this pop up from time to time in our bug tracker, but unable to recreate it myself. Maybe just a specific error message instead of just the default DOMException would already help in tracking down the real issue? |
We get this one in our logs using latest ember-table 5.0.6 and ember 3.28. We conditionally insert a div into the occluded list managed by vertical-collection, and the conditional's Comment Node in the DOM is the node implicated when breaking on the error. <et.body as |b|>
{{#if (eq this.rowHighlightId b.rowValue.id)}}
<div class="rowHighlight"></div>
{{/if}}
<b.row/>
</et.body> Conditionals have been a meaningful contributor for another glimmer-vm bug (#1410). Would be lovely if there was some common underlying bug with glimmer & conditionals, but I just mention this as a moonshot. |
This hack |
@golampo Thanks but wrapping things that are affected by this issue in I think this is a very common use case that warrants more resilient handling as OP suggests. |
@shama The linked comment describes that the issue comes from the existing usages of |
Fixes glimmerjsGH-1401 If `current` isn't a child of `parent` calling `parent.removeChild(current)` will result in the error: `Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node`. This commonly occurs when the DOM is externally modified by a browser, such as when translating a page. The `nextSibling` may not be a child node of `parent`. `current.parentNode?.removeChild(current)` ensures we are only removing a child node of a parent node.
Fixes glimmerjsGH-1401 If `current` isn't a child of `parent` calling `parent.removeChild(current)` will result in the error: `Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node`. This commonly occurs when the DOM is externally modified by a browser, such as when translating a page. The `nextSibling` may not be a child node of `parent`. `current.parentNode?.removeChild(current)` ensures we are only removing a child node of a parent node.
Let’s say the template is {{#if this.isExpanded}}
the time is {{this.currentTime}}.
{{/if}} We kept a reference to the TextNode with the current time. When the time changes we need to update it. If at that time we discovered the text node is gone because google translate decided it’s ok to merge the adjacent text nodes together during translation, what are we supposed to do? When the condition changes to false we need to clear the DOM. What are we supposed to do if we realized the nodes are not there anymore because Google translate decided to remove ours and make new ones in their place? Sure we can fail silently but that wouldn’t be correct either, you’ll just leave behind the translated text when it should have been removed. I understand why it feels like we should “just fix it” but we need to be more specific about how we are expected to interop with things that are allowed to arbitrarily change the DOM we manage |
Fixes glimmerjsGH-1401 If `current` isn't a child of `parent` calling `parent.removeChild(current)` will result in the error: `Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node`. This commonly occurs when the DOM is externally modified by a browser, such as when translating a page. The `nextSibling` may not be a child node of `parent`. `current.parentNode?.removeChild(current)` ensures we are only removing a child node of a parent node.
Fixes glimmerjsGH-1401 If `current` isn't a child of `parent` calling `parent.removeChild(current)` will result in the error: `Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node`. This commonly occurs when the DOM is externally modified by a browser, such as when translating a page. The `nextSibling` may not be a child node of `parent`.
Does anyone have a good debugging play when they encounter these? Perhaps a way to break execution the moment the Ember-managed element leaves the DOM in an unsupported way? Currently we are only seeing downstream of the root issue. We don't find out the DOM was invaded until Ember tries to remove an already-removed managed element. But reactivity broke long before then. Since we can't fix this downstream, we need to see if there are fixes that can be done at the root cause. I think there's 2 classes of this issue. One is triggered by external actors, like Google translate or browser extension content scripts. The other kind comes from inside the house, e.g. it's possible to trigger these with handlebars conditionals inside We should be able to address the instances caused by the Ember ecosystem itself, even if there's no good solution to the external actors problem. There's maybe a clever spot to set up a MutationObserver. Temporarily during local development? With flag support inside Glimmer? Does knowing the parent element give us a foothold? Being able to catch these in the moment will lead to reproduceable bug reports that don't involve external actors. If there were a performant detector, it could even be used in production to show a banner or toast to users when they break reactivity via external actors. If we can't fix the root issue, maybe we can improve the user experience when it happens. |
In the past I have tried to discourage page translation with some HTML meta. Something like <!DOCTYPE html>
<html lang="en-US" translate="no" class="notranslate">
<head>
<meta name="google" content="notranslate"> Although not specific glimmer and targeted at only a single root cause (page translation) Also for context: Historical react issue facebook/react/issues/11538 |
also cross-linking #1438 (comment) for visibility and context |
@gilest Yeah, I think that sounds about right and to the extent you are able to discourage these unauthorized mutations from happening, that would be the most ideal. Beyond that, I think trying to catalog and the type of behavior of these tools may also help you find targeted workarounds. For example, if my assumption is correct that (one of) the issue is Google translate sometimes merging/replacing adjacent text nodes while doing the translation in a part of the DOM we are tracking, say: {{!-- [ ... ] denotes a single text node --}}
[Hello, welcome ]{{#if @user.isNew}}[aboard]{{else}}[back]{{/if}}[.] {{!-- rendered --}}
[Hello, welcome ][aboard][.] {{!-- translated --}}
[你好,歡迎光臨。] Then, maybe you can prevent that somewhat by: {{!-- [ ... ] denotes a single text node --}}
[Hello, welcome ]{{#if @user.isNew}}<!-- -->[aboard]<!-- -->{{else}}[back]{{/if}}[.] {{!-- [ ... ] denotes a single text node --}}
[Hello, welcome ]<!-- Glimmer can now use this comment node for the bound start -->[aboard]<!-- bound end -->[.] If Google translate is civilized enough to know that it can't just remove comments randomly, then as far as we are concerned we don't care if they replace the middle text node. If we have specific cases we can analyze we can try to encourage the other party to play nicely, but yes, I think the root cause is not really fixable on our end. |
Interesting. It sort of looks like Fastboot output markers for rehydration. I think I read somewhere that Google Translate was particularly bad at mangling the DOM, like maybe it adds Would your suggestion survive something like that? Also curious about the performance impact of managing said comments... 🤔 |
Tracked by Chromium issue #872770 It's 5 years old... 💀 |
We have found in last time also this error in our production error logs... To fix this you need to add <!DOCTYPE html>
<html lang="en-US" translate="no" class="notranslate">
<head>
<meta name="google" content="notranslate">
</head>
<body translate="no">
</body>
</html> |
I see the error
Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node
popping up in sentry from time to time. I have yet to reproduce it when I try, but I know it freezes the whole UI when it happens.It is not a very viable solution to tell users to uninstall various chrome extensions and/or not use google translate in order to not break a glimmer app. This has occurred both on Chrome and Edge.
Is it possible with a more resilient handling in
clearElement
?glimmer-vm/packages/@glimmer/util/lib/dom.ts
Lines 4 to 12 in 4f1bef0
Perhaps a try-catch? Or will that disturb some other inner workings?
The text was updated successfully, but these errors were encountered: