Eva Lauren Kelly
Eva Lauren Kelly

Reputation: 413

Cannot access attributes of a custom element from its constructor

I'm trying to create a polyfill of sorts using the Custom Elements API for custom elements used by an in-game browser engine to display buttons and similar. However, I can't seem to access the element's attributes (eg. src, href ...) from within the constructor.

Here is an example:

class KWButton extends HTMLElement {
  constructor() {
    super();
    var attributes = this.attributes;
    var shadow = this.attachShadow({
      mode: 'open'
    });
    var img = document.createElement('img');
    img.alt = this.getAttribute('text'); // the getAttribute call returns null
    img.src = this.getAttribute('src'); // as does this one
    img.width = this.getAttribute('width'); // and this
    img.height = this.getAttribute('height'); // and this
    img.className = 'vivacity-kwbutton'; // this works fine
    shadow.appendChild(img);
    img.addEventListener('click', () => {
      window.location = this.getAttribute('href'); // this works perfectly fine
    });
  }
}
customElements.define('kw-button',
  KWButton);
<kw-button src="https://placekitten.com/g/198/39" width="198" height="39" icon_src="https://placekitten.com/g/34/32" icon_width="34" icon_height="32" href="https://placekitten.com/" text="placekiten" color="#ffffff" size="18"></kw-button>

Upvotes: 9

Views: 4279

Answers (2)

GullerYA
GullerYA

Reputation: 1776

Update as of 12me21 correction:

  • if the element was registered / defined before being parsed in HTML, the attributes won't be available
  • if the order of those is otherwise (parsed HTML as unknown, then define / upgrade) - they will

All in all I'd say now that since this is indeterminate to rely on this, well, exactly as it is warned by spec, I'd definitely refrain from accessing anything in the custom element c~tor, beside class' own definitions and alike.


Although I swear I've seen that spec once (the one @Supersharp mentioned), but nowadays:

  • inspection of the attributes is allowed (works for me on Chrome, Firefox and Safari), so getAttribute is OKAY
  • mutation of the attributes is, as expected, forbidden

Well, probably we indeed should read that 'gain' as specifically referring to mutation.

One could say - wait, but if element can't gain any attribute - obviously there is nothing to inspect. Well, the following snippet works for me (on any browser):

class A extends HTMLElement {
  constructor() {
    super();
    console.log(this.getAttribute('data-some'));
  }
}

globalThis.customElements.define('x-a', A);

const e = document.createElement('x-a');
// console: null

const t = document.createElement('div');
t.innerHTML = '<x-a data-some="test"></x-a>';
// console: test

CodePen

Upvotes: -1

Supersharp
Supersharp

Reputation: 31181

You cannot access the element DOM tree with querySelector() and appendChild(), and attributes with getAttribute() and setAttribute() in the constructor().

It's because at the time constructor() is called the custom element has no content yet.

You should defer that in the connectedCallback() method and it will be fine.

From the specs:

The element must not gain any attributes or children, as this violates the expectations of consumers who use the createElement or createElementNS methods.

In general, work should be deferred to connectedCallback as much as possible

Upvotes: 20

Related Questions