Elementy niestandardowe, wersja 1 – komponenty internetowe wielokrotnego użytku

Elementy niestandardowe pozwalają twórcom stron internetowych definiować nowe tagi HTML, rozszerzać istniejące i tworzyć komponenty sieciowe wielokrotnego użytku.

Dzięki elementom niestandardowym programiści stron internetowych mogą tworzyć nowe tagi HTML, zwiększyć istniejące tagi HTML ani rozszerzyć komponentów utworzonych przez innych programistów. . Interfejs API stanowi podstawę internetu . Zapewnia dostęp do sieci oparte na standardowych rozwiązaniach do tworzenia komponentów wielokrotnego użytku vanilla JS/HTML/CSS. W efekcie uzyskamy mniej pisania i kodu modułowego oraz więcej możliwości ponownego wykorzystania nasze aplikacje.

Wprowadzenie

Przeglądarka jest doskonałym narzędziem do tworzenia struktury aplikacji internetowych. Jest o nazwie HTML. Być może już o tym słyszeliście. Jest poręczny, deklaratywny. i łatwą w obsłudze. Może być świetnym językiem HTML, a jego słownictwo możliwości rozszerzania są ograniczone. Nowy styl HTML standardem zawsze był sposób, automatycznie kojarzyj zachowanie JS ze znacznikami... do tej pory.

Niestandardowe elementy to odpowiedź na modernizację HTML a także łączenie ich z zachowaniem. Jeśli w kodzie HTML brakuje parametru rozwiązania problemu, możemy stworzyć element niestandardowy, który to robi. Niestandardowy uczą przeglądarki nowych sztuczek, zachowując przy tym zalety HTML.

Definiowanie nowego elementu

Aby zdefiniować nowy element HTML, potrzebujemy potęgi JavaScriptu.

Globalny customElements jest używany do definiowania elementu niestandardowego i nauczania o nowym tagu w przeglądarce. Wywołaj funkcję customElements.define() i podaj nazwę tagu które chcesz utworzyć, i kod JavaScript class rozszerzający podstawę HTMLElement.

Przykład – definiowanie mobilnego panelu szuflady, <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 {...});

Przykład użycia:

<app-drawer></app-drawer>

Pamiętaj, że stosowanie elementu niestandardowego nie różni się za pomocą elementu <div> lub dowolnego innego elementu. Instancje można zadeklarować na stronie, tworzone dynamicznie w JavaScripcie, można dołączać detektory zdarzeń. Keep tutaj znajdziesz więcej przykładów.

Definiowanie interfejsu JavaScript API elementu

Funkcjonalność elementu niestandardowego jest określona w standardzie ES2015. class który obejmuje HTMLElement. Rozszerzenie HTMLElement zapewnia element niestandardowy dziedziczy cały interfejs DOM API i oznacza wszelkie właściwości/metody dodane do stają się częścią interfejsu DOM elementu. Zasadniczo użyj klasy do tego, utwórz dla swojego tagu publiczny interfejs API JavaScript.

Przykład – definiowanie interfejsu DOM <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);

W tym przykładzie tworzymy szufladę z właściwością open o nazwie disabled. i metodę toggleDrawer(). Dodatkowo odzwierciedla właściwości jako HTML .

Przydatną cechą elementów niestandardowych jest to, że element this znajduje się w definicji klasy odnosi się do samego elementu DOM, czyli instancji klasy. W naszym np. this odnosi się do <app-drawer>. W ten sposób element może Dołącz detektor click do samego siebie. Nie musisz się ograniczać do detektorów zdarzeń. Cały interfejs DOM API jest dostępny w kodzie elementu. Użyj adresu this, aby uzyskać dostęp do właściwości elementu, sprawdź jego elementy podrzędne (this.children), węzły zapytania (this.querySelectorAll('.items')) itp.

Reguły tworzenia elementów niestandardowych

  1. Nazwa elementu niestandardowego musi zawierać myślnik (-). Dlatego <x-tags>, <my-element> i <my-awesome-app> to prawidłowe nazwy, a <tabs> i <foo_bar> nie. Dzięki temu parser HTML może odróżniać elementy niestandardowe od zwykłych. Zapewnia też gdy do kodu HTML dodano nowe tagi.
  2. Nie możesz zarejestrować tego samego tagu więcej niż raz. Jeśli to zrobisz, DOMException. Poinformuj przeglądarkę o nowym tagu, . Bez cofnięcia się.
  3. Elementów niestandardowych nie można samodzielnie zamykać, bo kod HTML zezwala tylko na kilka elementy do samozamknięcia. Zawsze zapisuj tag zamykający (<app-drawer></app-drawer>).

Reakcje elementu niestandardowego

Element niestandardowy może definiować specjalne punkty zaczepienia cyklu życia do uruchamiania kodu podczas i w ciekawych czasach. Są to tak zwane elementy niestandardowe reakcji.

Nazwa Wywoływane, gdy
constructor Wystąpienie elementu to utworzonych lub uaktualnionych. Przydatne do inicjowania jak skonfigurować detektory zdarzeń, do tworzenia elementu shadow DOM. Zobacz spec aby poznać ograniczenia dotyczące czynności, które możesz wykonywać w usłudze constructor.
connectedCallback Wywoływane za każdym razem, gdy który jest wstawiany do DOM. Przydaje się do uruchamiania kodu konfiguracji, takiego jak pobierania zasobów lub renderowania. Zazwyczaj należy opóźnić pracę do tego czasu.
disconnectedCallback Wywoływana za każdym razem, gdy element jest usuwany z DOM. Przydatne dla podczas czyszczenia kodu.
attributeChangedCallback(attrName, oldVal, newVal) Wywoływane po zaktualizowaniu zaobserwowanego atrybutu. dodane, usunięte, zaktualizowane lub zastąpione. Występuje również w przypadku wartości początkowych gdy element jest tworzony przez parser. nowa. Tylko Uwaga: atrybutów wymienionych we właściwości observedAttributes oddzwonimy.
adoptedCallback element niestandardowy został przeniesiony do nowego elementu document (np. osoba o nazwie document.adoptNode(el)).

Wywołania zwrotne reakcji są synchroniczne. Jeśli ktoś dzwoni pod numer el.setAttribute() dla elementu, przeglądarka od razu wywoła funkcję attributeChangedCallback(). Podobnie otrzymasz disconnectedCallback() zaraz po tym, jak Twój element będzie usuniętych z DOM (np. użytkownik wywołuje el.remove()).

Przykład: dodawanie reakcji elementu niestandardowego do <app-drawer>:

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

  connectedCallback() {
    // ...
  }

  disconnectedCallback() {
    // ...
  }

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

Określ reakcje, jeśli mają sens. Jeśli element jest wystarczająco złożony i otwiera połączenie z IndexedDB w connectedCallback(), wykonaj niezbędne czyszczenie w disconnectedCallback(). Zachowaj jednak ostrożność. Nie możesz polegać na który jest usuwany z DOM we wszystkich okolicznościach. Przykład: Funkcja disconnectedCallback() nie zostanie nigdy wywołana, jeśli użytkownik zamknie kartę.

Właściwości i atrybuty

Właściwości odzwierciedlające atrybuty

Usługi HTML często odzwierciedlają swoją wartość z powrotem w DOM jako Atrybut HTML. Na przykład, gdy wartości hidden lub id zostaną zmienione w JS:

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

wartości są stosowane do aktywnego DOM jako atrybuty:

<div id="my-id" hidden>

Jest to tzw. „odzwierciedlenie właściwości atrybuty”. Tak jest w niemal każdej usłudze w kodzie HTML. Dlaczego? Atrybuty są też przydatne do: konfigurując element deklaratywny i określone interfejsy API, takie jak ułatwienia dostępu i CSS selektory zależą od atrybutów działania.

Odbicie właściwości jest przydatne wszędzie tam, gdzie chcesz zachować DOM elementu. zsynchronizowany ze stanem JavaScript. Dobrym pomysłem jest odzwierciedlają właściwość, więc po zmianie stanu JS styl zdefiniowany przez użytkownika jest stosowany.

Przypomnij sobie nasze <app-drawer>. Konsument tego komponentu może chcieć go zanikać lub uniemożliwiać użytkownikowi interakcję, gdy jest wyłączona:

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

Gdy właściwość disabled zostanie zmieniona w JS, chcemy, by atrybut ten dodany do DOM, tak aby selektor użytkownika się zgadzał. Element może zapewnić, zachowanie, odzwierciedlając wartość atrybutu o tej samej nazwie:

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();
}

Obserwowanie zmian atrybutów

Atrybuty HTML to wygodny sposób deklarowania przez użytkowników stanu początkowego:

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

Elementy mogą reagować na zmiany atrybutów, definiując atrybut attributeChangedCallback Przeglądarka będzie wywoływać tę metodę przy każdej zmianie do atrybutów wymienionych w tablicy observedAttributes.

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.
  }
}

W przykładzie ustawiamy atrybuty dodatkowe w <app-drawer>, gdy Zmieniono atrybut disabled. Chociaż nie zajmujemy się tym tutaj, możesz Używaj też attributeChangedCallback, by synchronizować właściwość JS ze swoim .

Uaktualnianie elementów

Stopniowo ulepszony kod HTML

Wiedzieliśmy, że elementy niestandardowe definiuje się za pomocą funkcji customElements.define() Nie oznacza to jednak, że musisz zdefiniować i zarejestrować elementu niestandardowego za jednym razem.

Elementów niestandardowych można używać przed zarejestrowaniem ich definicji.

Ulepszanie stopniowe to funkcja elementów niestandardowych. Innymi słowy, zadeklarować kilka elementów <app-drawer> na stronie i nigdy nie wywoływać customElements.define('app-drawer', ...) dużo później. To dlatego, że przeglądarka inaczej traktuje potencjalne elementy niestandardowe dzięki nieznanej . Proces wywoływania funkcji define() i obdarowywania istniejącej element z definicją klasy jest nazywany „uaktualnieniami elementu”.

Aby dowiedzieć się, kiedy zostanie zdefiniowana nazwa tagu, możesz użyć opcji window.customElements.whenDefined() Zwraca obietnicę rozwiązaną, gdy .

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

Przykład – opóźnienie pracy do momentu uaktualnienia zbioru elementów podrzędnych

<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.
});

Treść zdefiniowana przez element

Elementy niestandardowe mogą zarządzać własną treścią za pomocą interfejsów DOM API kodu elementu. Bardzo przydają się do tego Reakcje.

Przykład – utwórz element z domyślnym kodem HTML:

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

Zadeklarowanie tego tagu spowoduje:

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

// DO ZROBIENIA: DevSite – przykładowy kod usunięty, ponieważ użyto wbudowanych modułów obsługi zdarzeń

Tworzenie elementu korzystającego z modelu Shadow DOM

Shadow DOM umożliwia elementowi posiadanie, wyrenderowanie i określenie stylu fragmentu DOM, który jest oddzielony od reszty strony. Aha, można nawet ukryć całą aplikację w jednym tagu:

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

Aby użyć modelu Shadow DOM w elemencie niestandardowym, wywołaj this.attachShadow w 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));
  }
  // ...
});

Przykład użycia:

<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>

Niestandardowy tekst użytkownika

// DO ZROBIENIA: DevSite – przykładowy kod usunięty, ponieważ użyto wbudowanych modułów obsługi zdarzeń

Tworzenie elementów z: <template>

Jeśli nie znasz tych informacji, <template> pierwiastek umożliwia zadeklarowanie fragmentów DOM, które są analizowane i bezczynne podczas wczytywania strony oraz można aktywować później w czasie działania. To kolejny podstawowy obiekt API w internecie rodziny komponentów. Szablony są idealnym miejscem do zadeklarowania w strukturze elementu niestandardowego.

Przykład: zarejestrowanie elementu z treścią Shadow DOM utworzoną na podstawie <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>

Te kilka linijek kodu to nie lada wyzwanie. Dowiedzmy się, dnia:

  1. Definiujemy nowy element w kodzie HTML: <x-foo-from-template>
  2. Model Shadow DOM elementu jest tworzony na podstawie modelu <template>.
  3. DOM elementu jest lokalny dzięki interfejsowi Shadow DOM.
  4. Wewnętrzny kod CSS elementu jest ograniczony do niego dzięki interfejsowi Shadow DOM.

Korzystam z Shadow DOM. Moje znaczniki zostały opatrzone pieczęcią z szablonu <template>.

// DO ZROBIENIA: DevSite – przykładowy kod usunięty, ponieważ użyto wbudowanych modułów obsługi zdarzeń

Określanie stylu elementu niestandardowego

Nawet jeśli element definiuje swój własny styl za pomocą modelu Shadow DOM, użytkownicy mogą do Twojego elementu niestandardowego. Są to tak zwane „style zdefiniowane przez użytkownika”.

<!-- 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>

Możesz zadawać sobie pytanie, jak działa szczegółowość CSS, jeśli element ma style zdefiniowane w modelu Shadow DOM. Jeśli chodzi o konkretność, najważniejsze są style użytkownika. Będą zawsze zastępują styl zdefiniowany przez element. Zobacz sekcję Tworzenie elementu który korzysta z modelu Shadow DOM.

Wstępne dostosowywanie stylu niezarejestrowanych elementów

Przed uaktualnieniem elementu możesz kierować na niego reklamy w CSS za pomocą Pseudoklasa :defined. Jest to przydatne przy wstępnym określaniu stylu komponentu. Dla: np. zapobiec układowi lub innemu elementowi wizualnemu funkcji FOUC, ukrywając niezdefiniowane i zanikanie w momencie, gdy są zdefiniowane.

Przykład – ukryj <app-drawer> przed zdefiniowaniem:

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;
}

Po zdefiniowaniu parametru <app-drawer> selektor (app-drawer:not(:defined)) już nie pasuje.

Przedłużanie elementów

Interfejs Custom Elements API przydaje się do tworzenia nowych elementów HTML, ale pozwala też przydatny do rozszerzania innych elementów niestandardowych, a nawet wbudowanego kodu HTML przeglądarki.

Rozszerzanie elementu niestandardowego

Kolejny element niestandardowy można rozszerzyć przez rozszerzenie jego definicji klasy.

Przykład – utwórz typ <fancy-app-drawer>, który obejmuje rozszerzenie <app-drawer>:

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);

Rozciąganie natywnych elementów HTML

Załóżmy, że chcesz utworzyć bardziej wymyślny produkt <button>. Zamiast powielać zachowanie i funkcjonalność <button>, lepszym rozwiązaniem jest stopniowe wprowadzanie ulepszyć istniejący element za pomocą elementów niestandardowych.

Niestandardowy element wbudowany to niestandardowy element, który stanowi rozszerzenie jednego z wbudowanych tagów HTML przeglądarki. Główną korzyścią jest przedłużenie uzyskać wszystkie swoje cechy (właściwości DOM, metody, ułatwienia dostępu). Nie ma lepszego sposobu na stworzenie progresywnej sieci app, a nie stopniowo ulepszaj istniejący kod HTML. .

Aby rozszerzyć element, musisz utworzyć definicję klasy, która dziedziczy z odpowiedniego interfejsu DOM. Przykładem może być element niestandardowy, który rozciąga się Funkcja <button> musi dziedziczyć z reguły HTMLButtonElement zamiast HTMLElement. Analogicznie element rozszerzający <img> musi być dłuższy niż HTMLImageElement.

Przykład – rozszerzenie <button>:

// 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'});

Zwróć uwagę, że wywołanie metody define() nieco się zmienia po rozszerzeniu reklamy natywnej . Wymagany trzeci parametr informuje przeglądarkę, o którym tagu i rozszerzenia. Jest to konieczne, ponieważ wiele tagów HTML korzysta z tego samego DOM za pomocą prostego interfejsu online. <section>, <address>, i <em> (między innymi) mają taki sam udział HTMLElement; <q> i <blockquote> współużytkują HTMLQuoteElement; itp... Jeśli określisz {extends: 'blockquote'}, przeglądarka będzie wiedzieć, że tworzysz plik uzupełnione <blockquote> zamiast <q>. Przeczytaj kod HTML spec gdzie znajdziesz pełną listę interfejsów DOM HTML.

Klienci korzystający z dostosowanego wbudowanego elementu mogą korzystać z niego na kilka sposobów. Mogą zadeklaruj go, dodając atrybut is="" w tagu natywnym:

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

utwórz instancję w kodzie JavaScript:

// 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);

lub użyj operatora new:

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

Oto kolejny przykład z rozszerzeniem <img>.

Przykład – rozszerzenie <img>:

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'});

Użytkownicy deklarują ten komponent jako:

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

lub utwórz instancję w kodzie 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);

Różne szczegóły

Nieznane elementy a niezdefiniowane elementy niestandardowe

Kod HTML jest łatwy w obsłudze i elastyczny. Na przykład zadeklaruj <randomtagthatdoesntexist> na stronie, a przeglądarka jest całkowicie zadowolona akceptacja tego zaproszenia. Dlaczego działają tagi niestandardowe? Odpowiedź brzmi: HTML specyfikacja na to zezwala. Elementy, które nie są zdefiniowane w specyfikacji, są analizowane jako HTMLUnknownElement

To samo nie dotyczy elementów niestandardowych. Analiza potencjalnych elementów niestandardowych jako HTMLElement, jeśli zostały utworzone z prawidłową nazwą (ze znakiem „-”). Ty możesz sprawdzić to w przeglądarce, która obsługuje elementy niestandardowe. Uruchom konsolę: Ctrl+Shift+J (lub Cmd+Opt+J na Macu) i wklej następujące wiersze kodu:

// "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

Dokumentacja API

Globalna wartość customElements definiuje przydatne metody pracy z niestandardową .

define(tagName, constructor, options)

Definiuje nowy element niestandardowy w przeglądarce.

Przykład

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

get(tagName)

Po podaniu prawidłowej nazwy tagu elementu niestandardowego zwraca konstruktor elementu. Zwraca wartość undefined, jeśli nie zarejestrowano żadnej definicji elementu.

Przykład

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

whenDefined(tagName)

Zwraca obietnicę obowiązującą po zdefiniowaniu elementu niestandardowego. Jeśli Element jest już zdefiniowany, zastosuj natychmiast. Odrzuca, jeśli nazwa tagu nie jest prawidłowa prawidłową nazwę elementu niestandardowego.

Przykład

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

Obsługa historii i przeglądarki

Jeśli od kilku lat obserwujesz komponenty sieciowe, że w Chrome w wersji 36 lub nowszej została zaimplementowana wersja interfejsu Custom Elements API document.registerElement() zamiast customElements.define(). To teraz uznano za wycofaną wersję standardu o nazwie v0. customElements.define() to nowa popularność i dostawcy przeglądarek w Twojej firmie. Nosi ona nazwę „Elementy niestandardowe w wersji 1”.

Jeśli interesuje Cię stara specyfikacja v0, sprawdź html5rocks .

Obsługa przeglądarek

Chrome 54 (stan), Safari 10.1 (stan) i Firefox 63 (stan): Elementy niestandardowe w wersji 1. Edge rozpoczął się .

Aby funkcja wykrywała elementy niestandardowe, sprawdź, czy istnieją window.customElements:

const supportsCustomElementsV1 = 'customElements' in window;

Watolina

Dopóki obsługa przeglądarek nie stanie się powszechnie dostępna, samodzielna reklama polyfill dostępne w przypadku elementów niestandardowych w wersji 1. Zalecamy jednak korzystanie z pliku webcomponents.js loader aby optymalnie wczytywać elementy polyfill komponentów internetowych. Moduł ładujący korzysta z wykrywania cech, aby asynchronicznie wczytywać tylko niezbędne wypełnienia wymagane przez przeglądarkę.

Zainstaluj:

npm install --save @webcomponents/webcomponentsjs

Sposób użycia:

<!-- 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>

Podsumowanie

Elementy niestandardowe dają nam nowe narzędzie do definiowania nowych tagów HTML w przeglądarce tworzenie komponentów wielokrotnego użytku. Połącz je z inną nową platformą elementów podstawowych, takich jak Shadow DOM czy <template>. Zaczynamy zdawać sobie sprawę, obraz komponentów sieciowych:

  • Wiele przeglądarek (standard internetowy) do tworzenia i rozszerzania komponentów wielokrotnego użytku.
  • Aby zacząć, nie musisz korzystać z biblioteki ani platformy. Wanilla JS/HTML FTW!
  • Udostępnia znany model programowania. To po prostu DOM/CSS/HTML.
  • Współpracuje z innymi nowymi funkcjami platformy internetowej (Shadow DOM, <template>, CSS) właściwości niestandardowe itd.)
  • Ścisła integracja z Narzędziami deweloperskimi w przeglądarce.
  • Wykorzystaj istniejące ułatwienia dostępu.