core icon indicating copy to clipboard operation
core copied to clipboard

$el of templateRef points to comment VNode instead of first real child of component

Open markbrockhoff opened this issue 11 months ago • 9 comments

Vue version

3.5.15

Link to minimal reproduction

https://play.vuejs.org/#__PROD__eNp9U9uO2jAQ/ZWpVWlBAqOK9oUCUot4aNWbKI+WqigZghfHjnzJIiH+vWPnUrqs9i32nHN8Zs7kwj7VNW8CsgVbutzK2oPKdLkSzDvBwKEP9VpoWdXGetgcpSrgYE0FD3yWTpH88HFAXCA43GNVq8zjDg8TMPq7CdpjAdeO2TGEzo12HvIoQ1BYPeOOBEs1wcYEH3RGozGs1nARGiAqGIVcmbJHRyVUWKH2C8EmgzxvMhWQv0UV1Yh6d8+zotg2xPsmnUeNNkoqmZ+izM2jz5/dRAz1t0fnW68A17HQ1/i5nLVjpSHSwXfd0QlgWcgGcpU5R+OuM0svC5YqVGtHbfFAtW4KMGtpM+KtAaL2jR6bUGRk7CBL/uiMpkSTW6KbqpYK7c/aSzIu2KLvQ7BMKfP0Nd15G3DS3+dHzE8v3D+6c7wT7JdFh7ZBmk1f85ktkVqI5e3vH3im76FYmSIoQr9S3CENNUSPLexz0AXZvsElt1/Spkld7t32TDm5vqloNM0+4QWjPdu80vo/u3P+PvEoMprisNbxn/g/sDfTKeywMg2CP0pHe1DFPQNvoMpOSGHSJpAzWnp6oN0HeDL2BNPpfeRdrF3iCT3Eex/unwZtbJVczfkH/m7Orn8BgY08Yw==

Steps to reproduce

Hi, I just spent some hours debugging a very strange behavior when accessing the underlying element of a component ref by using templateRef.$el. The component I placed the template ref on had only one root level <div> inside its template, however there was a comment above it. It looks like this leads to the element being referenced by the template ref being the VNode of this comment instead of the <div> element.

Steps to reproduce:

  1. Open the Playground
  2. Open the console and see that the element referenced by childRef.value.$el is a text VNode
  3. Click on the text "Test" in the preview (Nothing should be logged to the console)
  4. Go into the component "Child.vue" and remove the comment in line 2
  5. After a hot reload (or manual one) childRef.value.$el should point to the div element as logged to the console. Clicking on the text "Test" in the preview should now also log "Clicked Test" to the console

What is expected?

I would expect templateRef.$el to point to the first real element inside the component excluding comments.

What is actually happening?

templateRef.$el points to a VNode for the comment inside the components template.

System Info

No response

Any additional comments?

No response

markbrockhoff avatar Jan 10 '25 12:01 markbrockhoff

Hey! Not working on the open source (not yet!) but hopefully can provide some insight on the topic, as I do develop in Vue.

Your Child component has two nodes, one HTMLDivElement and one of type Comment.

Vue will get the actual node if you are pointing to a html node or a Vue component that has only one child.

Therefore, this has nothing to do with the comment. You are asking Vue to add an event listener to a Vue Component that doesn't have a clear single valid node. Vue is not pointing to the Comment VNode, as you are suggesting.

It doesn't know where do you expect to assign the event listener. So, IMHO, this is expected behaviour.

Ways to fix this:

Solution 1 Wrap both components within a HTML node, such as another div.

Solution 2 Add a ref in the child's node where you want the addEventListener placed, in this case the div. You can later access that from the Parent component using childRef.value.$refs.yourRefName

Having said all that, though... From a dev's POV I would agree that it would make more sense if the comment was simply ignored.

xfontr avatar Jan 11 '25 16:01 xfontr

The root VNode of Child.vue is a Fragment. the text node is the Fragment's anchor.

edison1105 avatar Jan 12 '25 01:01 edison1105

Having said all that, though... From a dev's POV I would agree that it would make more sense if the comment was simply ignored.

Exactly, if there were two actual element inside the root of the template I'd totally agree and expect the issue. However having Vue respect a comment as part of the Fragment feels unintuitive.

markbrockhoff avatar Jan 13 '25 07:01 markbrockhoff

@edison1105 to clarify, would you say the current behavior is the desired behavior?

Another argument I can see towards the comments being ignored in this case is that it would make the user-facing behavior consistent with how comments in the template root are ignored for fallthrough component attributes e.g. https://play.vuejs.org/#eNp9UT1PwzAQ/SuHly5NMsBUQiWoOsAACDp6Cck1cXFsyz6Hoqr/HdtRS0FVN9/7sN7d27F7Y/LBI5ux0tVWGAKH5M2cK9EbbQkWnZANrK3uYZIXaYqGCVdlMTqCNgyEvZEVYZgAytHl6FviHWe1ltrOwGJzyxkUQVIWJ3o2ZeRqrdaizTdOqxBmF3+Jxt4IifbFkNDKcTaDxESuklJ/PSWMrMfpAa87rD/P4Bu3jRhnrxYd2gE5O3JU2RZppJfvz7gN7yPZ68bLoL5AvqHT0seMo+zBqybEPtGltI/ppEK1K7fcEip3WCoGjcp90nMWDry4sPpv3Ov8Jvm42ocrHtuJdf4t5CrLIHzZoyLIshFrxDBfdcIBSkyExRrFgA6oQ1iHyNRZ7dsOKiIrPjxhWUTT//72P9Ogwmk=

alex-snezhko avatar Jun 12 '25 21:06 alex-snezhko

Indeed, this behavior is not what most developers would expect. Although it's easy to work around by simply removing the comment, it is still an edge case. I do think there's room for optimization here. if $el could automatically ignore comment nodes, it would make the behavior more consistent with other scenarios and improve the DX. However, this might introduce a breaking change and could affect projects that rely on the current implementation. So, if such an adjustment is made, it would be better to do it in a major version update.

edison1105 avatar Jun 13 '25 00:06 edison1105

Makes sense! Do you think it would be worthwhile to allow this behavior via an opt-in flag which can be removed in the next major version to avoid a breaking change? It seems there is precedent for doing something like this https://github.com/vuejs/core/issues/4196#issuecomment-887860658 but I'm unsure if these temporary flags are still a recommended policy for new changes

alex-snezhko avatar Jun 13 '25 20:06 alex-snezhko

@alex-snezhko I think for this issue, there’s no need to introduce an additional flag. We can just add a DEV-only warning in the current version to inform users of the upcoming change. Then, submit a PR to the minor branch to update the behavior of $el directly.

However, I think for this edge case, users actually have a solution available, so it may not be worth introducing extra handling. I’d recommend Solution 2 mentioned by @xfontr in the comments above.

edison1105 avatar Jun 16 '25 00:06 edison1105

comment node is a standard domNode,it is specification,we should not compromise for the sake of so-called 'user perception', even adding a configuration item when using APIs is acceptable

guda-art avatar Nov 13 '25 09:11 guda-art

Hi everyone! When the template contains multiple root nodes, $el still points to a #text node — is this the expected behavior? Vue Playground

update: https://vuejs.org/api/component-instance.html#el

rzzf avatar Nov 13 '25 10:11 rzzf