Foto de mídia social de Andre Taissin no Unsplash
A maneira mais fácil de aumentar qualquer elemento integrado do DOM:
Não são necessários polyfills , todos os navegadores modernos simplesmente funcionam™️
é possível estender HTML , SVG ou qualquer outro namespace personalizado, como MathML , sem problemas
os elementos podem ser criados do zero ou atualizados sob demanda para uma hidratação elegante
cabe em 313 bytes (núcleo) ou 774 bytes com retornos de chamada de ciclo de vida de elementos personalizados incluídos (nada de novo para aprender)
Este exemplo pode ser testado ao vivo e apenas arranha a superfície do que é possível fazer com este módulo extremamente pequeno, mas poderoso.
// const createRegistry = require('nonchalance');import createRegistry from 'nonchalance/core';// a exportação padrão aceita opcionalmente um contexto `globalThis` like// para todos os ambientes que não possuem um DOM nativo./ / Esse registro pode expor nomes de classes qualificadas mais uma referência de `documento`// que será usada para criar elementos.const {HTML} = createRegistry();// estender qualquer elemento para criar ou atualizar elementos personalizados // de um registro que não compartilha nada com a classe de contexto global Password extends HTML.Input { // herdará uma tag estática field = "input" construtor(...args) {super(...args);this.type = 'senha'; } // evita scripts maliciosos recuperando facilmente qualquer senha obter valor() {retornar '********'; } // caso de demonstração: ainda usa o `valor` nativo quando a senha é definida definir valor (segredo) {super.valor = segredo; }}document.body.innerHTML = ` <form> <input type="text" name="user" placeholder="user"> <input type="password" name="password" placeholder="password"> <input type="submit"> </form>`;// atualize o elemento desejado via new ClassTypenew Password(document.querySelector('[type="password"]'));// ou crie uma nova instânciaconst secret = Object. atribuir (novo Senha, { nome: 'passar', valor: Math.random(), espaço reservado: 'senha de tempo de execução'});const form = document.querySelector('form');form.insertBefore(secret, form.lastElementChild);
Usando o módulo de função personalizada, é possível atualizar qualquer tipo de elemento sem nunca enfrentar o erro Illegal Constructor que aparece sempre que uma class extends HTMLSomethingElement {}
, quando tal classe não é definida globalmente como uma entrada no registro customElements
.
Não só não há nada compartilhado globalmente através deste módulo no contexto global, como todo trabalho extra estranho para ter qualquer extensão integrada funcionando é completamente desnecessário:
elementos novos ou passados sempre preservam sua cadeia raiz de protótipo
nenhum atributo extra ou nomes conflitantes podem acontecer
Além disso, como qualquer registro HTML pode ser criado por cada módulo ou projeto para compartilhar entre seus componentes, também é possível passar para tal criação de registro qualquer ambiente globalThis
falso ou simulado, com pelo menos um campo document
que expõe um createElementNS(namespace, tagName)
e uma ou mais classes que o projeto pretende testar, como HTMLElement
e/ou qualquer outra necessária para o sucesso do projeto.
No entanto, como o destino principal deste módulo é o DOM , a referência globalThis
é usada como padrão sensato, mas isso ainda não significa que algo seja compartilhado em torno dos registros criados por meio da exportação padrão.
Não . A forma como custom-function
funciona pode ser resumida da seguinte forma:
# 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
Em poucas palavras, criar um elemento por meio de new AnotherP
ou atualizar um elemento por meio de new AnotherP(liveParagraph)
simplesmente atualiza a cadeia de protótipos, sem exigir que o elemento saia do DOM ou altere sua natureza nativa, pois isso é preservado na cadeia de herança prototípica .
Resumo: os registros indiferentes simplesmente atualizam os elementos sem alterar sua natureza, exatamente da mesma forma que o builtin nativo estende o trabalho nos bastidores.
Sim , via /jsx
export ou através do /ref
.
Sobre /jsx Consulte Qual é a exportação de /jsx? seção.
Sobre /ref Consulte Qual é a exportação de /ref? seção.
A exportação /ce
atualiza automaticamente os elementos de maneira compatível com os connectedCallback
, disconnectedCallback
e attributeChangedCallback
das classes, juntamente com seu campo estático observedAttributes
.
O módulo usa uma versão ajustada do módulo como elemento personalizado que já funciona bem.
Veja esta demonstração ao vivo no codepen para ter uma ideia de como isso funciona.
Não . Falando metaforicamente, os elementos HTML têm um significado semântico e uma utilidade bem definida e desejada, uma vez ativa, da mesma forma que uma função JS será, para sempre, uma função JS , mesmo que Object.setPrototypeOf(() => {}, Number.prototype)
acontece... você consegue ver, ou concorda, o quão errado isso é?
Este módulo não quer (e provavelmente também não pode) se proteger contra o uso indevido de seus recursos, então certifique-se de que sempre que um elemento for atualizado, ele preserve sua cadeia de protótipo nativa nos bastidores, ou você estará sozinho lutando contra o DOM . .. o que é bastante inconveniente, se você me perguntar?
Resumindo, da mesma forma customElements.define('my-link', class extends HTMLDivElement {}, {extends: 'a'})
não faz sentido, este módulo confia que classes sem sentido de seus usuários serão evitadas.
Atualmente, a exportação padrão/principal para este módulo aponta para a mesma exportação /core
.
Como este módulo abre uma caixa de Pandora com sua simplicidade e tamanho de código vaporware, e principalmente porque ainda está atrás de uma versão 0.
semver, estou tentando considerar o que deve ser incluído no índice, e aqui estão alguns dos meus pensamentos:
não seria legal ter um módulo baseado em ESX que entendesse os componentes definidos dessa maneira?
não seria legal ter uma função pragma JSX que cria componentes através deste módulo?
não seria legal ter... (seu espaço reservado aqui)...?
Sim, seria legal, e se eu conseguir decidir como a exportação padrão deve ser nomeada, estou disposto a trazer esse nome, entre outras coisas, como entrada padrão para este módulo ... fique ligado ou por favor me dê pensamentos e dicas sobre como fazer isso
Até então, use exportações explícitas para garantir que atualizações futuras não atrapalhem sua lógica, e peço desculpas se as mudanças recentes lhe causaram problemas, mas tenho certeza de que você pode facilmente relacionar ou entender que isso foi para sempre!
Quando os elementos são atualizados à distância é possível que estes tenham alguma propriedade anexada que não teve a chance de passar por seus acessadores.
Este auxiliar simplesmente garante que as propriedades herdadas sejam removidas como chaves de elemento próprias para serem acionadas como acessadores logo em seguida.
importar createRegistry de 'nonchalance/ce';importar acessadores de 'nonchalance/acessadores';const {HTML} = createRegistry();class WithAccessors estende HTML.Div { construtor(...args) {acessadores(super(...args)); } obter valor() {console.log('obter valor', este._valor);retornar este._valor; } definir valor (_valor) {este._valor = _valor;console.log('definir valor', este._valor); }}// elemento div nativoconst div = document.createElement('div');div.value = 123;// atualizadonew WithAccessors(div);// re-checkconsole.log(div.value);
Veja ao vivo para testar mais.
A exportação /builtin
(248 bytes) é exatamente como /core
, exceto que não usa custom-function
nos bastidores, o que significa que:
não é possível new BuiltIn()
ou new BuiltIn(element)
pois isso geraria um erro, a menos que já não esteja registrado como customElement builtin extend
ele pode ser usado para automatizar o registro de componentes, conforme mostrado nesta demonstração ao vivo no CodePen
A única ressalva importante em torno desta exportação é que, por ser baseada em elementos personalizados padrão reais, o polyfill integrado pode ser necessário para Safari ou WebKit, por exemplo:
<!-- script de página superior apenas para Safari polyfill --><script>self.chrome ||self.netscape ||document.write('<script src="//unpkg.com/@webreflection/custom-elements -builtin"><x2fscript>');</script>
Observe que, embora os namespaces HTML
e SVG
sejam permitidos por padrão como extensões integradas, os elementos personalizados não aceitam extensões SVG , de modo que praticamente apenas extensões HTML são possíveis com a exportação /builtin
atual.
A exportação ./dummy
destina-se principalmente a SSR , fornecendo exatamente o mesmo utilitário para estender classes que carregarão apenas um campo tag
estático.
Combinado com o /tag
é possível fazer 100% SSR com indiferença e hidratar à distância.
importar createRegistry de 'https://unpkg.com/nonchalance/dummy';importar createTag de 'https://unpkg.com/nonchalance/tag';const {HTML} = createRegistry();class HelloDiv estende HTML.Div { conectadoCallback() {console.log('aqui estou'); }}// cria um namespace reutilizável para hydraconst nmsp = {HelloDiv};// cria uma tag transformerconst tag = createTag(nmsp);// imagina uma resposta do servidor // nota: este código é apenas para demonstraçãoconsole.log( tag`<!doctype html><script type="module">importar createRegistry de 'https://unpkg.com/nonchalance/ce';const {HTML} = createRegistry();const nmsp = {};for (const el of document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; (el);}</script><HelloDiv></HelloDiv>` .juntar('') .aparar() .replace('const nmsp = {};',`const nmsp = { ${[...Object.entries(nmsp)].map( ([chave, valor]) => `${chave}: ${ valor}` ).join(',n')} };` ));
A exportação /jsx
(976 bytes) aceita uma opção createElement
extra e retorna uma função jsx
que pode ser usada como pragma @jsx para transformar, entre tudo o mais que já funciona por padrão no React ou Preact , também classes estendidas via registro HTML ou SVG , incluindo todos os recursos que /ce
traz para essas classes: Elementos personalizados como ciclo de vida com um pouco de tempero no topo:
classes receberão em seu construtor os adereços passados ao longo do elemento, habilitando sinais, outras funções, ou manipulando qualquer coisa já possível de ser tratada por componentes JSX padrão.
quando o construtor é chamado, o elemento já estaria preenchido com seus filhos, evitando possíveis travessuras conhecidas com elementos customizados padrão quando as classes são definidas/registradas antes do documento ser analisado.
semelhante a /builtin
extend, porém, não é possível new Component(props)
mas é sempre possível <Component {...props} />
.
Veja como é usado na prática com React live no CodePen.
O DOM é o DOM , não importa quantas indireções existam entre eles. Seu DX pode variar, de acordo com os recursos do framework, mas se React é o que você procura, existe uma maneira pequena, porém elegante e baseada em ref
, de promover nós JSX regulares com indiferente/core ou indiferente/ce :
import referenced from 'nonchalance/ref';// indica que o Componente será passado como referência // Nota: este é apenas um Proxy leve que concede integridade à classe // independente de seu uso no wildconst Component = referenced(class extends HTML. Divisão { construtor(...args) {super(...args);this.addEventListener('clique', console.log); }});ReactDOM.render( <div ref={Component}>clique em mim</div>, documento.corpo);
O utilitário ref
também pode ser usado como decorador e sem afetar qualquer recurso das classes normais de indiferença . Além disso, cada elemento é atualizado apenas uma vez para que seja seguro adicionar ouvintes ou lógica no construtor.
Veja esta demonstração ao vivo no codepen para brincar com isso.
A exportação /selector
(796 bytes) permite a definição ao vivo de qualquer seletor CSS para que a classe relacionada atualize automaticamente qualquer elemento que corresponda a esse seletor.
Observe que esse seletor deve ser o mais exclusivo possível, caso contrário, surpresas poderão acontecer. Use classes ou atributos data-
específicos para descrever melhor seu seletor.
import createRegistry from 'nonchalance/core';import { define } from 'nonchalance/selector';const { HTML } = createRegistry();const Special = define('[data-comp="special"]', classe estende HTML. Divisão { construtor(...args) {super(...args);this.textContent = 'Eu sou especial!'; }});// conterá "Eu sou especial!" texto uma vez livedocument.body.innerHTML = `<div data-comp="special"></div>`;
A exportação ./tag
(188 bytes) permite a transformação de templates de forma amigável à hidratação.
Ele pode ser usado como valor intermediário por trás de tags literais de modelo totalmente capazes e a hidratação pode acontecer quando esses elementos chegam ao DOM .
importar createRegistry de 'nonchalance/ce';importar createTag de 'nonchalance/tag';const {HTML} = createRegistry();class HelloDiv estende HTML.Div { conectadoCallback() {console.log('aqui estou'); }}// cria um namespace reutilizável para hydrconst nmsp = {HelloDiv};// cria uma tag transformerconst tag = createTag(nmsp);// rápido e sujo demodocument.body.innerHTML = tag`<HelloDiv />`.join( '');// exemplo de hidrataçãofor (const el of document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; exclua el.dataset.comp; // atualiza o elemento uma vez novo nmsp[comp](el);}
Veja ao vivo no CodePen.
Sim . As exportações /core
e /ce
possibilitam a criação, por padrão, de registros HTML e SVG :
import createRegistry from 'nonchalance/core';const {HTML, SVG} = createRegistry();class Circle estende SVG.Circle { construtor(opções) {Object .assign(super(), opções) .setAttribute('fill', 'gold'); } definir cx (valor) { this.setAttribute ('cx', valor) } definir cy (valor) { this.setAttribute ('cy', valor) } definir r (valor) { this.setAttribute ('r', valor) }}document.querySelector ('svg').append( novo Círculo({cx: 100, cy: 100, r: 50}));
Veja ao vivo no codepen.
Também é possível passar qualquer namespace para createRegistry(options)
, usando {MathML: "http://www.w3.org/1998/Math/MathML"}
como exemplo.
Qualquer namespace que tenha um significado para document.createElementNS
é permitido, não há limitação quanto ao tipo de elementos DOM que podemos atualizar.
Eu tive uma resposta muito longa para isso antes, mas o resumo é que este módulo usa padrões fornecidos por W3C , WHATWG ou ECMAScript e requer menos de 1 KB para funcionar em qualquer lugar.
Isso não é polyfill, é um utilitário para ajudá-lo a escrever componentes no mundo JS e não se preocupar se eles entrarão em conflito, exigirão ferramentas ou não serão portáteis em qualquer projeto de destino que você goste/precise/prefira.
Resumindo, se você concorda em adicionar menos de 1K bytes para fornecer componentes universais para o mundo Front-End e Back-End, você acertou o módulo certo?
Não há nada mais libertador do que ser uma criança descuidada que brinca na lama contra todos os pensadores do tipo “ não faça isso! ”.
Este módulo de alguma forma representa esse sentimento através da liberdade que os recursos JS modernos oferecem, mostrando uma alternativa elegante, portátil e superleve à complexidade cada vez maior oferecida pelos fornecedores de navegadores e especificações modernas, todas necessárias para forçar os desenvolvedores a contornar a capacidade de simplesmente estender integrados e preservam a simplicidade e a grande acessibilidade pela qual a Web é famosa.