dom icon indicating copy to clipboard operation
dom copied to clipboard

Custom element callback/promise for "connected and parsed"

Open jakearchibald opened this issue 7 years ago • 11 comments

Developers seem to assume that connectedCallback means the element's contents will be present. This isn't helped by browser differences (demo). Safari & Firefox get it wrong and show some element contents in connectedCallback depending on loading speed. Chrome appears to get it right.

However, consider an element like <video>. Because <video>'s contents influence what should be loaded, the content shouldn't be considered until the parser has stopped adding elements into the <video>, else you can end up with pointless double fetching.

Do we need something similar for custom elements? This could be an additional callback, or perhaps even htmlElement.parsed, which is a promise that resolves once the element has fully parsed. This would be useful in other situations too, as document.documentElement.parsed would be similar to DOM-ready.

jakearchibald avatar Jun 29 '18 10:06 jakearchibald

Previously: https://github.com/w3c/webcomponents/issues/619, https://github.com/w3c/webcomponents/issues/550.

Video/source does not appear to work in the way you describe:

If a source element is inserted as a child of a media element that has no src attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke the media element's resource selection algorithm.

There are a couple elements which do work this way. You can find them by searching for things that trigger when the element is popped off the stack of open elements. It appears that list consists of:

  • object
  • video/audio but only with respect to text tracks
  • textarea
  • style
  • script elements, but via a different mechanism (more hard-coded into the parser)

I'm not sure it's worth spreading this pattern further, as opposed to just saying it's a legacy pattern. The most compelling examples you might want to emulate are style/script (i.e., don't apply styles/execute script until all of the text content is present). But do we see a lot of people trying to do the style/script case? My impression is it's mostly been people who are unwilling to put in the work of making their element properly reactive to descendant changes, i.e. they want to use children as configuration for initial setup, and not as part of a dynamic tree.

domenic avatar Jun 29 '18 15:06 domenic

Video/source does not appear to work in the way you describe

Yeah, that feels like a bug as it makes the fetching nondeterministic.

But do we see a lot of people trying to do the style/script case?

I guess you would if your web component was interpreting the input. Eg a babel/jsx component. A responsive image web component would likely want the same pattern, assuming we didn't already have responsive images.

I don't feel strongly about this, so I'll leave it for others to add use cases.

jakearchibald avatar Jun 29 '18 16:06 jakearchibald

Although, I guess <video> and <picture> do work sequentially. Ignore me in terms of that. I guess it's only the interpreting case that's valid.

jakearchibald avatar Jun 29 '18 16:06 jakearchibald

I agree with @domenic here. It will open the gates for a lot of wrong-doing. As for browser differences, do we have tickets for safari and firefox to correct that behavior?

caridy avatar Jun 29 '18 16:06 caridy

Not yet. Sorry it took me a while to confirm the correct behaviour. I'll file bugs. It's covered by tests.

On Fri, 29 Jun 2018, 17:30 Caridy Patiño, [email protected] wrote:

I agree with @domenic https://github.com/domenic here. It will open the gates for a lot of wrong-doing. As for browser differences, do we have tickets for safari and firefox to correct that behavior?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/whatwg/dom/issues/662#issuecomment-401406648, or mute the thread https://github.com/notifications/unsubscribe-auth/AAFtmvUS2cIIafwPnuL76hO00B9sepv9ks5uBlY_gaJpZM4U8yZx .

jakearchibald avatar Jun 29 '18 17:06 jakearchibald

I've definitely wanted to have behaviour similar to this issue for simple inline code. e.g. For this sort've use case:

<math-plot>
  <module-script>
    export default function(x) {
      return x**2
    }
  </module-script>
</math-plot>

In this case it's the children of the module-script I would like to ensure only execute once (which can be important for use cases that fetch expensive resources).

I tried to create a customized builtin <script> element in Chrome Canary to simulate the behaviour but still the connectedCallback is executed too early.




On the other hand this particular problem could be solved by ensuring that the customElements.define call always happens after parsing has completed.

e.g. Just defer the customElements.define call until DOMContentLoaded:

class ScriptLike extends HTMLElement {
  // ...
}

if (document.readyState === 'interactive'
|| document.readyState === 'complete') {
  customElements.define('script-like', ScriptLike)
} else {
  document.addEventListener('DOMContentLoaded', _ => {
    customElements.define('script-like', ScriptLike)
  })
}

Jamesernator avatar Jul 11 '18 06:07 Jamesernator

Help is on the way: https://github.com/WebReflection/html-parsed-element

franktopel avatar Oct 23 '18 13:10 franktopel

@Jamesernator

Just defer the customElements.define call until DOMContentLoaded:

Normally developers just add customElements.define() after the class declaration of the custom element. The code of deferring it is a little bit tricky. Are you suggesting customElements.deferDefine? 😝

hax avatar Nov 09 '18 08:11 hax

@Jamesernator

Just defer the customElements.define call until DOMContentLoaded

Imo that's a bad idea for at least two reasons:

a) it won't solve problems related to dynamic creation/updating of elements via e.g. morphdom, and, even more important, b) it will disallow for people using your components to register their own listeners/ do whatever stuff with your components which they'd usually do on DOMContentLoaded.

That would introduce a mess of customElements.whenDefined() callbacks (where all the consumer of the components usually wants is a webcomponentsReady() callback mechanism).

franktopel avatar Nov 09 '18 09:11 franktopel

Filed https://github.com/w3c/webcomponents/issues/809 to track this in web components repository.

rniwa avatar Apr 30 '19 05:04 rniwa

I don't feel strongly about this, so I'll leave it for others to add use cases.

I can help with that. 😄 One good use case might be in my Svelte custom element polyfill, svelte-retag. Essentially, it was built to support slots in the light DOM, among other things (one of its core differentiating features from Svelte's existing custom elements).

Doing this well is of course a bit tricky if you're balancing performance concerns. Funny enough, my workaround ended up looking a bit like, uh... a light "shadow" DOM of sorts... in order to efficiently get out of the way of the parser to facilitate rendering ASAP (at least on the next rAF). Demo here. I also wrote in some detail about it if you're at all interested.

patricknelson avatar Dec 09 '23 06:12 patricknelson