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

Cleaning up (undefining) custom elements? #754

Closed
trusktr opened this issue Jul 6, 2018 · 57 comments
Closed

Cleaning up (undefining) custom elements? #754

trusktr opened this issue Jul 6, 2018 · 57 comments

Comments

@trusktr
Copy link
Contributor

trusktr commented Jul 6, 2018

I notice many web-based libs/APIs focus on creation without reciprocal destruction mechanisms. They assume that cleanup happens when the "web page" is closed.

A long running, wanting-to-be-robust, web application might like to unload elements if it knows they are not being used on the page any more (f.e. it switched to another client-side route). It may like to lose references to classes, and can fetch the relevant JS (cached) later.

Would it make sense to be able to unregister elements, to free up memory?

@trusktr trusktr changed the title How to clean up (undefine) custom elements? Cleaning up (undefining) custom elements? Jul 6, 2018
@domenic
Copy link
Collaborator

domenic commented Jul 6, 2018

It may. What we'd want to see before committing to this sort of work is memory profiles of popular custom element-using sites, showing that a large portion of their memory usage comes from no-longer-used custom element definitions. Are you interested in collecting such evidence?

@trusktr
Copy link
Contributor Author

trusktr commented Jul 9, 2018

I'm interested, but not sure how to find the sites. They should have many client-side routes that load route-specific JS which includes new elements for the route. At some point my own site will do that.

@caridy
Copy link

caridy commented Jul 9, 2018

In my experience, with one of the heaviest components ecosystems that I know of, the component's registry is never the bottle neck, not even close. On top of that, this seems like a lot of work for so little reward. What if the component itself kicks in some services that will eventually attempt to create instances of itself? Who is in charge?

@annevk
Copy link
Collaborator

annevk commented Mar 16, 2019

Closing, happy to reopen once there's a clearer demonstration this can be useful.

@annevk annevk closed this as completed Mar 16, 2019
@mbostock
Copy link

The fact that custom elements can’t be redefined essentially makes them unusable within Observable’s reactive notebook environment. Our runtime is predicated on being able to re-evaluate cells whenever either the code in a cell changes, or a value it references changes. So, you can’t really use custom elements within Observable, because every time you change the code, you can’t cleanup the old definition before you re-evaluate the cell.

@stevenvachon
Copy link

stevenvachon commented Nov 19, 2019

Redefining custom elements is also useful for:

  • Hot Module Replacement (HMR).
  • Unit testing without page refreshes.

@mattsteve
Copy link

mattsteve commented Nov 21, 2019

@annevk Please reopen. It's silly to have an irreversible process.

@annevk
Copy link
Collaborator

annevk commented Nov 21, 2019

I think #754 (comment) clearly states what is needed, no?

@stevenvachon
Copy link

@annevk I think that comment is less relevant now, given the later comments.

@rniwa
Copy link
Collaborator

rniwa commented Nov 22, 2019

Wouldn't most of use cases here be addressed by #716?

@mattsteve
Copy link

@annevk Look at mbostock and stevenvachon's comments. Our concern isn't about freeing up memory, it's about the ability to replace a custom element after it's already been defined.

@mattsteve
Copy link

And think realistically about what actually happens on a website. Many websites use vendor Javascript and sometimes vendors do things that collide with the main functionality of the page. In enterprises, a developer may not have a choice to use a different vendor as it is decided by the business. Having the ability to work around misguided code is invaluable for Javascript.

@stevenvachon
Copy link

stevenvachon commented Nov 22, 2019

@rniwa Scoped custom elements are a partial solution, but still not when testing the default registry.

@rniwa
Copy link
Collaborator

rniwa commented Nov 23, 2019

@rniwa Scoped custom elements are a partial solution, but still not when testing the default registry.

What you'd do in that situation is to override what window.customElements does.

@stevenvachon
Copy link

@rniwa then my custom element would be atonomous.

@bathos
Copy link

bathos commented Nov 23, 2019

What would happen to an object representing a custom element instance which has become ‘undefined’? It presumably has the prototype associated with the custom element and likely has local state via own properties, private fields, or WM privacy. These things can’t be removed after the fact (the object may not even be extensible). Even if the object representing the node could be replaced somehow in the DOM, references to the original object may exist in closures. This doesn’t seem coherent to me.

@rniwa
Copy link
Collaborator

rniwa commented Nov 23, 2019

In addition to defining & collecting compelling use cases, here are some of additional issues / edge cases that need to be sorted out off the top of my head:

  • What happens to exiting elements' prototype & its methods.
  • What happens when someone clones an existing element, which is now "undefined".
  • What happens when when a custom element reaction callback or upgrading a custom element (inadvertently) undefines itself before draining some of items in custom element reaction queue.

I'm sure there is a lot more cases to consider that I haven't even thought about.

@mbostock
Copy link

What would happen to an object representing a custom element instance which has become ‘undefined’?

How about not changing anything for these existing instances, in the same way that if you call document.createElement("my-element") before customElements.define, you still have a generic HTMLElement after customElements.define, and customElements.define only affects newly created instances. (Apologies if this is a bad idea; I’m not especially familiar with the specification and I’m just trying to be help!)

@bathos
Copy link

bathos commented Nov 23, 2019

I think you’re on the right track that it would need to behave something like that @mbostock. I.E. what’s being disassociated in the registry is ‘this tag name points at this definition’, like setting the name and local name parts of the definition to null. I think that would imply in turn that new OriginalConstructor would need to throw.

I think this would create the possible scenario of multiple elements with the same tag name occurring with two or more non-unknown meanings in the DOM. I don’t think that can currently happen with autonomous elements and am unsure what implications it might have.

@trusktr
Copy link
Contributor Author

trusktr commented Dec 6, 2019

I think that would imply in turn that new OriginalConstructor would need to throw.

That's a given; there totally needs to be behavior like that. 👍

This downgrade process may be complicated, but ability to clean up memory usage is a critical part of designing any API that creates memory usage.

Even if we (any one of us) haven't seen a use case where memory growth from defining new elements is a specific problem, just the act of designing any re-usable and repetitive API with irreversible memory growth is simply bad practice that should be avoided.

"Downgrading" elements has intricacies to think about, but it's probably something important to think about for the longevity of web-tech beyond simple short-running applications.

Hypothetical Scenario 1, Users with many windows and tabs:

Imagine everyone drops React/Angular/Vue for plain Web Components. Now imagine a user that has 30 Chrome windows open, with let's say an average of 10 tabs per window (I've seen worse than this in practice).

Now imagine that after loading custom elements in many tabs, and switching routes (etc), on average each web app in every tab uses 300 custom elements over the app life time, and on average only needs 20 custom elements at any moment.

Without disposing any custom elements, and with current state of APIs (no cleanup of definitions), this means we'll load 90,000 Custom Element definitions.

Now, if there was a way to clean things up, this number would be reduced to 6,000 loaded at any point. That's a lot better, and could improve user experience when switching tabs often, especially on mobile devices.

Hypothetical Scenario 2: Web-based OS

A web-based OS-like system that loads "micro apps" by loading Custom Elements in a shared DOM. Over time, memory growth from using (installing) many apps will grow and never shrink.

Hypothetical Scenario 3: low-power low-cost embedded devices

We need to squeeze things into small amounts of memory. Easy to imagine problems here...

Hypothetical Scenario 4: Long running game

Like Half-life, this game might be an infinite world game, and we'd need to unload the components as we leave various areas of the game. All the cool kids like efficient game engines and micro-optimizations that eventually add up.

Yeah, let's make FPS's with HTML!

Hypothetical Scenario 4.5: "Infinite-world" game

Similar to the previous idea, in an infinite world game where users can travel to many places and cover large amounts distances with changing scenery, landscapes, characters, and items, you can imagine how leaking every definition of every scene, landscape, character, and item could be a problem.

Yeah, let's make infinite-world games with HTML!

Hypothetical Scenario 5: Testing

It becomes is simpler to write tests when there's no naming conflicts.

Hypothetical Scenario 6: Live code editors

Replacing elements with new implementation on code changes. If we do something like add an incrementing number to the end of an element name, then the user CSS won't work unless you painstakingly write a system for that too, because selectors with tag names will break. And then it won't be "just CSS" anymore.

Hypothetical Scenario 7: Live code pushes/patches or HMR

Imagine an in-app purchase system upgrades some objects with 3rd-party extensions from an extension store. The objects reload (maybe they are covered with a loading icon for aesthetics), and now the objects have the new features.

@ptrot001
Copy link

Another use-case is with using micro-frontend architecture. We have a custom-elements library for visual components that is reused through many SPA. The library is integrated at build-time in the SPA, so each SPA can use a different version of the components. At runtime, the first spa that will load the custom-elements will define them and after that every other spa will use the custom-elements first registered even if they are incompatible.

In this context, I would like to be able to unregister all custom-elements when the micro-frontend switch from one SPA to another. It would be useful for :

  • memory efficiency
  • version control of the custom-elements running in the page

@justinfagnani
Copy link
Contributor

@ptrot001 I think Scoped CustomElementRegistry is going to be a better solution there. Once a registry and all associated shadow roots are unreachable, a browser should be able to clean up definitions mostly non-observably (modulo any potential restrictions on classes being definable in at most one registry at a time).

@KrishnaPG
Copy link

KrishnaPG commented Feb 19, 2021

Came across this while looking for a way to resolve the error:

Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': the name "..." has already been used with this registry

image

In an SPA components get loaded and unloaded all the time. It is very surprising that this CustomElementRegistry is a one way call with no way to undefine or unRegister a component when needed.

@rniwa
Copy link
Collaborator

rniwa commented Feb 19, 2021

I think the key is to define how scoped custom elements registry would work. Since we'd have to solve many of the issues raised for unregistering custom elements for scoped custom elements registry, I'd imagine we'd be able to tackle this issue better once that's done.

@KrishnaPG
Copy link

Meanwhile, please treat the CustomElementRegistry as a dictionary (name -> component pair) and allow those who want to remove an entry do them that with a simple API like undefine. If you want, you can later deprecate it.

@rniwa
Copy link
Collaborator

rniwa commented Feb 19, 2021

Meanwhile, please treat the CustomElementRegistry as a dictionary (name -> component pair) and allow those who want to remove an entry do them that with a simple API like undefine. If you want, you can later deprecate it.

It's not that simple. We need an answer to all the questions I raised and probably a lot more questions as we define that behavior.

@trusktr
Copy link
Contributor Author

trusktr commented Mar 8, 2021

Can we re-open this issue?

In my humble opinion, any API without cleanup methods should always have its cleanup issue remain open, regardless of how long it takes to find a solution, because it is simply best practice to always have cleanup in mind when making any API.

Having this issue closed discourages a good mindset that, in my opinion, every developer should have.

@rniwa:

  • What happens to existing elements' prototype & its methods.

Two ideas:

  1. The element gets removed from DOM, and a new non-upgraded element gets placed in its spot, all synchronously. Any parent element (f.e. another custom element using MutationObserver) or API using the child should be responsible to clean up when the element is removed.
  2. Downgrade; undo the prototype changes that upgrade did. But keep it simple: any own properties that were added would simply remain on the downgraded element.
    • Before downgrade, call the element's disconnectedCallback so it has a chance to clean up. Either that, or a new downgradeCallback, but perhaps that would be unnecessary. disconnectedCallback is effectively synonym for time to cleanup.

I think option 2 is a lot better. However I think an idea similar to childConnectedCallback or childDisconnectedCallback would be helpful for parent elements to clean certain things up. customElement.whenUndefined(...) could be helpful for this, though callbacks like childUpgradedCallback and childDowngradedCallback would be ergonomic and easy to use, leading to a better dev experience. Compare those callbacks with having to continually make calls to cE.whenDefined and cE.whenUndefined to continually have new promises in order to react to repeated defines and undefines: it would be messier.

  • What happens when someone clones an existing element, which is now "undefined".

If we go with option 2 above, this is easier. They simply still have the same reference, and the referenced element may have cleaned itself up.

Going with option 1 would be more complicated, and it would lead to the same problems people had before web components and before mutation observer: how would one clean up when an element is removed (or downgraded)?

cE.whenUndefined would be the tool to use in this case. But an option with better DX would be to allow people to observe downgrades by extending the DOM Mutation events to also have upgrade and downgrade events, and to also not deprecate that API but instead add a batching mechanism to it to prevent the performance concerns. Mutation events are highly beneficial for developer experience when observing a particular element from the outside, as opposed to MutationObserver which is better for broad whole-tree observation or adding new callbacks to CEs.

Another DX improvement (in my opinion) would be to add lifecycle callbacks to Element.prototype which would allow users to monkey patch the methods for certain elements or for all elements. This would align built-ins with the Extensible Web Manifesto better too.

  • What happens when when a custom element reaction callback or upgrading a custom element (inadvertently) undefines itself before draining some of items in custom element reaction queue.

If the element undefines itself (for whatever reason),

  • two options for existing callbacks:
    1. cancel any attribute, adopted, or connected callback (remove them from queue or whatever),
    2. or just run them first before finally
  • running disconnected callback (if it is already queued, just leave it).
  • ensure downgrade happens right after disconnectedCallback

This would work the same (or with little difference) regardless of options 1 and 2 above. But still i think this makes more sense with option 2.

I'm sure there is a lot more cases to consider that I haven't even thought about.

Got any more? The above seems fairly straight forward: downgrading is just a simple object manipulation just like upgrading is (going with option 2, which seems better):

  • in the upgrade case, prototypes are injected, constructor runs on the instance (yay JavaScript magic), and finally connectedCallback runs. Note that the upgraded element may have had existing own properties added to it before upgrade.
  • in the downgrade case, disconnectedCallback runs, then prototypes are removed. Note that, reciprocally to upgrade, the downgraded instance may have remaining own props still present, which is totally fine. A future upgrade could re-use those again, for example, similar to how the very first upgrade may encounter existing own props and would need to deal with them.

Wdyt?

@getflourish
Copy link

I’ve been able to re-define custom elements at runtime (so that I can change the constructor and also upgrade previously existing instances with the new constructor). Of course it’s a hack but I’ll try to follow up with an explainer.

@sashafirsov
Copy link

Once the CustomElementRegistry is a method of DOMElement instead of window, most of the issues are gone, along with element in dom subtree :)

@rniwa
Copy link
Collaborator

rniwa commented Apr 1, 2022

As I have previously stated, there are a number of questions that need to be answered for this to work that will be resolved once the scoped custom element registry is well defined.

So folks who are interested in this cleanup capability should also focus on the scoped custom element registry since having that feature not only addresses most of important use cases raised in this issue, having a concrete of scoped registry would pave a way to define how this cleanup feature will work.

@imdavidmin
Copy link

As I have previously stated, there are a number of questions that need to be answered for this to work that will be resolved once the scoped custom element registry is well defined.

So folks who are interested in this cleanup capability should also focus on the scoped custom element registry since having that feature not only addresses most of important use cases raised in this issue, having a concrete of scoped registry would pave a way to define how this cleanup feature will work.

I don't think scoping resolves cleaning the use case I mentioned above, as Section A can be removed from page then later added again. Presumably the scope would have been the same unless the page itself implemented some scope naming rules. Otherwise, there will still be an error redeclaring the same component.

RE your edge cases, I'd propose something like dismount(true) to unregister when there are still modes of the custom element in place, or dismount() when that element isn't used. Error is returned when 'dismount()' is called when that element is still in DOM. When a dismounted element is still in DOM, it should be treated as a custom element that had never been registered. All internal states and methods should be lost.

All this though is something I think we should only work on after the issue is reopened, and @annevk wants us to demonstrate use case first.

@caridy
Copy link

caridy commented Apr 5, 2022

@imdavidmin @getflourish if you're looking for hot module reload, testing, etc, you can still do all that in user-land, here is a POC of that for you to test:
https://github.com/caridy/redefine-custom-elements

@imdavidmin
Copy link

imdavidmin commented Apr 5, 2022

@imdavidmin @getflourish if you're looking for hot module reload, testing, etc, you can still do all that in user-land, here is a POC of that for you to test:
https://github.com/caridy/redefine-custom-elements

Respectfully, it is not difficult to work something out in Javascript or with a library. The issue at hand here is what I believe to be a design oversight in Web standard specs. I already am working around the issue but just not very elegant.

@rniwa
Copy link
Collaborator

rniwa commented Apr 6, 2022

@imdavidmin @getflourish if you're looking for hot module reload, testing, etc, you can still do all that in user-land, here is a POC of that for you to test:
https://github.com/caridy/redefine-custom-elements

Respectfully, it is not difficult to work something out in Javascript or with a library. The issue at hand here is what I believe to be a design oversight in Web standard specs. I already am working around the issue but just not very elegant.

It wasn't really an oversight. We specifically designed so that custom element can't be redefined since allowing re-definition raises all sorts of design questions we weren't comfortable answering at the time. If you're interested in this issue getting any traction, as I have stated numerous times earlier in the thread, please follow up with the discussion of scoped custom element registry.

@imdavidmin
Copy link

@imdavidmin @getflourish if you're looking for hot module reload, testing, etc, you can still do all that in user-land, here is a POC of that for you to test:
https://github.com/caridy/redefine-custom-elements

Respectfully, it is not difficult to work something out in Javascript or with a library. The issue at hand here is what I believe to be a design oversight in Web standard specs. I already am working around the issue but just not very elegant.

It wasn't really an oversight. We specifically designed so that custom element can't be redefined since allowing re-definition raises all sorts of design questions we weren't comfortable answering at the time. If you're interested in this issue getting any traction, as I have stated numerous times earlier in the thread, please follow up with the discussion of scoped custom element registry.

By no means I'm saying the current spec is done haphazardly, I'm sure a lot more deliberation than myself thinking have gone into it. Regarding scoped registry, I've already answered why I think that doesn't resolve the problem completely, in the case of re-loading the same contents.

@rniwa
Copy link
Collaborator

rniwa commented Apr 6, 2022

It wasn't really an oversight. We specifically designed so that custom element can't be redefined since allowing re-definition raises all sorts of design questions we weren't comfortable answering at the time. If you're interested in this issue getting any traction, as I have stated numerous times earlier in the thread, please follow up with the discussion of scoped custom element registry.

By no means I'm saying the current spec is done haphazardly, I'm sure a lot more deliberation than myself thinking have gone into it. Regarding scoped registry, I've already answered why I think that doesn't resolve the problem completely, in the case of re-loading the same contents.

Sigh... this is the third time I'm having to explain this. Adding the capability to re-define custom elements or delete the definition, we need to answer some questions I had previously stated. As I have previously stated, figuring out how scoped custom element registry would answer most of those questions. So, if we have figured out how to implement scoped custom element registry in the browsers, it would pave a way to add the ability to clean up custom element definitions in the global scope as well. So the step 1 of this feature is to work on the scoped custom element registry.

@leobalter
Copy link

@rniwa I see there is a tentative answer at #754 (comment) with no feedback.

I just want to understand the next steps to work here. Thanks!

@bathos
Copy link

bathos commented Apr 7, 2022

in the downgrade case, disconnectedCallback runs, then prototypes are removed. Note that, reciprocally to upgrade, the downgraded instance may have remaining own props still present, which is totally fine. A future upgrade could re-use those again, for example, similar to how the very first upgrade may encounter existing own props and would need to deal with them.

Part of the trouble here is that JS itself has no generic concept of "object teardown" to build off of, so there can be no answer involving "transitioning" the original object that is not at best idiosyncratic and at worst incompatible with core language features like private fields (which would throw on attempted reallocation if the definition were added back).

@caridy
Copy link

caridy commented Apr 7, 2022

All the use-cases stated on this thread are targeting development time. I'm very sympathetic with that, and I have seen this first hand, that's why I wrote that little library, but it is my opinion that such feature can wait, you have workarounds, and we are in the process of getting scoped registries. Once we gather a lot more info, and see how developers -- specially big libraries authors -- respond to scoped registries, we can revisit this decision, to see if this is truly needed.

@elmarsto
Copy link

elmarsto commented Jul 6, 2022

Here's another dev-related use case:

  • I'm building a plugin for an Electron-based app.
  • I can't use React, so I thought I'd try web components
  • Every time the plugin is reloaded (during development), it throws an exception, as the previous definition still exists
  • This means that, instead of HMR, I have to close and reopen the entire app, which, yes, happens to be an entire web browser.

yes, this is 'just' a development-related concern.

however, as I like to say, "DX is UX". The experience of your developers just is a user experience, as developers are also users.

And DX, like any other UX, strongly determines adoption.

All I need to be happy here is the ability to mutate an entry in the registry -- no 'unloading' logic, just good ol' idempotence.

But because I can't get it, I think I'm stuck with CSS classes.

Web components, we had a good 7 minute run

@caridy
Copy link

caridy commented Jul 15, 2022

@elmarsto just use a tool to allow you to do HMR during development, that's the basic workaround that I mentioned above, here is one: https://github.com/caridy/redefine-custom-elements

@sashafirsov
Copy link

sashafirsov commented Nov 4, 2022

@rniwa ,

Sigh... this is the third time I'm having to explain this. Adding the capability to re-define custom elements or delete the definition, we need to #754 (comment). As I have #754 (comment), figuring out how scoped custom element registry would answer most of those questions.

The software is soft enough that even craziest things could be done. Just a question of effort. If the polyfiull with good enough performance can be done, it means in native browser it would be done even more efficient. It is not an argument that "difficult to implement" to ban the standard proposal. The proposal can be put on hold with query to provide PR to one of OSS browsers
as a proof. Not closed. It is inappropriate that whole range of "must have" patterns been shut down that way, many by @rniwa you personally.

The unregistering custom element is a part of facade pattern which is quite useful in AOP approach of incremental features associations on the component. Those are (but not limited to)

  • semver support in runtime,
  • live app upgrade
  • translation
  • analytics
  • ...

@annevk , would this argument be sufficient to reopen proposal?

Note none of the samples are specific to development environment. Those needed in prod. Of course there are work around. And even unregistering can be polyfilled as of now. The question is to keep debate on why needed and how to implement better. Not how to dump brilliant ideas.

@caridy
Copy link

caridy commented Nov 4, 2022

@sashafirsov most of the time it is not really about whether we can do it or not, or whether it can be polyfilled or not... most of the time it is about the unknowns, and whether or not we will put ourself is a corner if we implement a particular feature with respect to future optimizations, or whether or not we will probably hinder on some other more prominent future idea.

For this particular case, as a creator of one of the polyfills (https://github.com/caridy/redefine-custom-elements), and as a participant on many of the discussions about this on the various groups with implementers, I just don't think we should implement this ATM in the UA, we can wait and see.

@sashafirsov
Copy link

sashafirsov commented Nov 7, 2022

@caridy , @annevk, could you, please, elaborate on the criteria of reopening of proposal?

Is it contest of polyfill popularity, upvotes on the issue, or something else?

I see the chicken and egg problem. Without proper proposal, even polyfill would not be created. Without implementation or polyfill there is no way to track the uses or potential use cases.

Also the API on any kind of object in browser should be functionally complete: create-attach/request-list/watch/remove-replace. Perhaps I am missing some more on the generic entity life cycle.

If the API have been created with one leg, it shall be treated as insufficient.

@johngamble
Copy link

johngamble commented Nov 20, 2022

Here is a demonstration of why it would be useful:

https://stackoverflow.com/questions/73849002/unit-tests-of-angular-elements-registered-with-the-customelementregistry-as-a-h

There are countless other demonstrations of the usefulness of undefining custom elements which have been posted by others in this ticket.

@annevk please re-open this ticket.

If there is anything else that you feel is needed in order to re-open the ticket then please let us know specifically what that is and in what form it's required. Or perhaps re-open it anyway so that what is required can be openly discussed.

@MattiasMartens
Copy link

use case where this is helpful: if i download two dependencies that both try to define the same custom element (possibly because they each separately invoke that dependency), and those dependencies don't check if the element is already defined, i could use "undefine" to prevent the second one from failing.

the current state of play is that i can do nothing about this.

@Giwayume
Copy link

the current state of play is that i can do nothing about this.

I wouldn't say there's nothing you can do. Not that it is exactly desirable to do this, but:

<script>
	// Block certain custom elements from being defined.
	const defineCustomElement = CustomElementRegistry.prototype.define;
	CustomElementRegistry.prototype.define = function define(name, constructor, options) {
		if (name == 'element-you-do-not-want-defined') {
			return;
		}
		return defineCustomElement.call(this, name, constructor, options);
	};
</script>

<script src="library-you-want-limit-custom-definitions-from.js"></script>

<script>
	// Unblock
	CustomElementRegistry.prototype.define = defineCustomElement;
</script>

<script src="this-library-should-work-as-normal.js"></script>

@kevinclarkadstech
Copy link

This whole issue is so infuriating it is pretty much pushing me away from custom elements and back to React. There are reasons to undefine an element, definitely for hot module reload which I am trying to implement with lit and vite. Some of the commenters on here 🤡

@johngamble

This comment was marked as spam.

@andyearnshaw
Copy link

@MattiasMartens

use case where this is helpful: if i download two dependencies that both try to define the same custom element (possibly because they each separately invoke that dependency), and those dependencies don't check if the element is already defined, i could use "undefine" to prevent the second one from failing.

Libraries should not be automatically registering their custom elements in this way without providing a non-automatic route too. While it can be useful to just include the script in a plug and play manner, it is too rigid and, if you want to extend or customise the element before registration, the registered element may never even be used. I'd also find it strange for a library to register custom elements from its dependencies or allow its dependencies to register custom elements without any attempt at scoping or isolation, that's already a recipe for problems. It's akin to using a fixed global variable to store information or defining a global function as the main entrypoint for your library (a problem that modules were meant to solve).

I try to ensure the output for my custom element libraries is either the custom element class, which the page author can register themselves, or a function that will register my custom elements and any dependency custom elements under a provided tag name/prefix. Even if the author has not provided an official approach to importing the element without registration, you might be able to import it from the dependency source code directly:

// Automatically defines <awesome-widget>
// import AwesomeWidgetElement from 'awesome-widget';
import AwesomeWidgetElement from 'awesome-widget/src/AwesomeWidgetElement.js';

customElements.define('my-awesome-widget', AwesomeWidgetElement);

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