0

If I use a custom element in HTML the provided attributes are all available in the constructor

<my-element myparam="fruit">I like bananas</my-element>

constructor() {}
    super()
    this.param = this.getAttribute("myparam")

> this.param = "fruit"

But if I create an instance of my custom element in JS with document.createElement the constructor runs immediately, without the attributes being available.

const elem = document.createElement("my-element")

> this.param = null

Once I have appended the element to a DOM node I can hook attributeChangedCallback which fires when I call elem.setAttribute, to set this.param. But that seems kind of ugly. I feel as though you ought not to have to create extra methods just to make a component usable via JS as well as HTML. It seems like there's similar situation with textNodes / Children too.

Is there any way to make attributes and children available to the constructor when creating nodes programmatically?

edit: So it seems seeking to do this is bad practice and I should only try to access attributes in the connectedCallback() function. As long as I use setAttribute before adding the component with .appendChild the values should be available a that point.

Roger Heathcote
  • 3,091
  • 1
  • 33
  • 39
  • Is it called `my-element` or `my-component`? I doubt that's the issue though – evolutionxbox Jan 14 '22 at 16:23
  • This seems normal. When we create a link using `createElement('a')` it doesn't have a `href` property set (`""`). So we have to do that manually. Elements have no attributes when created programmatically. – evolutionxbox Jan 14 '22 at 16:29
  • Your example works because your ``constructor`` ran **after** the ```` DOM was parsed in the page. Never rely on this _edge-case_ behavior! to stay save **never** access DOM in the ``constructor`` (you can ofcourse access the **shadowDOM** of an element if it was created in the ``constructor``) – Danny '365CSI' Engelman Jan 14 '22 at 18:54
  • Also be aware for **observerdAttributes** the ``attributeChangedCallback`` runs before the ``connectedCallback``. See: https://stackoverflow.com/questions/70691243/cant-set-textcontent-of-a-customised-built-in-element-except-in-timeout – Danny '365CSI' Engelman Jan 14 '22 at 18:58

1 Answers1

2

The constructor runs before the element has attached to the DOM when you use document.createElement so you will not be able to access attributes as they don't exist yet.

You can access attributes in connectedCallback. The following basic example works

customElements.define(
  "my-el",
  class extends HTMLElement {
    constructor() {
      super();
    }
    
    connectedCallback() {
      this.foo = this.getAttribute("foo")
      console.log("foo", this.foo) // logs "bar"
    }
  }
);

const el = document.createElement("my-el");
el.setAttribute("foo", "bar");
document.body.append(el)

You have some typos: my-element vs. my-component, this.getAttribute("myparam") when you attribute above is called param.

Doug Hamlin
  • 996
  • 1
  • 11
  • 17