-
Notifications
You must be signed in to change notification settings - Fork 375
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
Preventing re-renders prior to first call to connectedCallback()
#1081
Comments
Might be related to #809. |
Yes, Attributes (may) drive UI (or other stuff your Element does) so they need to initialize before your Element presents itself in the DOM Then, From MDN isConnected:
It does NOT mean the Maybe Can be demonstrated with 2 Custom Element instances in a HTML document: <my-element id="ONE"> lightDOM </my-element>
<script>
customElements.define( "my-element", class extends HTMLElement {
constructor() {
super()
console.log("constructor", this.id, this.isConnected, this.innerHTML.length)
}
connectedCallback() {
console.log("connectedCallback", this.id, this.isConnected, this.innerHTML.length)
}
},
)
</script>
<my-element id="TWO"> lightDOM </my-element>
Note on #809: the Some developers will say to always do Did connectedCallback ran? Yes, that means you have to add your own semaphore for the Instead of:
I sometimes hack it like this:
That way I don't need a semaphore in another method I later have to hunt for when refactoring OPs wishlist: Ideally, I would like one or both of:
"attributes initialized" could be a lot of work, There will probably be a mismatch between Observed attributes and declared attributes.
There is no "in between" HTH |
it's not just re-rendering though ... this.innerHTML = renderFunction.render(this.attributes) this fails if your custom element declaration happens before the element is discovered/streamed in the DOM. this works if your element is already live, meaning your declaration happened after the DOM was already parsed. The latter point means you are using modules to define custom elements and modules play (as side-effect) better because they exec on This is the reason we've been asking a lot a way to have the possibility to operate on Custom Elements once their end tag is either reached or implicitly enforced by the parser, but the issue here is pretty normality from a parser point of view: <custom-element attr="1">
Maybe content
</custom-element> A parser would find the node, which is already connected, and it will start parsing Then it passes to its childNodes, if any, and right before that, it will trigger
These are the basics behind Custom Elements to know though, otherwise much more issues could be found in the "shipping". TL;DR is any callback except for P.S. if you use |
Thank you everyone for the in-depth explanations. I need to do some experimentation to see if I'll be affected by some of these cases due to the specific way I'm creating custom elements. For background, I am building a templating library that allows mixing JS and HTML to create custom elements similar to what you see in back-end template libraries like Ruby's ERB, and ASP.NETs Razor where native code is mixed with HTML. I'm following in the vein of no-build like HTMX, but with a more client-side focus and using actual JS as the script for interactivity. Effectively, I'm creating a declarative way of writing custom elements. Something that can't currently be done with An example I have working is: <html>
<head>
<script type="module" src="../jst.js"></script>
</head>
<script type="jst" name="jst-counter" count :increment>
$ const clicks = count == 1 ? 'click' : 'clicks'
<button onmousedown="$increment">$(count || 0) $clicks</button>
</script>
<body>
<jst-counter count="5" :increment="incrementCounter(this)"></jst-counter>
<script>
function incrementCounter(el) {
++el.attributes.count.value
}
</script>
</body> Ideally, the user shouldn't have to use I next want to implement slots, however, based on the feedback from @Danny-Engelman and @WebReflection, I now suspect I will encounter issues with this: <html>
<head>
<script type="module" src="../jst.js"></script>
</head>
<script type="jst" name="jst-condition" condition>
$ if (condition) {
<slot name="true"></slot>
$ } else {
<slot name="false"></slot>
$ }
</script>
<body>
<script>
const someVar = true;
</script>
<jst-condition condition="someVar">
<span slot="true">someVar was true</span>
<span slot="false">someVar was false</span>
</jst-condition>
</body> The user should be able to do anything custom elements can, like extend other classes, and the myriad of other custom element features that exist: <script type="jst" name="jst-checkbox" extend="HTMLInputElement" extend-type="HTMLInputElement">
...content...
</script> |
@br3nt you can do conditional DOM with So instead of: <script type="jst" name="jst-condition" condition>
$ if (condition) {
<slot name="true"></slot>
$ } else {
<slot name="false"></slot>
$ }
</script> Try: <template type="jst" name="jst-condition" condition>
<template type="if">
<slot name="true"></slot>
</template>
<template type="else">
<slot name="false"></slot>
</template>
</script> (using whatever attribute names you want to signify the specific functionality of each My project Heximal (declarative HTML templates, custom elements, variables, and more) uses this technique: https://github.com/elematic/heximal/?tab=readme-ov-file#features I'm happy to talk more about this on the WCCG Discord or more on-topic issues here :) |
Thanks @justinfagnani for taking an interest. In this case, I want to stick with mixing JS and HTML. The problem I foresee isn't the template parsing, as I have that worked out. I will encounter the specific problem mentioned by @WebReflection regarding the inability to assign to So I'm a plus one for:
Having a callback and hopefully a flag to validate I can assign to I'm concerned the @justinfagnani, I'd like to reach out, because looking briefly at your library, I think we will encounter many of the same problems you may have already tackled. (edit... hmmm cant find the discord lol) |
In my custom components with observable attributes, I've noticed that
attributeChangedCallback()
is called multiple times beforeconnectedCallback()
is called. Even thoughconnectedCallback()
hasn't been called yet, the value ofisConnected
in the earlierattributeChangedCallback()
calls is true. This occurs when a custom element is defined directly on the HTML page.The problem with this is there is no flag I can check to prevent unnecessarily re-rendering the element content before all attributes are initialised.
E.g.:
The example component may look like this:
In this example,
attributeChangedCallback()
is called three times, andthis.isConnected
istrue
each time. Finally,connectedCallback()
is called.This is problematic because I want to prevent unnecessary (re-)rendering of the element content until all the attributes defined in the HTML have been initialised due to the expense of computing the content for the element.
Now, I can manually add a flag and set it to true when
connectedCallback()
and false whendisconnectedCallback()
is called. But I'm pretty sure every developer using web components would want/need this flag, so it makes sense for this to be part of the web component specification.Ideally, I would like one or both of:
false
before the call toconnectedCallback()
andtrue
after the initial calls toattributeChangedCallback()
for each of the defined attributes.attributesInitialised
orattributesInitialized
attributeChangedCallback()
and the call toconnectedCallback()
attributesInitialisedCallback
orattributesInitializedCallback
The text was updated successfully, but these errors were encountered: