Skip to content Skip to sidebar Skip to footer

Attaching Shadow Dom To A Custom Element Removes Error, But Why?

Per the custom element specification, The element must not gain any attributes or children, as this violates the expectations of consumers who use the createElement or createEleme

Solution 1:

Every time an element is created it is done through the constructor. But, when the constructor is called there are no children nor any attributes, Those are all added AFTER the component is created.

Even if the element is defined in the HTML page, it is still created by code using the constructor and then the attributes and children are added by the code that is parsing the DOM in the HTML page.

When the constructor is called there are no children and you can not add them since the DOM parser may be adding them as soon as the constructor is finished. The same rule applies for the attributes.

Currently there is no way to specify shadowDOM or shadowDOM children except through JS code. The DOM parser will not add any children to the shadowDOM.

So according to the spec it is illegal to access, change or do anything with attributes or children in the constructor. But, since there is no way for the DOM parser to add anything into a components shadowDOM that is not illegal.

I have gotten around this problem when not using shadowDOM by using an internal template element that is created in the constructor and then placed as a child once the connectedCallback is called.

// Class for `<test-el>`classTestElextendsHTMLElement {
  constructor() {
    super();
    console.log('constructor');
    const template = document.createElement('template');
    template.innerHTML = '<div class="name"></div>';
    this.root = template.content;
    this.rendered = false;
  }

  staticgetobservedAttributes() {
    return ['name'];
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    if (oldVal !== newVal) {
      console.log('attributeChangedCallback', newVal);
      this.root.querySelector('.name').textContent = newVal;
    }
  }

  connectedCallback() {
    console.log('connectedCallback');
    if (!this.rendered) {
      this.rendered = true;
      this.appendChild(this.root);
      this.root = this;
    }
  }

  // `name` propertygetname() {
    returnthis.getAttribute('name');
  }
  setname(value) {
    console.log('set name', value);
    if (value == null) { // Check for null or undefinedthis.removeAttribute('name');
    }
    else {
      this.setAttribute('name', value)
    }
  }
}

// Define our web component
customElements.define('test-el', TestEl);

const moreEl = document.getElementById('more');
const testEl = document.getElementById('test');
setTimeout(() => {
testEl.name = "Mummy";
  const el = document.createElement('test-el');
  el.name = "Frank N Stein";
  moreEl.appendChild(el);
}, 1000);
<test-elid="test"name="Dracula"></test-el><hr/><divid="more"></div>

This code creates a template in the constructor and uses this.root to reference it. Once connectedCallback is called I insert the template into the DOM and change this.root to point to this so that all of my references to elements still work.

This is a quick way to allow your component to always be able to keep its children correct without using shadowDOM and only placing the template into the DOM as children once connectedCalback is called.

Solution 2:

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

The "expectations" of createElement() is to be given a empty element (without HTML attributes, or children HTML elements), like any other standard HTML element that you create with createElement().

Therefore the impact of adding the Custom Elements to the HTML and DOM specification (and as a consequence, on the HTML engine implementation) is somehow limited.

This restriction doesn't apply to the Shadow DOM because it was not part of the specs before. This won't alter the above expectations. Hence the "odd" difference between the normal DOM tree and the Shadow DOM tree, you're right.

Also, the ability to add a Shadow DOM in the contructor() and not a light DOM, ensures that when the elements of the light DOM are added, they will preventively be filtered according to the Shadow DOM template (and event model, if you listen to the slotchange event).

Post a Comment for "Attaching Shadow Dom To A Custom Element Removes Error, But Why?"