Benutzerdefinierte Elemente v1 – Wiederverwendbare Webkomponenten

Mit benutzerdefinierten Elementen können Webentwickler neue HTML-Tags definieren, vorhandene Tags erweitern und wiederverwendbare Webkomponenten erstellen.

Mit benutzerdefinierten Elementen können Webentwickler neue HTML-Tags erstellen, vorhandene HTML-Tags optimieren oder die Komponenten anderer Entwickler erweitern, verfasst wurde. Das API ist die Grundlage für Web- Komponenten. Sie ermöglicht eine standardbasierten Verfahren zur Erstellung wiederverwendbarer Komponenten Vanilla JS/HTML/CSS. Das Ergebnis: weniger Code, modularer Code und mehr Wiederverwendung in für unsere Apps.

Einführung

Der Browser ist ein hervorragendes Tool zum Strukturieren von Webanwendungen. Es ist namens HTML. Vielleicht hast du schon davon gehört! Sie ist deklarativ, übertragbar unterstützt und einfach zu arbeiten. Großartig, wie HTML auch sein mag, mit seinem Vokabular und Erweiterbarkeit sind begrenzt. Die HTML-Laufenden Google-Standard bisher keine Möglichkeit geboten hat, automatisch JS-Verhalten mit eurem Markup zu verknüpfen.

Benutzerdefinierte Elemente sind die Antwort auf die Modernisierung von HTML. und die Strukturierung mit Verhalten. Sollte der HTML-Code für ein Problem lösen, können wir ein benutzerdefiniertes Element erstellen, das dies tut. Benutzerdefiniert -Elementen vermitteln dem Browser neue Tricks und behalten gleichzeitig die Vorteile von HTML bei.

Neues Element definieren

Um ein neues HTML-Element zu definieren, benötigen wir die Leistungsfähigkeit von JavaScript!

Der globale customElements wird zum Definieren eines benutzerdefinierten Elements und zum Unterrichten verwendet über ein neues Tag informieren. customElements.define() mit dem Tag-Namen aufrufen die du erstellen möchtest, und ein JavaScript-class, das die Basis-HTMLElement erweitert.

Beispiel – Festlegen eines mobilen Leistenbereichs, <app-drawer>:

class AppDrawer extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawer);

// Or use an anonymous class if you don't want a named constructor in current scope.
window.customElements.define('app-drawer', class extends HTMLElement {...});

Nutzungsbeispiel:

<app-drawer></app-drawer>

Ein benutzerdefiniertes Element funktioniert genauso wie mit einem <div> oder einem anderen Element. Instanzen können auf der Seite deklariert werden, dynamisch in JavaScript erstellt oder Ereignis-Listener angehängt werden usw. finden Sie weitere Beispiele.

JavaScript API eines Elements definieren

Die Funktionalität eines benutzerdefinierten Elements wird mit einem ES2015 definiert. class das sich über HTMLElement erstreckt. Durch das Erweitern von HTMLElement wird sichergestellt, dass das benutzerdefinierte Element übernimmt die gesamte DOM-API und bedeutet alle Eigenschaften/Methoden, die Sie der -Klasse in die DOM-Schnittstelle des Elements integriert. Im Wesentlichen verwenden Sie die Klasse, um Erstellen Sie eine öffentliche JavaScript API für Ihr Tag.

Beispiel – Definition der DOM-Schnittstelle von <app-drawer>:

class AppDrawer extends HTMLElement {

  // A getter/setter for an open property.
  get open() {
    return this.hasAttribute('open');
  }

  set open(val) {
    // Reflect the value of the open property as an HTML attribute.
    if (val) {
      this.setAttribute('open', '');
    } else {
      this.removeAttribute('open');
    }
    this.toggleDrawer();
  }

  // A getter/setter for a disabled property.
  get disabled() {
    return this.hasAttribute('disabled');
  }

  set disabled(val) {
    // Reflect the value of the disabled property as an HTML attribute.
    if (val) {
      this.setAttribute('disabled', '');
    } else {
      this.removeAttribute('disabled');
    }
  }

  // Can define constructor arguments if you wish.
  constructor() {
    // If you define a constructor, always call super() first!
    // This is specific to CE and required by the spec.
    super();

    // Setup a click listener on <app-drawer> itself.
    this.addEventListener('click', e => {
      // Don't toggle the drawer if it's disabled.
      if (this.disabled) {
        return;
      }
      this.toggleDrawer();
    });
  }

  toggleDrawer() {
    // ...
  }
}

customElements.define('app-drawer', AppDrawer);

In diesem Beispiel erstellen wir eine Leiste mit dem Attribut open, disabled und einer toggleDrawer()-Methode. Außerdem werden Eigenschaften als HTML dargestellt. Attribute enthalten.

Eine tolle Funktion von benutzerdefinierten Elementen ist, dass this in einer Klassendefinition sich auf das DOM-Element selbst, d.h. die Instanz der Klasse. In unserem Beispiel: this bezieht sich auf <app-drawer>. So (😉) kann das Element einen click-Listener an sich selbst anhängen! Und Sie sind nicht auf Event-Listener beschränkt. Die gesamte DOM API ist im Elementcode verfügbar. Verwenden Sie this, um auf Eigenschaften des Elements, untergeordnete Elemente prüfen (this.children), Knoten abfragen (this.querySelectorAll('.items')) usw.

Regeln zum Erstellen benutzerdefinierter Elemente

  1. Der Name eines benutzerdefinierten Elements muss einen Bindestrich (-) enthalten. Also <x-tags>, <my-element> und <my-awesome-app> sind gültige Namen, während <tabs> und <foo_bar> nicht. Diese Anforderung ist erforderlich, damit der HTML-Parser benutzerdefinierte Elemente von regulären Elementen unterscheiden. Außerdem wird so sichergestellt, Kompatibilität beim Hinzufügen neuer Tags zu HTML.
  2. Sie können ein Tag nur einmal registrieren. Wenn Sie dies versuchen, DOMException ausgeben. Sobald Sie dem Browser ein neues Tag mitgeteilt haben, . Keine Rücknahmen.
  3. Benutzerdefinierte Elemente dürfen nicht selbstschließend sein, da bei HTML nur einige wenige Elemente zulässig sind. Elemente selbstschließend sein. Schreiben Sie immer ein schließendes Tag. (<app-drawer></app-drawer>).

Reaktionen auf benutzerdefinierte Elemente

Mit einem benutzerdefinierten Element können spezielle Lebenszyklus-Hooks zum Ausführen von Code während interessante Zeiten ihrer Existenz. Diese werden als benutzerdefiniertes Element Reaktionen.

Name Angerufen, wenn
constructor Eine Instanz des -Elements ist erstellt oder aktualisiert. Nützlich für die Initialisierung Ereignis-Listener einrichten oder zum Erstellen eines Schattendoms. Weitere Informationen finden Sie in der <ph type="x-smartling-placeholder"></ph> Spezifikation .constructor
connectedCallback Wird jedes Mal aufgerufen, wenn der wird in das DOM eingefügt. Nützlich zum Ausführen von Einrichtungscode, z. B. Abrufen von Ressourcen oder Rendern. Im Allgemeinen sollten Sie versuchen, bis zu diesem Zeitpunkt.
disconnectedCallback Wird jedes Mal aufgerufen, wenn das Element aus dem DOM entfernt wird. Nützlich für Clean-up-Code ausgeführt wird.
attributeChangedCallback(attrName, oldVal, newVal) Wird aufgerufen, wenn ein beobachtetes Attribut hinzugefügt, entfernt, aktualisiert oder ersetzt wurden. Wird auch für Anfangswerte aufgerufen Ein Element wird vom Parser erstellt. aktualisiert. Hinweis: Nur Attribute, die in der observedAttributes-Property aufgeführt sind, diesen Rückruf empfangen.
adoptedCallback Die benutzerdefiniertes Element wurde in ein neues document verschoben (z.B. Jemand namens document.adoptNode(el)).

Reaktions-Callbacks sind synchron. Wenn jemand el.setAttribute() anruft für Ihr Element, ruft der Browser sofort attributeChangedCallback() auf. Entsprechend erhalten Sie ein disconnectedCallback() direkt, nachdem Ihr Element aus dem DOM entfernt werden (z.B. wenn der Nutzer el.remove() aufruft).

Beispiel:Reaktionen für benutzerdefinierte Elemente zu <app-drawer> hinzufügen:

class AppDrawer extends HTMLElement {
  constructor() {
    super(); // always call super() first in the constructor.
    // ...
  }

  connectedCallback() {
    // ...
  }

  disconnectedCallback() {
    // ...
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    // ...
  }
}

Definieren Sie Reaktionen, wenn es Sinn ergibt. Wenn Ihr Element ausreichend komplex ist und eine Verbindung zu IndexedDB in connectedCallback() herstellt, führen Sie Bereinigungsarbeiten in disconnectedCallback(). Aber sei vorsichtig! Sie können sich nicht darauf verlassen, das unter allen Umständen aus dem DOM entfernt wird. Beispiel: disconnectedCallback() wird nie aufgerufen, wenn der Nutzer den Tab schließt.

Eigenschaften und Attribute

Attribute auf Attribute anwenden

Es ist üblich, dass HTML-Eigenschaften ihren Wert im DOM als HTML-Attribut. Das ist beispielsweise der Fall, wenn die Werte von hidden oder id in JS:

div.id = 'my-id';
div.hidden = true;

werden die Werte als Attribute auf das Live-DOM angewendet:

<div id="my-id" hidden>

Dies wird als „reflektierende Eigenschaften von Attribute“. Dies ist bei fast jeder HTML-Property möglich. Warum? Attribute sind auch nützlich für Deklaratives Konfigurieren eines Elements und bestimmter APIs wie Barrierefreiheit und CSS Selektoren funktionieren nur mit Attributen.

Das Reflektieren einer Eigenschaft ist überall dort nützlich, wo Sie das DOM des Elements mit dem JavaScript-Status synchron. Ein Grund, warum Sie reflektiert eine -Eigenschaft, damit benutzerdefinierte Stile angewendet werden, wenn sich der JS-Zustand ändert.

Erinnern Sie sich an unsere <app-drawer>. Ein Nutzer dieser Komponente möchte sie möglicherweise ausblenden. und/oder Nutzerinteraktionen verhindern, wenn sie deaktiviert sind:

app-drawer[disabled] {
  opacity: 0.5;
  pointer-events: none;
}

Wenn das Attribut disabled in JS geändert wird, soll dieses Attribut wurde dem DOM hinzugefügt, damit die Auswahl des Nutzers übereinstimmt. Das Element kann dazu beitragen, durch reflektieren des Werts an ein Attribut desselben Namens:

get disabled() {
  return this.hasAttribute('disabled');
}

set disabled(val) {
  // Reflect the value of `disabled` as an attribute.
  if (val) {
    this.setAttribute('disabled', '');
  } else {
    this.removeAttribute('disabled');
  }
  this.toggleDrawer();
}

Änderungen an Attributen beobachten

HTML-Attribute sind eine bequeme Möglichkeit für Nutzer, den Anfangszustand zu deklarieren:

<app-drawer open disabled></app-drawer>

Elemente können auf Attributänderungen reagieren, indem sie eine attributeChangedCallback Der Browser ruft diese Methode bei jeder Änderung auf zu Attributen, die im Array observedAttributes aufgeführt sind.

class AppDrawer extends HTMLElement {
  // ...

  static get observedAttributes() {
    return ['disabled', 'open'];
  }

  get disabled() {
    return this.hasAttribute('disabled');
  }

  set disabled(val) {
    if (val) {
      this.setAttribute('disabled', '');
    } else {
      this.removeAttribute('disabled');
    }
  }

  // Only called for the disabled and open attributes due to observedAttributes
  attributeChangedCallback(name, oldValue, newValue) {
    // When the drawer is disabled, update keyboard/screen reader behavior.
    if (this.disabled) {
      this.setAttribute('tabindex', '-1');
      this.setAttribute('aria-disabled', 'true');
    } else {
      this.setAttribute('tabindex', '0');
      this.setAttribute('aria-disabled', 'false');
    }
    // TODO: also react to the open attribute changing.
  }
}

In diesem Beispiel legen wir zusätzliche Attribute für <app-drawer> fest, wenn ein Das Attribut „disabled“ wurde geändert. Auch wenn wir das hier nicht tun, auch attributeChangedCallback verwenden, um eine JS-Eigenschaft mit ihrer .

Element upgrades

Progressive HTML-Optimierung

Wie Sie bereits wissen, werden benutzerdefinierte Elemente durch Aufrufen customElements.define() Das bedeutet jedoch nicht, dass Sie eine auf einmal benutzerdefinierte Elemente.

Benutzerdefinierte Elemente können verwendet werden, bevor ihre Definition registriert wird.

Progressive Enhancement ist eine Funktion benutzerdefinierter Elemente. Mit anderen Worten: Sie können eine Reihe von <app-drawer>-Elementen auf der Seite deklarieren und niemals customElements.define('app-drawer', ...). Das liegt daran, dass der Browser mögliche benutzerdefinierte Elemente aufgrund von unbekannter -Tags. Der Aufruf von define() und die Verleihung eines vorhandenen mit einer Klassendefinition wird als "Elementupgrades" bezeichnet.

Wenn Sie wissen möchten, wann ein Tag-Name definiert wird, können Sie window.customElements.whenDefined() Es gibt ein Promise zurück, das aufgelöst wird, wenn der -Element definiert wird.

customElements.whenDefined('app-drawer').then(() => {
  console.log('app-drawer defined');
});

Beispiel – Arbeit verzögern, bis eine Gruppe von untergeordneten Elementen aktualisiert wurde

<share-buttons>
  <social-button type="twitter"><a href="...">Twitter</a></social-button>
  <social-button type="fb"><a href="...">Facebook</a></social-button>
  <social-button type="plus"><a href="...">G+</a></social-button>
</share-buttons>
// Fetch all the children of <share-buttons> that are not defined yet.
let undefinedButtons = buttons.querySelectorAll(':not(:defined)');

let promises = [...undefinedButtons].map((socialButton) => {
  return customElements.whenDefined(socialButton.localName);
});

// Wait for all the social-buttons to be upgraded.
Promise.all(promises).then(() => {
  // All social-button children are ready.
});

Elementdefinierter Inhalt

Benutzerdefinierte Elemente können ihren eigenen Inhalt mithilfe der darin enthaltenen DOM-APIs verwalten -Elementcode. In diesem Fall sind Reaktionen sehr praktisch.

Beispiel: Erstellen Sie ein Element mit Standard-HTML:

customElements.define('x-foo-with-markup', class extends HTMLElement {
  connectedCallback() {
    this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
  }
  // ...
});

Das Deklarieren dieses Tags führt zu Folgendem:

<x-foo-with-markup>
  <b>I'm an x-foo-with-markup!</b>
</x-foo-with-markup>

// TODO: DevSite - Codebeispiel entfernt, da Inline-Event-Handler verwendet wurden

Element erstellen, das Shadow DOM verwendet

Mit Shadow DOM kann ein Element einen Teil des Elements DOM, das vom Rest der Seite getrennt ist. Sie können sich sogar die gesamte App in einem einzigen Tag verwenden:

<!-- chat-app's implementation details are hidden away in Shadow DOM. -->
<chat-app></chat-app>

Um Shadow DOM in einem benutzerdefinierten Element zu verwenden, rufen Sie this.attachShadow in Ihrem constructor:

let tmpl = document.createElement('template');
tmpl.innerHTML = `
  <style>:host { ... }</style> <!-- look ma, scoped styles -->
  <b>I'm in shadow dom!</b>
  <slot></slot>
`;

customElements.define('x-foo-shadowdom', class extends HTMLElement {
  constructor() {
    super(); // always call super() first in the constructor.

    // Attach a shadow root to the element.
    let shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.appendChild(tmpl.content.cloneNode(true));
  }
  // ...
});

Nutzungsbeispiel:

<x-foo-shadowdom>
  <p><b>User's</b> custom text</p>
</x-foo-shadowdom>

<!-- renders as -->
<x-foo-shadowdom>
  #shadow-root
  <b>I'm in shadow dom!</b>
  <slot></slot> <!-- slotted content appears here -->
</x-foo-shadowdom>

Benutzerdefinierter Text des Nutzers

// TODO: DevSite - Codebeispiel entfernt, da Inline-Event-Handler verwendet wurden

Elemente aus einem <template> erstellen

Für diejenigen, die ihn nicht kennen, finden Sie hier die <template> Element können Sie DOM-Fragmente deklarieren, die geparst, beim Seitenaufbau inaktiv und und kann später zur Laufzeit aktiviert werden. Das ist ein weiteres API-Primitive im Web. Familie der Komponenten. Vorlagen sind ein idealer Platzhalter, um Struktur eines benutzerdefinierten Elements.

Beispiel:Registrieren eines Elements mit Shadow DOM-Inhalten, die aus einem <template>:

<template id="x-foo-from-template">
  <style>
    p { color: green; }
  </style>
  <p>I'm in Shadow DOM. My markup was stamped from a &lt;template&gt;.</p>
</template>

<script>
  let tmpl = document.querySelector('#x-foo-from-template');
  // If your code is inside of an HTML Import you'll need to change the above line to:
  // let tmpl = document.currentScript.ownerDocument.querySelector('#x-foo-from-template');

  customElements.define('x-foo-from-template', class extends HTMLElement {
    constructor() {
      super(); // always call super() first in the constructor.
      let shadowRoot = this.attachShadow({mode: 'open'});
      shadowRoot.appendChild(tmpl.content.cloneNode(true));
    }
    // ...
  });
</script>

Diese wenigen Codezeilen machen es möglich. Sehen wir uns an, am:

  1. Wir definieren ein neues Element in HTML: <x-foo-from-template>
  2. Das Shadow DOM des Elements wird aus einem <template> erstellt
  3. Dank des Shadow DOM ist das DOM des Elements lokal beim Element.
  4. Der interne CSS-Code des Elements wird dank des Shadow DOM auf das Element beschränkt.

Ich befinde mich in Shadow DOM. Mein Markup wurde aus einer <template> gestempelt.

// TODO: DevSite - Codebeispiel entfernt, da Inline-Event-Handler verwendet wurden

Benutzerdefiniertes Element gestalten

Auch wenn Ihr Element mithilfe von Shadow DOM seinen eigenen Stil definiert, können Nutzer Stile Ihr benutzerdefiniertes Element von ihrer Seite entfernen. Diese werden als „benutzerdefinierte Stile“ bezeichnet.

<!-- user-defined styling -->
<style>
  app-drawer {
    display: flex;
  }
  panel-item {
    transition: opacity 400ms ease-in-out;
    opacity: 0.3;
    flex: 1;
    text-align: center;
    border-radius: 50%;
  }
  panel-item:hover {
    opacity: 1.0;
    background: rgb(255, 0, 255);
    color: white;
  }
  app-panel > panel-item {
    padding: 5px;
    list-style: none;
    margin: 0 7px;
  }
</style>

<app-drawer>
  <panel-item>Do</panel-item>
  <panel-item>Re</panel-item>
  <panel-item>Mi</panel-item>
</app-drawer>

Sie fragen sich vielleicht, wie die CSS-Spezifität funktioniert, wenn das Element über Stile verfügt. die im Shadow DOM definiert sind. In puncto Spezifität ist der Stil der Nutzenden entscheidend. Sie werden immer den elementdefinierten Stil überschreiben. Weitere Informationen hierzu finden Sie im Abschnitt Ein Element erstellen das Shadow DOM verwendet.

Stile für nicht registrierte Elemente festlegen

Bevor ein Element aktualisiert wird, können Sie in CSS mithilfe der Methode :defined. Dies ist nützlich, wenn Sie eine Komponente vorab gestalten möchten. Für Sie können beispielsweise Layout- oder andere visuelle Elemente verhindern, indem Sie undefinierte und einblenden, wenn sie definiert wurden.

Beispiel: Blenden Sie <app-drawer> aus, bevor es definiert wird:

app-drawer:not(:defined) {
  /* Pre-style, give layout, replicate app-drawer's eventual styles, etc. */
  display: inline-block;
  height: 100vh;
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

Nachdem <app-drawer> definiert wurde, wird der Selektor (app-drawer:not(:defined)) stimmt nicht mehr überein.

Elemente erweitern

Die Custom Elements API ist nützlich zum Erstellen neuer HTML-Elemente, sie ist aber auch nützlich zur Erweiterung anderer benutzerdefinierter Elemente oder sogar des integrierten HTML-Codes des Browsers.

Benutzerdefiniertes Element erweitern

Zum Erweitern eines weiteren benutzerdefinierten Elements wird dessen Klassendefinition erweitert.

Beispiel: Erstellen Sie <fancy-app-drawer>, das <app-drawer> erweitert:

class FancyDrawer extends AppDrawer {
  constructor() {
    super(); // always call super() first in the constructor. This also calls the extended class' constructor.
    // ...
  }

  toggleDrawer() {
    // Possibly different toggle implementation?
    // Use ES2015 if you need to call the parent method.
    // super.toggleDrawer()
  }

  anotherMethod() {
    // ...
  }
}

customElements.define('fancy-app-drawer', FancyDrawer);

Erweitern von nativen HTML-Elementen

Angenommen, Sie möchten ein ansprechendes <button>-Element erstellen. Anstatt die Verhalten und der Funktionalität von <button> hat, ist es besser, schrittweise nach und nach können Sie das vorhandene Element mit benutzerdefinierten Elementen optimieren.

Ein benutzerdefiniertes integriertes Element ist ein benutzerdefiniertes Element, das eine der die integrierten HTML-Tags des Browsers. Der Hauptvorteil der Erweiterung einer bestehenden -Element besteht darin, alle seine Funktionen zu erhalten (DOM-Eigenschaften, Methoden, Zugänglichkeit). Es gibt keinen besseren Weg, ein progressives Web zu schreiben. als vorhandenen HTML-Code schrittweise zu verbessern. Elemente.

Um ein Element zu erweitern, müssen Sie eine Klassendefinition erstellen, die aus der richtigen DOM-Schnittstelle. Beispiel: Ein benutzerdefiniertes Element, das <button> muss von HTMLButtonElement anstelle von HTMLElement übernehmen. Ebenso muss ein Element, das <img> erweitert, HTMLImageElement erweitern.

Beispiel<button> erweitern:

// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class FancyButton extends HTMLButtonElement {
  constructor() {
    super(); // always call super() first in the constructor.
    this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY));
  }

  // Material design ripple animation.
  drawRipple(x, y) {
    let div = document.createElement('div');
    div.classList.add('ripple');
    this.appendChild(div);
    div.style.top = `${y - div.clientHeight/2}px`;
    div.style.left = `${x - div.clientWidth/2}px`;
    div.style.backgroundColor = 'currentColor';
    div.classList.add('run');
    div.addEventListener('transitionend', (e) => div.remove());
  }
}

customElements.define('fancy-button', FancyButton, {extends: 'button'});

Der Aufruf von define() ändert sich geringfügig, wenn eine native Anzeige erweitert wird. -Elements. Der erforderliche dritte Parameter teilt dem Browser mit, sich ausweiten kann. Dies ist notwendig, da viele HTML-Tags dasselbe DOM verwenden. . <section>, <address> und <em> haben alle etwas geteilt HTMLElement; Sowohl <q> als auch <blockquote> teilen HTMLQuoteElement. usw... Wenn Sie {extends: 'blockquote'} angeben, weiß der Browser, dass Sie versprühten <blockquote> statt <q>. Siehe HTML Technische Daten finden Sie eine vollständige Liste der HTML-DOM-Schnittstellen.

Nutzer eines benutzerdefinierten integrierten Elements können es auf verschiedene Arten verwenden. Sie können deklarieren Sie sie, indem Sie dem nativen Tag das Attribut is="" hinzufügen:

<!-- This <button> is a fancy button. -->
<button is="fancy-button" disabled>Fancy button!</button>

eine Instanz in JavaScript erstellen:

// Custom elements overload createElement() to support the is="" attribute.
let button = document.createElement('button', {is: 'fancy-button'});
button.textContent = 'Fancy button!';
button.disabled = true;
document.body.appendChild(button);

oder verwenden Sie den new-Operator:

let button = new FancyButton();
button.textContent = 'Fancy button!';
button.disabled = true;

Hier ein weiteres Beispiel für die Erweiterung von <img>.

Beispiel<img> erweitern:

customElements.define('bigger-img', class extends Image {
  // Give img default size if users don't specify.
  constructor(width=50, height=50) {
    super(width * 10, height * 10);
  }
}, {extends: 'img'});

Nutzer deklarieren diese Komponente als:

<!-- This <img> is a bigger img. -->
<img is="bigger-img" width="15" height="20">

oder erstellen Sie eine Instanz in JavaScript:

const BiggerImage = customElements.get('bigger-img');
const image = new BiggerImage(15, 20); // pass constructor values like so.
console.assert(image.width === 150);
console.assert(image.height === 200);

Sonstige Details

Unbekannte und nicht definierte benutzerdefinierte Elemente

HTML ist wenig benutzerfreundlich und flexibel. Erklären Sie z. B., <randomtagthatdoesntexist> pro Seite und der Browser ist zufrieden. und akzeptieren. Warum funktionieren nicht standardmäßige Tags? Die Antwort ist die HTML- Spezifikation dies zulässt. Elemente, die nicht in der Spezifikation definiert sind, werden geparst als HTMLUnknownElement

Dasselbe gilt für benutzerdefinierte Elemente. Potenzielle benutzerdefinierte Elemente werden geparst als HTMLElement, wenn sie mit einem gültigen Namen (einschließlich "-") erstellt wurden. Ich können Sie dies in einem Browser überprüfen, der benutzerdefinierte Elemente unterstützt. Starten Sie die Konsole: Drücken Sie Strg + Umschalt + J (oder Cmd + Opt + J auf einem Mac) und fügen Sie den folgenden Codezeilen aus:

// "tabs" is not a valid custom element name
document.createElement('tabs') instanceof HTMLUnknownElement === true

// "x-tabs" is a valid custom element name
document.createElement('x-tabs') instanceof HTMLElement === true

API-Referenz

Der globale customElements definiert nützliche Methoden für die Arbeit mit benutzerdefinierten Elemente.

define(tagName, constructor, options)

Definiert ein neues benutzerdefiniertes Element im Browser.

Beispiel

customElements.define('my-app', class extends HTMLElement { ... });
customElements.define(
    'fancy-button', class extends HTMLButtonElement { ... }, {extends: 'button'});

get(tagName)

Gibt bei Angabe eines gültigen Tag-Namens für ein benutzerdefiniertes Element den Konstruktor des Elements zurück. Gibt undefined zurück, wenn keine Elementdefinition registriert wurde.

Beispiel

let Drawer = customElements.get('app-drawer');
let drawer = new Drawer();

whenDefined(tagName)

Gibt ein Promise zurück, das aufgelöst wird, wenn das benutzerdefinierte Element definiert ist. Wenn die -Element bereits definiert ist, muss sofort aufgelöst werden. Wird abgelehnt, wenn der folgende Tag-Name nicht lautet: einen gültigen Namen für das benutzerdefinierte Element.

Beispiel

customElements.whenDefined('app-drawer').then(() => {
  console.log('ready!');
});

Verlauf und Browserunterstützung

Wenn Sie in den letzten Jahren Webkomponenten folgen, wissen, dass in Chrome 36+ eine Version der Custom Elements API implementiert ist, bei der document.registerElement() statt customElements.define(). Das ist jetzt als nicht mehr unterstützte Version des Standards v0 an. customElements.define() ist aktuell angesagt und welche Browseranbieter sind da mit der Umsetzung beginnen. Es heißt Custom Elements v1.

Wenn du Interesse an der alten Version 0 hast, sieh dir die html5rocks

Unterstützte Browser

Chrome 54 (Status), Safari 10.1 (status) und Firefox 63 (Status) Benutzerdefinierte Elemente (Version 1). Edge hat begonnen Entwicklung.

Um benutzerdefinierte Elemente zu erkennen, müssen Sie prüfen, ob folgende Elemente vorhanden sind: window.customElements:

const supportsCustomElementsV1 = 'customElements' in window;

Polyfill

Solange die Browserunterstützung nicht allgemein verfügbar ist, Eigenständiger Polyfill die für Custom Elements v1 verfügbar sind. Wir empfehlen jedoch die Verwendung von webcomponents.js Ladeprogramm um die Webkomponenten-Polyfills optimal zu laden. Ladeprogramm verwendet die Feature-Erkennung, um nur die erforderlichen Pollyfills asynchron zu laden die für den Browser erforderlich sind.

Installieren:

npm install --save @webcomponents/webcomponentsjs

Verwendung:

<!-- Use the custom element on the page. -->
<my-element></my-element>

<!-- Load polyfills; note that "loader" will load these async -->
<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js" defer></script>

<!-- Load a custom element definitions in `waitFor` and return a promise -->
<script type="module">
  function loadScript(src) {
    return new Promise(function(resolve, reject) {
      const script = document.createElement('script');
      script.src = src;
      script.onload = resolve;
      script.onerror = reject;
      document.head.appendChild(script);
    });
  }

  WebComponents.waitFor(() => {
    // At this point we are guaranteed that all required polyfills have
    // loaded, and can use web components APIs.
    // Next, load element definitions that call `customElements.define`.
    // Note: returning a promise causes the custom elements
    // polyfill to wait until all definitions are loaded and then upgrade
    // the document in one batch, for better performance.
    return loadScript('my-element.js');
  });
</script>

Fazit

Benutzerdefinierte Elemente bieten uns ein neues Tool zum Definieren neuer HTML-Tags im Browser und wiederverwendbaren Komponenten. Kombinieren Sie sie mit der anderen neuen Plattform. wie Shadow DOM und <template>. Wir erkennen, von Web Components:

  • Browserübergreifend (Webstandard) zum Erstellen und Erweitern wiederverwendbarer Komponenten.
  • Erfordert keine Bibliothek oder kein Framework. Vanilla JS/HTML FTW!
  • Stellt ein vertrautes Programmiermodell bereit. sondern lediglich DOM/CSS/HTML.
  • Funktioniert gut mit anderen neuen Webplattformfunktionen (Shadow DOM, <template>, CSS). benutzerdefinierte Eigenschaften usw.)
  • Eng in die Entwicklertools des Browsers integriert.
  • Nutzen Sie vorhandene Bedienungshilfen.