`v-for` content rendered outside of parent element
Vue version
3.2.47
Link to minimal reproduction
https://jsfiddle.net/websiter/p3w7t4j8/
Steps to reproduce
Run the fiddle
What is expected?
<tr>
s should render inside <table>
What is actually happening?
<tr>
s render outside <table>
System Info
No idea. It happens in browser, when using https://unpkg.com/vue@3.2.47/dist/vue.global.prod.js I'm using Chrome (Version 110.0.5481.177 (Official Build) (x86_64)), on Mac (macOS Ventura Version 13.2.1 (22D68)) - I don't think it's relevant.
Any additional comments?
If we replace the
<template v-for="driver in drivers" :key="driver.position">
<Driver v-bind="driver"></Driver>
</template>
, it works as expected. See it here: https://jsfiddle.net/websiter/p3w7t4j8/1/
I'm not sure if this is because driver's template contains a v-for, or if it has to do with runtime compiling. I haven't checked if this has already been reported in some other shape or form.
Ran into the bug while answering a SO question. https://stackoverflow.com/a/75665085/1891677 (I believe that's kind of irrelevant, but you suggested I write this down, so...)
I believe this is a consequence of using an in-DOM template, as documented at https://vuejs.org/guide/essentials/component-basics.html#element-placement-restrictions.
The HTML in the template gets processed by the browser before Vue gets access to it. Consider the following example, which doesn't involve Vue:
https://jsfiddle.net/skirtle/1Lmfk4dw/
That example uses the following HTML:
<table>
<tbody>
<tr></tr>
<template></template>
<Driver></Driver>
</tbody>
</table>
However, if you run that example and inspect the elements, you'll see that the <Driver>
element has moved:
<Driver></Driver>
<table>
<tbody>
<tr></tr>
<template></template>
</tbody>
</table>
The <tr>
and <template>
elements are allowed inside a <tbody>
, but the browser moves the <Driver>
element outside the <table>
.
In your example, Vue doesn't get chance to see the original markup, it only sees the innerHTML
of the <div id="app">
after the browser has moved the <Driver>
element to be outside the <table>
.
It is largely coincidental that the Vue special tag <template>
shares the same name as the native <template>
element, which results in it not being moved outside the <table>
during the browser's initial parse.
Interesting. Thanks for the explanation. Makes perfect sense.
Coincidental or not, using <template />
as the vessel for Vue templates therefore allowing them anywhere, even in elements with a very short list of allowed children, was a smart choice, to say the least.
I guess that settles this issue, then.
Thank you, @skirtles-code.
Pretty cool workaround/syntax, pointed out by Moritz Ringler, digged from docs:
<tr
is="vue:driver"
v-for="driver in drivers"
:key="driver.position"
v-bind="driver"
/>