NotFoundError: Node.removeChild: The node to be removed is not a child of this node

AI-generated summary

The discussion revolves around the NotFoundError: Node.removeChild: The node to be removed is not a child of this node error when using a theme component to remove elements in the composer preview.

magjac created a theme component that replaces BBCode with SVG, but encountered the error when typing a character after removing elements. magjac was able to reproduce the issue with a simple theme component.

david suggested that the composer preview’s Ember rendering engine is confused by the DOM changes and recommended hiding nodes instead of removing them. david confirmed that this is a quirk of the implementation and not a bug, with no plans to change it.

@riking shared their experience with a similar issue and provided a solution by creating a sacrificial div node for the widget to consume, leaving the original div intact.

I’ve developed a theme component:

https://github.com/magjac/discourse-d3-graphviz

It replaces BBCode with SVG. This works also in the editor preview.

My problem is that in some cases I get an error when I type a character in the editor:

NotFoundError: Node.removeChild: The node to be removed is not a child of this node

I’m pretty sure that this is related to that I have removed some elements, because if I don’t remove anything, I don’t get the error.

EDIT: I doesn’t happen when I do the removal, but when I type the first character in the editor after the removal.

My question is if removing elements is not allowed?

If it is; Do you have any idea why I’m getting this error?

I realize this is kind of vague. I may be able to provide more details if necessary.

The stack trace can be seen here:

EDIT 2: It’s not just elements that I remove. I sometimes remove only text nodes.

I can reproduce this with this simple theme component:

<script type="text/discourse-plugin" version="0.8">

  api.decorateCooked($elem => {
    if ($elem.get(0).className == 'd-editor-preview') {
      const $paragraphs = $elem.children('p');
      $.each($paragraphs, (paragraphIndex, paragraph) => {
        paragraph.remove();
      });
    }
  });

</script>
  1. Create a new topic
  2. Start typing. The error appears in the dev console for the second character that is typed.

The composer preview uses ember to render the cooked content. It looks like that error is coming from the ember rendering engine, which is probably confused by the DOM being changed by something else.

To avoid the issue, I suggest that you don’t remove() any nodes, and instead hide them using display:none; or something similar.

Thanks. That’s a workaround I’ve been thinking of, but I would appreciate if you could confirm that this is a bug or not.

The composer preview is an unusual combination of ember rendering and custom ‘decorateCooked’ functions. It looks like this is a quirk of that implementation. We don’t have any plans to change the way it works at the moment, especially since the workaround is nice and simple.

@j.jaffeux I notice you have this cleanUp function passed to api.cleanupStream when you used WidgetGlue.

I’m getting the error in the OP when I try to write in the composer after my widget attaches. Notably, the original div data-wrap is completely gone after I target it with WidgetGlue.appendTo()…

EDIT: Solved. I create a sacrificial <div> node for the Widget to consume, so that I can leave the [wrap=dice] div alive.

  // elem: <div class="d-wrap" data-wrap="dice">

  //- const glue = new WidgetGlue("dice-result", register, attrs);
  elem.innerHTML = "";
  //- glue.appendTo(elem);
  const sacrificial = document.createElement("div");
  elem.appendChild(sacrificial);
  attachDiceWidget(sacrificial, attrs);