Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom element lifecycle and innerHTML usage #25

Closed
lekoala opened this issue May 31, 2024 · 8 comments
Closed

Custom element lifecycle and innerHTML usage #25

lekoala opened this issue May 31, 2024 · 8 comments

Comments

@lekoala
Copy link

lekoala commented May 31, 2024

Hi there,

First of all, little disclaimer, this is not completely related to your project, even if I did try with uce in order to see if my issue was linked to my approach or if any custom element library is having the issue. So if you find my issue out of scope, feel free to close it but i'm sure you will enjoy the challenge :-)

Here is my use case:
I have a custom element that is binding an html node to a third party library (a custom form input). When the custom element is created and the html is parsed (either when using setTimeout or any technique like your "when parsed" library), the third party library is initialized and alter the html to add relevant ui elements.

Now, my issue:
If a third party code is doing something like

const node = document.querySelector('#node-with-the-custom-element');
const othernode = document.querySelector(#some-other-node');

othernode.innerHTML = node.innerHTML;
node.innerHTML = '';

expecting to move content is a very crude way (instead of doing othernode.appendChild(...form.node) which works fine), the cleanup you might do in the disconnectCallback is not working at all. Let's say you want to remove any ui element that has been added, you can try to remove it, but since it's working on a fragment, it's "working" in the sense it's not doing any error, but it's not changing the dom.
at the end, you end up with the ui elements being there two times if the third party library simply adding them without any extra checks.

the obvious solution is: don't do the innerHTML swap, but I was wondering if there was a way to deal with this if you cannot avoid it. one solution i had so far is to store original html based on the id in a map and restore it if needed upon construction of the element, but that seems rather extreme^^.

i've put together a little playground for this: https://codepen.io/lekoalabe/pen/JjqEgxE including uce element as well that have the same issue because they are after all custom elements like any other

@WebReflection
Copy link
Owner

file an issue to your 3rd party library? I mean ... utilities or even frameworks can't, and shouldn't really, solve bad practices out of foreign code stuck in in the 90's ... should these?

@WebReflection
Copy link
Owner

WebReflection commented May 31, 2024

sory I didn't mean to reply grumpy, but I really have zero time for this these days .. you have an issue elsewhere and you want me to solve it here ... I don't have time to understand what your 3rd party library is doing, and I am full time dedicated to other projects so please bear with me ... what do you expect from me, exactly? I allow bad foreign practices to pollute my code to solve your issue with foreign code I've no idea who wrote? Any other hint of how this should make this module better?

@lekoala
Copy link
Author

lekoala commented May 31, 2024

@WebReflection hey no worry that's why i put the disclaimer

but here is why i thought it might apply to your library as well : if you use an uce custom element and that element is being moved because of innerHTML usage (which, while bad, can still happen in large projects and cause unexpected issues), it's lifecycle won't work as expected (meaning, any cleanup in disconnected will not apply)

and therefore, i was wondering if there was any way that the disconnected callback would actually do what you expect from it. i've experimented various approaches and as far as i can tell, it's just not possible to rely on disconnected callback if innerhtml is being used... so yeah, maybe it's just something to be aware of, but I wasn't until facing strange issues :-)

@WebReflection
Copy link
Owner

Disconnect should work regardless because innerHTML doesn’t preserve nodes identities, it trashes previous nodes and create new one. Here it looks like the node is just cloned via its string content but not removed … innerHTML reaches internals, not the node content, so unless you can provide a minimal use case that shows an element removed via any mean doesn’t trigger disconnect, I’m afraid I don’t know how to help

@lekoala
Copy link
Author

lekoala commented Jun 2, 2024

@WebReflection you can check this example where i left only your library

https://codepen.io/lekoalabe/pen/JjqWmep

to summarize, this is what i see:

  • uce element is created with a dynamic button added to the node
  • if you copy the content of the div to another div, i would expect the disconnected callback to allow me to cleanup that dynamic button. calling this.queryselector('button').remove() works, but it has no effect since it apply to some kind of disconnected state
  • when copied to the other node, the button is added again

@WebReflection
Copy link
Owner

copy is not move and copy is not deostry ... is a copy ... a copied source is not disconnected, it's copied ... I am not sure here we're lacking terminology but if you copy stuff without removing it: it's a copy, not a disconnect.

@lekoala
Copy link
Author

lekoala commented Jun 3, 2024

yes, I just realized that it requires some extra checks to guard against double initialization if the content is duplicated or cloned.

maybe this is just out of scope then and needs to be dealt with in each component as needed. i'll close the issue, sorry for bothering you

@lekoala lekoala closed this as completed Jun 3, 2024
@WebReflection
Copy link
Owner

WebReflection commented Jun 3, 2024

@lekoala there's a native DOM API called cloneNode ... you can use it out of any DOM node you like, that doesn't change at all the state of the source node you cloned, it just create new clones of the same node which is still live.

If you want to clone something that is not supposed to be live but it's useful for other nodes, remove that node from its live state and see disconnect happening, then every time you clone that, or copy its innerHTML, you'll see the new node passing through connect and the source will never bother you again, unless you explicitly put it back in the DOM.

This is how the Web and any XML/HTML tree works, so I hope it makes sense to you and helps you forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants