Social-Media-Foto von Andre Taissin auf Unsplash
Der einfachste Weg, ein beliebiges DOM-integriertes Element zu erweitern:
Keine Polyfills erforderlich , alle modernen Browser funktionieren einfach™️
Es ist problemlos möglich, HTML , SVG oder jeden anderen benutzerdefinierten Namespace wie MathML zu erweitern
Elemente können entweder von Grund auf neu erstellt oder bei Bedarf für eine sanfte Feuchtigkeitsversorgung aufgerüstet werden
passt in 313 Bytes (Kern) oder 774 Bytes inklusive Lebenszyklusrückrufen für benutzerdefinierte Elemente (nichts Neues zu lernen)
Dieses Beispiel kann live getestet werden und kratzt nur an der Oberfläche dessen, was mit diesem extrem kleinen, aber leistungsstarken Modul möglich ist.
// const createRegistry = require('nonchalance');import createRegistry from 'nonchalance/core';// Der Standardexport akzeptiert optional einen „globalThis“-ähnlichen// Kontext für alle Umgebungen, die kein natives DOM haben./ / Eine solche Registrierung kann qualifizierte Klassennamen sowie einen „Dokument“-Verweis offenlegen//, der zum Erstellen von Elementen verwendet wird.const {HTML} = createRegistry();// jedes Element erweitern, um es zu erstellen oder zu aktualisieren, benutzerdefinierte Elemente// aus einer Registrierung, die nichts mit der globalen Kontextklasse gemeinsam hat. Passwort erweitert HTML.Input { // erbt ein statisches Tag field = „input“ Konstruktor(...args) {super(...args);this.type = 'password'; } // Vermeiden Sie, dass bösartige Skripte problemlos Passwörter abrufen get value() {return '********'; } // Demo-Fall: Verwenden Sie weiterhin den nativen „Wert“, wenn das Passwort festgelegt ist set value(secret) {super.value = Secret; }}document.body.innerHTML = ` <form> <input type="text" name="user" placeholder="user"> <input type="password" name="password" placeholder="password"> <input type="submit"> </form>`;// aktualisieren Sie das gewünschte Element über new ClassTypenew Password(document.querySelector('[type="password"]'));// oder erstellen Sie eine neue Instanzconst Secret = Object.assign(neues Passwort, { Name: 'pass', Wert: Math.random(), Platzhalter: 'Laufzeitpasswort'});const form = document.querySelector('form');form.insertBefore(secret, form.lastElementChild);
Mithilfe des benutzerdefinierten Funktionsmoduls ist es möglich, jede Art von Element zu aktualisieren, ohne jemals mit dem Fehler „Illegal Constructor“ konfrontiert zu werden, der jedes Mal auftritt, wenn eine natürliche class extends HTMLSomethingElement {}
, wenn diese Klasse nicht global als Eintrag in definiert ist customElements
Registrierung.
Nicht nur, dass durch dieses Modul nichts global über den globalen Kontext geteilt wird, auch jede umständliche zusätzliche Arbeit, damit eine integrierte Erweiterung funktioniert, ist völlig unnötig:
Neue oder übergebene Elemente behalten immer ihre Prototyp-Wurzelkette
Es können niemals zusätzliche Attribute oder widersprüchliche Namen auftreten
Darüber hinaus ist es, da für jedes Modul oder Projekt eine beliebige HTML-Registrierung erstellt werden kann, um diese zwischen seinen Komponenten zu teilen, auch möglich, an eine solche Registrierungserstellung jede gefälschte oder verspottete globalThis
-ähnliche Umgebung zu übergeben, mit mindestens einem document
, das ein createElementNS(namespace, tagName)
verfügbar macht createElementNS(namespace, tagName)
-Methode und eine oder mehrere Klassen, die das Projekt testen soll, wie z. B. HTMLElement
und/oder alle anderen, die für den Erfolg eines solchen Projekts erforderlich sind.
Da das primäre Ziel dieses Moduls jedoch das DOM ist, wird die globalThis
Referenz als sinnvolle Standardeinstellung verwendet. Dies bedeutet jedoch nicht, dass in den durch den Standardexport erstellten Registrierungen etwas gemeinsam genutzt wird.
NEIN . Die Funktionsweise custom-function
lässt sich wie folgt zusammenfassen:
# a native <p> protoype chain HTMLParagraphElement -> HTMLElement -> Element -> Node # a <p> passed to new (class CustomP extends HTML.P {}) CustomP -> HTMLParagraphElement -> HTMLElement -> Element -> Node # a <p> passed to class AnotherP extends CustomP {} AnotherP -> CustomP -> HTMLParagraphElement -> HTMLElement -> Element -> Node
Mit wenigen Worten: Das Erstellen eines Elements über new AnotherP
oder das Aktualisieren eines Elements über new AnotherP(liveParagraph)
aktualisiert lediglich die Prototypenkette, ohne dass das Element jemals das DOM verlassen oder seine native Natur ändern muss, da diese in der prototypischen Vererbungskette erhalten bleibt .
Zusammenfassung: Nonchalance -Registrierungen aktualisieren einfach Elemente, ohne ihre Natur zu ändern, genau so, wie native Built-Ins die Arbeit unter der Haube erweitern.
Ja , entweder über /jsx
Export oder über den /ref
Export.
Informationen zu /jsx finden Sie unter Was ist der /jsx-Export? Abschnitt.
Informationen zu /ref finden Sie unter Was ist der /ref-Export? Abschnitt.
Der /ce
-Export aktualisiert Elemente automatisch auf eine Weise, die mit den Methoden connectedCallback
, disconnectedCallback
und attributeChangedCallback
der Klassen zusammen mit ihrem statischen observedAttributes
Feld kompatibel ist.
Das Modul verwendet eine fein abgestimmte Version des bereits gut funktionierenden As-Custom-Element-Moduls.
Sehen Sie sich diese Live-Demo auf Codepen an, um eine Vorstellung davon zu bekommen, wie das funktioniert.
NEIN . Metaphorisch gesprochen haben HTML- Elemente sowohl eine semantische Bedeutung als auch einen klar definierten und gewünschten Nutzen, sobald sie live sind, genauso wie eine JS -Funktion für immer eine JS -Funktion sein wird, selbst wenn Object.setPrototypeOf(() => {}, Number.prototype)
passiert ... können Sie sehen oder zustimmen, wie falsch das ist?
Dieses Modul möchte (und kann wahrscheinlich auch nicht) vor Missbrauch seiner Funktionen schützen. Stellen Sie daher sicher, dass jedes Element, wenn es aktualisiert wird, seine native Prototypenkette im Hintergrund beibehält, andernfalls kämpfen Sie allein gegen das DOM . .. was ziemlich unpraktisch ist, wenn Sie mich fragen?
Kurz gesagt, genauso wie customElements.define('my-link', class extends HTMLDivElement {}, {extends: 'a'})
keinen Sinn ergibt, wird dieses Modul hoffentlich darauf vertrauen, dass seine Benutzer unsinnige Klassen vermeiden.
Derzeit verweist der Standard-/Hauptexport für dieses Modul auf denselben /core
-Export.
Da dieses Modul mit seiner Einfachheit und der Größe des Vaporware-Codes eine Büchse der Pandora öffnet, und vor allem, weil es immer noch hinter einer 0.
Semver-Version steckt, versuche ich zu überlegen, was in den Index aufgenommen werden sollte, und hier einige meiner Gedanken:
Wäre es nicht cool, ein ESX-basiertes Modul zu haben, das auf diese Weise definierte Komponenten versteht?
Wäre es nicht cool, eine JSX- Pragma-Funktion zu haben, die Komponenten über dieses Modul erstellt?
Wäre es nicht cool, ... (Ihren Platzhalter hier) ... zu haben?
Ja, es wäre cool, und wenn ich mich darüber entscheiden kann, wie der Standardexport benannt werden soll, bin ich bereit, diesen Namen neben anderen Vorteilen als Standardeintrag für dieses Modul einzuführen ... bleiben Sie dran oder geben Sie es mir bitte Gedanken und Hinweise, wie das geht
Bis dahin verwenden Sie jedoch bitte explizite Exporte, um sicherzustellen, dass zukünftige Updates Ihre Logik nicht durcheinander bringen, und ich entschuldige mich, wenn Ihnen die jüngsten Änderungen Probleme bereitet haben, aber ich bin mir ziemlich sicher, dass Sie das leicht nachvollziehen oder verstehen können, dass das für immer war!
Wenn Elemente aus der Ferne aufgewertet werden, ist es möglich, dass ihnen Eigenschaften zugewiesen wurden, die nicht durch ihre Zugriffselemente übertragen werden konnten.
Dieser Helfer stellt einfach sicher, dass geerbte Eigenschaften als eigene Elementschlüssel entfernt werden, um dann direkt danach als Zugriffsfunktionen ausgelöst zu werden.
import createRegistry from 'nonchalance/ce';import accessors from 'nonchalance/accessors';const {HTML} = createRegistry();class WithAccessors erweitert HTML.Div { Konstruktor(...args) {accessors(super(...args)); } get value() {console.log('get value', this._value);return this._value; } set value(_value) {this._value = _value;console.log('set value', this._value); }}// native div elementconst div = document.createElement('div');div.value = 123;// upgradednew WithAccessors(div);// re-checkconsole.log(div.value);
Sehen Sie es live, um mehr zu testen.
Der /builtin
Export (248 Bytes) ist genau wie /core
, außer dass er hinter den Kulissen keine custom-function
verwendet, was bedeutet, dass:
Es ist nicht möglich, new BuiltIn()
oder new BuiltIn(element)
zu verwenden, da dies einen Fehler auslösen würde, es sei denn, es ist nicht bereits als „customElement“-Built-in-Erweiterung registriert
Es kann zur Automatisierung der Komponentenregistrierung verwendet werden, wie in dieser Live-Demo auf CodePen gezeigt
Die einzige große Einschränkung bei diesem Export besteht darin, dass die integrierte Polyfüllung möglicherweise für Safari oder WebKit benötigt wird, da er auf echten benutzerdefinierten Standardelementen basiert. Beispiel:
<!-- Skript der obersten Seite nur für Safari polyfill --><script>self.chrome ||self.netscape ||document.write('<script src="//unpkg.com/@webreflection/custom-elements -builtin"><x2fscript>');</script>
Bitte beachten Sie , dass zwar sowohl HTML
als auch SVG
Namespaces standardmäßig als integrierte Erweiterungen zulässig sind, benutzerdefinierte Elemente jedoch keine SVG- Erweiterungen akzeptieren, sodass mit dem aktuellen /builtin
-Export praktisch nur HTML- Erweiterungen möglich sind.
Der ./dummy
ist hauptsächlich für SSR gedacht und bietet genau das gleiche Dienstprogramm zum Erweitern von Klassen, die nur ein statisches tag
-Feld enthalten.
In Kombination mit dem /tag
ist es möglich, 100 % SSR mit Lässigkeit durchzuführen und auf Distanz zu hydrieren.
import createRegistry from 'https://unpkg.com/nonchalance/dummy';import createTag from 'https://unpkg.com/nonchalance/tag';const {HTML} = createRegistry();class HelloDiv erweitert HTML.Div { connectedCallback() {console.log('hier bin ich'); }}// Erstellen Sie einen wiederverwendbaren Namespace für hydrateconst nmsp = {HelloDiv};// Erstellen Sie ein Tag transformatorconst tag = createTag(nmsp);// Stellen Sie sich stattdessen eine Serverantwort vor// Hinweis: Dieser Code dient nur zu Demozweckenconsole.log( tag`<!doctype html><script type="module">import createRegistry from 'https://unpkg.com/nonchalance/ce';const {HTML} = createRegistry();const nmsp = {};for (const el of document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; delete new nmsp[comp] (el);</script><HelloDiv></HelloDiv>` .verbinden('') .trimmen() .replace('const nmsp = {};',`const nmsp = { ${[...Object.entries(nmsp)].map( ([key, value]) => `${key}: ${ value}` ).join(',n')} };` ));
Der /jsx
Export (976 Bytes) akzeptiert eine zusätzliche Option createElement
und gibt eine jsx
-Funktion zurück, die als @jsx-Pragma verwendet werden kann, um neben allem anderen, das bereits standardmäßig in React oder Preact funktioniert, auch Klassen zu transformieren, die über die HTML- oder SVG- Registrierung erweitert wurden , einschließlich aller Funktionen, die /ce
diesen Klassen bietet: Benutzerdefinierte Elemente wie Lebenszyklus mit etwas Würze obendrauf:
Klassen erhalten in ihrem Konstruktor die Requisiten , die entlang des Elements übergeben werden, um Signale und andere Funktionen zu aktivieren oder alles zu verarbeiten, was bereits von standardmäßigen JSX- Komponenten verarbeitet werden kann.
Wenn der Konstruktor aufgerufen wird, ist das Element bereits mit seinen untergeordneten Elementen gefüllt, wodurch mögliche Spielereien vermieden werden, die bei benutzerdefinierten Standardelementen bekannt sind, wenn Klassen definiert/registriert werden, bevor das Dokument analysiert wird.
Ähnlich wie bei /builtin
extension ist es jedoch nicht möglich, new Component(props)
zu verwenden, aber es ist immer möglich, <Component {...props} />
.
Sehen Sie es in der Praxis mit React live auf CodePen.
Das DOM ist das DOM , egal wie viele Indirektionen dazwischen liegen. Ihr DX kann entsprechend den Framework-Funktionen variieren, aber wenn React das ist, was Sie suchen, gibt es eine kleine, aber elegante und ref
-basierte Möglichkeit, reguläre JSX-Knoten mit nonchalance/core oder nonchalance/ce zu fördern:
import referenziert aus 'nonchalance/ref';// gibt an, dass die Komponente als Referenz übergeben wird// Hinweis: Dies ist nur ein leichter Proxy, der Klassenintegrität gewährt// unabhängig von seiner Verwendung im Wildconst Component = referenziert(Klasse erweitert HTML. Div { Konstruktor(...args) {super(...args);this.addEventListener('click', console.log); }});ReactDOM.render( <div ref={Component}>klick mich</div>, document.body);
Das Dienstprogramm ref
könnte auch als Dekorateur verwendet werden, ohne dass sich dies auf Funktionen regulärer Nonchalance -Klassen auswirkt. Außerdem wird jedes Element nur einmal aktualisiert, sodass das Hinzufügen von Listenern oder Logik im Konstruktor sicher ist.
Sehen Sie sich diese Demo live auf Codepen an, um damit herumzuspielen.
Der /selector
Export (796 Byte) ermöglicht die Live-Definition eines beliebigen CSS-Selektors, sodass die zugehörige Klasse jedes Element, das diesem Selektor entspricht, automatisch aktualisiert.
Bitte beachten Sie, dass ein solcher Selektor so eindeutig wie möglich sein sollte, da es sonst zu Überraschungen kommen kann. Verwenden Sie bestimmte Klassen oder data-
, um Ihren Selektor am besten zu beschreiben.
import createRegistry from 'nonchalance/core';import { define } from 'nonchalance/selector';const { HTML } = createRegistry();const Special = define('[data-comp="special"]', Klasse erweitert HTML. Div { Konstruktor(...args) {super(...args);this.textContent = 'Ich bin etwas Besonderes!'; }});// enthält „Ich bin etwas Besonderes!“ Text einmal livedocument.body.innerHTML = `<div data-comp="special"></div>`;
Der ./tag
(188 Byte) ermöglicht die hydrationsfreundliche Transformation von Vorlagen.
Es kann als Zwischenwert hinter voll funktionsfähigen Template-Literal-Tags verwendet werden und eine Hydratation kann stattfinden, sobald diese Elemente im DOM landen.
import createRegistry from 'nonchalance/ce';import createTag from 'nonchalance/tag';const {HTML} = createRegistry();class HelloDiv erweitert HTML.Div { connectedCallback() {console.log('hier bin ich'); }}// einen wiederverwendbaren Namespace erstellen für hydrateconst nmsp = {HelloDiv};// ein Tag erstellen transformatorconst tag = createTag(nmsp);// schnell und schmutzig demodocument.body.innerHTML = tag`<HelloDiv />`.join( '');// Hydratation examplefor (const el of document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; el.dataset.comp löschen; // das Element einmal aktualisieren new nmsp[comp](el);}
Sehen Sie es live auf CodePen.
Ja . Sowohl /core
als auch /ce
-Exporte ermöglichen es, standardmäßig sowohl HTML- als auch SVG- Registrierungen zu erstellen:
import createRegistry from 'nonchalance/core';const {HTML, SVG} = createRegistry();class Circle erweitert SVG.Circle { Konstruktor(Optionen) {Object .assign(super(), Optionen) .setAttribute('fill', 'gold'); } set cx(value) { this.setAttribute('cx', value) } set cy(value) { this.setAttribute('cy', value) } set r(value) { this.setAttribute('r', value) }}document.querySelector('svg').append( neuer Kreis({cx: 100, cy: 100, r: 50}));
Sehen Sie es live auf Codepen.
Es ist auch möglich, einen beliebigen Namespace an createRegistry(options)
zu übergeben, am Beispiel von {MathML: "http://www.w3.org/1998/Math/MathML"}
.
Jeder Namespace, der eine Bedeutung für document.createElementNS
hat, ist zulässig. Es gibt keine Einschränkung hinsichtlich der Art der DOM -Elemente, die wir aktualisieren können.
Ich hatte zuvor eine sehr lange Antwort darauf, aber die Zusammenfassung ist, dass dieses Modul Standards verwendet, wie sie von W3C , WHATWG oder ECMAScript bereitgestellt werden, und dass es weniger als 1 KB benötigt, um überall zu funktionieren.
Dies ist kein Polyfill, sondern ein Dienstprogramm, das Ihnen beim Schreiben von Komponenten in der JS-Welt hilft, ohne sich Sorgen machen zu müssen, dass diese in Konflikt geraten, Tools erfordern oder nicht auf ein Zielprojekt übertragen werden können, das Sie mögen/benötigen/bevorzugen.
Kurz gesagt: Wenn Sie damit einverstanden sind, weniger als 1 KB Bytes hinzuzufügen, um universelle Komponenten sowohl für die Front-End- als auch für die Back-End-Welt bereitzustellen, haben Sie das richtige Modul gefunden?
Es gibt nichts Befreienderes, als ein sorgloses Kind zu sein, das gegen alle „ Tu das nicht! “-Denker im Dreck spielt.
Dieses Modul repräsentiert in gewisser Weise dieses Gefühl durch die Freiheit, die moderne JS-Funktionen bieten, und zeigt eine elegante, tragbare und superleichte Alternative zu der ständig zunehmenden Komplexität, die stattdessen von Browser-Anbietern und modernen Spezifikationen geboten wird Built-ins und bewahren sowohl die Einfachheit als auch die gute Zugänglichkeit, für die das Web bekannt ist.