Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node."

Hello,

I’ve replicated this in a new minimal Vue app:

which can be run locally with `npm run serve’ and accessed at http://localhost:8080/#/detail/123

This happens on Vue page which is defined in router as

                path: 'records/:id',
                name: 'RecordDetail',
                props: true,

and linked with ‘a.com/#/records/123

Record with id 123 is loaded from database, usually rendered properly (other times it has this issue: https://stackoverflow.com/questions/58729274/vue-font-awesome-svg-sometimes-renders-icons-twice )
then clicking on a button which has handler ‘itemConfirm’ gives the same error:

vue.esm.js?a026:628 [Vue warn]: Error in nextTick: “NotFoundError: Failed to execute ‘insertBefore’ on ‘Node’: The node before which the new node is to be inserted is not a child of this node.”

itemConfirm @ RecordDetail.vue?f97b:145

click @ RecordDetail.vue?e784:50

invokeWithErrorHandling @ vue.esm.js?a026:1863

invoker @ vue.esm.js?a026:2184

original._wrapper @ vue.esm.js?a026:7559

vue.esm.js?a026:1897 DOMException: Failed to execute ‘insertBefore’ on ‘Node’: The node before which the new node is to be inserted is not a child of this node.

There’s no nextTick() function in my code. I do NOT use v-for (saw this as another cause).

The whole component code, simplified and still giving the error:

This forum doesn’t render script code, so here it is: https://stackoverflow.com/questions/58729274/vue-font-awesome-svg-sometimes-renders-icons-twice

Record JSON data as loaded from server:

{
  "id": "mkt7y3v0m5vupnal",
  "practitioner_id": "yyie5csyc0y1lwcg",
  "day": "2019-11-08",
  "start_time": "16:00",
  "end_time": "16:45",
  "status": "New",      
  "practitioner_name": "Dr Ruby Decker"
}

When clicking Confirm, the page often renders this duplicated content:

Screenshot%20from%202019-11-07%2012-29-14

Removing this condition
v-if="item.status == 'New'"

gets rid of the error.

Any hints, please? (@LinusBorg maybe you’d have a suggestion?)

1 Like

It sure does: Please read the following guide about how to properly format code and then edit your topic accordingly.


I’d take a look right now but I can’t access public dropboy links from the office. A github repository would have been easier.

I’ll take a look after work later.

Thanks, Linus!
It seems there’s a known issue with Font Awesome SVG and Vue. Instead of using the bundle, they recommend the vue-Font Awesome plugin. It was quite hard to find that out, though, as the behavior is weird.

Appreciate your time, the cause was tracked down after a few wasted hours.

2 Likes

A simple key on the paragraph with the v-if works as well:

<p v-if="item.status == 'New'" key="new_p">
  Please confirm your appointment:
</p>

I’ve seen these kinds of issues before but never manage to track the exact source, but a key usually helps Vue to target the right elements in these cases.

4 Likes

Thank you, Linus! Didn’t know key works for more than v-for. Will use that in future in similar situations.
Have a good weekend :slight_smile:

Adding a key to your v-if="condition" will clear up this issue.

I found this error pops up when you have 2 v-if conditions comparing the same condition, which I’m assuming (or guessing) stops Vue from knowing which element to target.

And how can I avoid duplicate key when using async components?

<div v-if="completedLoadingContentItems">
	<div v-for="(instance, index) in editorInstances" :key="index">
		<TheHtmlEditor v-if="userStore.isUserAdminOrEditor" :key="index + 1"
				:contentItem="instance.contentItem" />
		<TheHtmlViewer v-else :htmlData="instance.contentItem.content[0]" />
	</div>
</div>

image

The component “TheHtmlEditor” itself also has an async component in order to load the editor (with many imports) dynamically. Also here I use a key, but assigning the key to “ci_inline” seems not to work / is overwritten by Vue:

<template>
	<div class="container-fluid pb-3">
		<div :style="borderStyles">
			<div v-if="!isDraftMode" v-html="contentItem.content" :key="ci_inlined" />
			<TheWysiwygEditor v-else :id="contentItem.id"
				:content="contentItem.draft" />
		</div>
	</div>
</template>

Ensure the keys are unique. Using index is not recommended - it’s akin to not providing a key at all.

Is ci_inline a unique value or just a string? I assume you’re using it to refresh the html content? I’d expect that to work, but it’s been awhile since I dabbled with v-html in this way. Another approach could be to use $forceUpdate

I’ve updated the code to generate keys that have each a proper prefix so the components differ in the rendered page. However, there seems to be no way to define the key for the AsyncWrapper as you can see in the screenshot below.

TheCMsPage.vue:

<template>
	...
	<div v-for="instance in editorInstances || [] as EditorInstance[]"
		:key="'editor-choice_' + instance.contentItem.id">
		<TheHtmlEditor v-if="userStore.isUserAdminOrEditor"
			:key="'html-editor_' + instance.contentItem.id"
			:contentItem="instance.contentItem" />
		...
	</div>
</template>

<script setup lang="ts">
// imports...

const TheHtmlEditor = defineAsyncComponent({
	loader: () => import('@/components/cms/TheHtmlEditor.vue'),
	loadingComponent: TheLoadingHtmlEditor,
	delay: 2000,
	errorComponent: TheErrorCouldNotLoadHtmlEditor,
	timeout: 30000
	// would be great to define a key here, but defineAsyncComponent does not provide it
	//key: 'Async_HtmlEditor_' + Math.ceil(Math.random() * 10000)
})
</script>

TheHtmlEditor.vue:

<template>
	...
	<div :style="borderStyles">
		<TheWysiwygEditor v-if="isDraftMode" :id="contentItemLocal.id"
			:key="'wysiwyg_' + contentItem.id" :content="contentItemLocal.draft[0]" />
	</div>
</template>

<script>
// imports...

const TheWysiwygEditor = defineAsyncComponent({
	//loader: () => retryImport(() => import(/* @vite-ignore */ '@/components/cms/TheWysiwygEditor.vue')),
	loader: () => import('@/components/cms/TheWysiwygEditor.vue'),
	loadingComponent: TheLoadingHtmlEditor,
	delay: 200,
	errorComponent: TheErrorCouldNotLoadHtmlEditor,
	timeout: 5000
	// would be great to define a key here, but defineAsyncComponent does not provide it
	//key: 'Async_Wysiwyg_' + Math.ceil(Math.random() * 10000)
})
</script>

And the actual Editor is within TheWysiwygEditor.vue which loads a huge payload of imports (which is why an async loading of this component is crucial):

<template>
	<ckeditor v-model="editorData" :editor="editor" :config="editorConfig" @ready="onEditorReady"
		:key="'ck-editor_' + id" />
</template>

<script>
// import ckeditor libraries..

export default {
	name: 'TheWysiwygEditor',
	components: {
		ckeditor: CKEditor.component
	},
	props: {
		content: {
			type: String,
			default: ''
		},
		id: {
			type: String,
			default: ''
		}
	},
	//...
}

</script>

In order to avoid confusion, I have removed the HtmlViewer that binds html content via a v-html, so the problem actually only deals with the dynamic loading of async components.

When I’m logged in to the page as an admin, I’m able to press “Edit draft” and where the green area is shown on the page, the HtmlEditor is loaded through an AsyncComponent. The list of components however reveals that the AsyncComponentWrapper carries the same key as its child component.

When I’m saving or cancelling the draft which sets isDraftMode to false the v-if removes the html editor and throws the same error as mentioned in the OP.

I’ve searched a long while to find some blog or online article about this approach “use a v-if around an async component” but found nothing that would help.

I’ve made sure all components where required have individual keys. Also, I removed all font-awesome tags to make sure that they can be excluded as error source.

Again, the only two components with the same key are the AsyncComponentWrapper and its child TheHtmlEditor which I strongly assume that this is triggering the above error.

First time Vue 3 would give me that much trouble spending 2 full days analyzing and debugging.

Since I have no idea how to set a key for the async component differently from its child I won’t be using “v-if in combination with an async component” any longer.

I’d say take a step back… why do you need a key on the editor? The loop I get because it needs keys to manage the order, but why do you need it on the editor?