Vue 3 appends two text nodes for a slotted/template elements which breaks XPath selectors
Vue version
3.3.4
Link to minimal reproduction
https://codesandbox.io/s/hidden-water-0wm7te?file=/src/components/Slotter.vue
Steps to reproduce
- Open the link - https://codesandbox.io/s/hidden-water-0wm7te?file=/src/components/Slotter.vue
- See the alerted value (3)
- The value being alerted is the number if childNodes in the slotted element (which is plain text)
This causes issues with people who use XPath for their automation tests. For example try running this command in the devtools
document.evaluate("//*[contains(text(), 'testing')]", document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
It will result in a null value. But if you delete and manually add the same text node using devtools it works because of the missing empty text nodes.
What is expected?
It should alert 1, there is only 1 node being slotted
What is actually happening?
It appends three text nodes, one surrounding the main text node each
System Info
No response
Any additional comments?
No response
From what I can see this happens because we use
hostInsert(fragmentStartAnchor, container, anchor)
hostInsert(fragmentEndAnchor, container, anchor)
I see why we need those anchors and I am unable to think of a better solution at the moment. My current "fix" is like so:
<template>
<div v-if="slotTextValue"
>{{ slotTextValue }}</div
>
<div v-else>
<slot />
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
computed: {
slotTextValue() {
const value = this.$slots.default?.();
if (value?.length === 1 && typeof value[0].children === "string") {
return value[0].children;
}
return null;
},
}
});
</script>
This is necessary for Vue to manage the fragments. "Fixing" this would require a massive refactor, and to be honest I do not believe it can be justified by "breaking XPath selectors". This sounds very specific to the way the test is written. You can assert text by getting .textContent
of the parent element instead of asserting specific text nodes.