2

This is a conceptual question for those who understand custom elements without seeing concrete code examples.

I'm creating a custom element that's display depends on its textContent. In the custom element's constructor, if I try to access this.textContent it returns an empty string, even though the html of that custom element does indeed contain text.

In order to obtain the textContent during the custom element's construction, I enclosed my constructor code within a setTimeout and then was able to make my custom elements construction be base on its textContent.

However, this felt a little hacky and I figured there was a more proper way to obtain textContent during an element's construction. That's when I found Using the life Cycle Callbacks.

The connectedCallback method allowed me to see textContent without putting a setTimeout into the custom element's constructor. However, this quote concerns me:

connectedCallback: . . . may happen before the element's contents have been fully parsed.

This worries me that if I have a lot of content inside the custom element, this.textContent may still return an empty string because all that text may not yet be "fully parsed".

Is this worry justified? Is there a more sure way to obtain this.textContent as a basis for your custom element's construction? Or, should I go with my initial solution of putting a setTimeout within the custom element's constructor?

Lonnie Best
  • 9,936
  • 10
  • 57
  • 97
  • 1
    No need to hurry, 'aliens' have more advanced technology and master time travel, thus the whole concept of 'hurry' doesn't apply.... https://www.space.com/aliens-time-traveling-humans-ufo-hypothesis.html – Danny '365CSI' Engelman Oct 08 '20 at 12:59
  • @Danny'365CSI'Engelman Thanks, Danny. It seems you've already thought this through :) Do you have a link to an example of the concept you mentioned [here](https://github.com/w3c/webcomponents/issues/889#issuecomment-704930044)? If not, no worries; I'll try it out soon. – Lonnie Best Oct 09 '20 at 06:05
  • 1
    It is an HTML tag **_inside another_** HTML tag. If that is too complex I have to shield you from more errors... okay, okay... here you go: https://jsfiddle.net/CustomElementsExamples/Lhcsd2m5/ – Danny '365CSI' Engelman Oct 09 '20 at 14:59
  • Great, thanks! However, I'll warn you: because of your knowledge, I've added you to my list of suspected aliens (with Elon Musk). @Danny'365CSI'Engelman – Lonnie Best Oct 09 '20 at 15:33
  • 1
    We're not aliens, only alienated. Michael J. Fox was great in that movie; but once you go *back* you can't **go** back. We are *"guides"* from the future, now stuck in the same time-travel mode you are in. Well.. I am a *guide*. Elon is just a pot smoking idiot, who is doing some things right for a better future; so we chose not to *"re-direct"* him. – Danny '365CSI' Engelman Oct 09 '20 at 16:32
  • 1
    https://jsfiddle.net/CustomElementsExamples/Lhcsd2m5/ now has updated code... alas this is a drawback of regular *forward time travel*, enhancements are **never** published in the past. – Danny '365CSI' Engelman Oct 09 '20 at 18:23

1 Answers1

1

In your example textContent is DOM content

The constructor should not (try to) access DOM,
as it can be run from .createElement('your-element') when there is no DOM at all.
(or in a Server-Side-Rendering scenario)

connectedCallback runs before the whole DOM inside finished parsing. If you want to access its DOM content you have to wait till Element DOM is ready.

Only in FireFox you can access Element DOM content;
see: wait for Element Upgrade in connectedCallback: FireFox and Chromium differences

For detailed analysis see: https://jsfiddle.net/CustomElementsExamples/n20bwckt/
Note the difference when run in FireFox and Chrome.

setTimeout( func , 0 )

is totally valid (but better not in the constructor) and ensures your code runs when the EventLoop is empty, thus when all DOM is ready to be accessed. You could also use requestAnimationFrame.
All libraries do something similar under the hood to add updateComplete and the likes.
With bare-bones Custom Element API you have to write it yourself.

Note: The connectedCallback now triggers code that runs after a (potential) disconnectedCallback, so check for that with this.isConnected in your code.

For visual diagram when all callbacks run see:
https://andyogo.github.io/custom-element-reactions-diagram/

Note: if you don't specify any methods in your own Element, the methods from HTMLElement run.
So Elements without any declared constructor or connectedCallback are perfectly valid.
In IconMeister I only use attributeChangedCallback

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
  • [Roger that](https://www.dictionary.com/e/slang/roger-that/), [Danny](https://i.imgur.com/Lj398Xq.png)! Thanks for bringing all these considerations to my attention. – Lonnie Best Oct 02 '20 at 18:07
  • Does `attributeChangedCallback` fire during the initialization/modification of element content (such a `this.textContent` or `this.innerHTML`)? The name implies it would only fire if an HTML attribute has changed and I wouldn't expect that to fire during the initialization or modification of element content. – Lonnie Best Oct 02 '20 at 18:40
  • 1
    I updated [the JSFidlle](https://jsfiddle.net/CustomElementsExamples/n20bwckt/) It shows **observed**-attribute value _FIRST_ fires the ``attributeChangedCallback`` **BEFORE** the ``connectedCallback``. That is why most examples check for ``oldValue===null`` in the ``attributeChangedCallback`` – Danny '365CSI' Engelman Oct 03 '20 at 10:12