Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating new instances of custom HTML elements

I am trying to understand the newish HTML custom elements.

My goal is, given some array of data, create n instances of the custom element. For example, given a list of 10 users, create 10 user html objects.

Ok - so I define a custom element in the html

HTML

<template-user>
   <div class="user-name"></div>
</template-user>

Then I create my controller

JS

class UserTemplate extends HTMLElement {
   constructor(){
      super();
      this.username = this.querySelectorAll('[class="user-name"]')[0];
   }
   setName(name){
      this.username.innerHtml = name;
   }
}
customElements.define('template-user', UserTemplate);

The page loads fine, but now I am confused on how to reuse that element. If I was doing normal old school stuff, I'd have a for-loop pumping out some HTML strings and setting the innerHTML of something. But now I'd rather do something like

for(let i = 0; i < users.length; i++){
   let userTemplate = new UserTemplate();
   userTemplate.setName(user.name);
   // append to user list, etc..
}

When I try to do this, it seems to almost work. But it cannot find username, ie this.querySelectorAll will return null. That's only when I try to construct a new instance of this element. How then, am I supposed to create new custom element DOM objects?

like image 799
jozenbasin Avatar asked Oct 28 '18 23:10

jozenbasin


People also ask

How can we create custom HTML elements?

We can create custom HTML elements, described by our class, with its own methods and properties, events and so on. Once a custom element is defined, we can use it on par with built-in HTML elements.

What is custom elements in Web Components?

The bread and butter of Web Components are custom elements. The customElements API gives us a path to define custom HTML tags that can be used in any document that contains the defining class. Think of it like a React or Angular component (e.g. ), but without the React or Angular dependency.

What are the different types of custom elements?

There are two types of custom elements: Autonomous custom elements are standalone — they don't inherit from standard HTML elements. You use these on a page by literally writing them out as an HTML element. For example <popup-info>, or document.createElement ("popup-info"). Customized built-in elements inherit from basic HTML elements.

How to extend and customize built-in HTML elements?

We can extend and customize built-in HTML elements by inheriting from their classes. For example, buttons are instances of HTMLButtonElement, let’s build upon it. Provide the third argument to customElements.define, that specifies the tag:


1 Answers

Make sure you understand the requirements and limitations of constructors for Web Components:


https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance

4.13.2 Requirements for custom element constructors

When authoring custom element constructors, authors are bound by the following conformance requirements:

  • A parameter-less call to super() must be the first statement in the constructor body, to establish the correct prototype chain and this value before any further code is run.
  • A return statement must not appear anywhere inside the constructor body, unless it is a simple early-return (return or return this).
  • The constructor must not use the document.write() or document.open() methods.
  • The element's attributes and children must not be inspected, as in the non-upgrade case none will be present, and relying on upgrades makes the element less usable.
  • 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—especially work involving fetching resources or rendering. However, note that connectedCallback can be called more than once, so any initialization work that is truly one-time will need a guard to prevent it from running twice.
  • In general, the constructor should be used to set up initial state and default values, and to set up event listeners and possibly a shadow root.

Several of these requirements are checked during element creation, either directly or indirectly, and failing to follow them will result in a custom element that cannot be instantiated by the parser or DOM APIs. This is true even if the work is done inside a constructor-initiated microtask, as a microtask checkpoint can occur immediately after construction.


You could make changes similar to this:

class TemplateUser extends HTMLElement {
  static get observedAttributes() {
    return ['user-name'];
  }

  constructor(){
    super();
    this.attachShadow({mode:'open'});
    this.shadowRoot.innerHTML = '<div></div>';
  }
   
  attributeChangedCallback(attrName, oldVal, newVal) {
    if (oldVal !== newVal) {
      this.shadowRoot.firstChild.innerHTML = newVal;
    }
  }
 
  get userName() {
    return this.getAttribute('user-name');
  }
  
  set userName(name) {
    this.setAttribute('user-name', name);
  }
}

customElements.define('template-user', TemplateUser);


setTimeout( function () {
  var el = document.querySelector('[user-name="Mummy"]');
  el.userName = "Creature from the Black Lagoon";
}, 2000);
<template-user user-name="Frank N Stein"></template-user>
<template-user user-name="Dracula"></template-user>
<template-user user-name="Mummy"></template-user>

This uses shadowDOM to store a <div>, then you set the value through the user-name attribute or through the userName property.

like image 80
Intervalia Avatar answered Sep 29 '22 14:09

Intervalia