Web Components
@ Elo7

Luiz Corte Real

O que são?

(onde vivem, o que comem)

Hoje, no Globo Repórter

4 specs, na verdade

  • Custom Elements
  • Shadow DOM
  • HTML Imports
  • Templates

Custom Elements

<minha-tag />

document.registerElement('minha-tag', {...})

Custom Elements

document.registerElement({
	createdCallback() {...},
	attachedCallback() {...},
	detachedCallback() {...},
	attributeChangedCallback() {...}
});

Exemplo

<html-gl>
	<li class="broadcast">
		<div class="image_background"></div>
		<img src="./pic/boxes/box_argo.png">
		<p>
			Select me with DevTools
		</p>
		<p>
			<img src="./assets/img/imdb.png">
			<span>7.8<span>/10</span></span>
		</p>
	</li>
</html-gl>
Demo

Shadow DOM

Shadow DOM no inspetor do navegador

DOM dentro de DOM ⇒ encapsulamento!

Demo

Suporte

Suporte às specs de Web Components pelos navegadores. Apenas Chrome e Opera suportam tudo

Fonte: webcomponents.org

Bibliotecas

Bibliotecas que facilitam o desenvolvimento com WebComponents

Fonte: webcomponents.org

Mas...

não estamos aqui para falar de libs

ou de interfaces ultra sofisticadas

Web Components
@ Elo7

para solucionar problemas comuns

Uma breve história do tempo...

v1 ⇒ Raça

v2 ⇒ Bootstrap-like

v3 ⇒ Elo7 Bootstrap

Guidelines v3

elo7.com.br/bootstrap

Componentes no servidor

<desktop:sidebar links="${links}" active="Meu perfil"/>

<nav class="links">
	<a class="link active" href="#" title="Meu perfil">
		Meu perfil
	</a>
	<a class="link " href="#" title="Conta">
		Conta
	</a>
	<a class="link " href="#" title="Loja">
		Loja
	</a>
</nav>
<desktop:page css="saleConversation" jsFiles="...">
	<section class='order-detail'>
		<desktop:headerDetails user="..."/>
		<order:buyerDetail order="..."/>
	</section>
	<desktop:saleConversationBox>
		<desktop:saleConversation messages="..."/>
	</desktop:saleConversationBox>
</desktop:page>

Parece isolado

Nosso problema

Componentes de interface realmente isolados

Ideia #1: iframe

<iframe src="meucomponente.html"></iframe>

<iframe> é um container isolado!

Ideia #1: iframe

Componente com barra de rolagem

Problema: integração no layout

JS resolve, mas e o progressive enhancement?

Ideia #2: nomenclatura

elo7-chat__bubble no CSS

Elo7.Chat.Bubble no JS

...

Ideia #2: nomenclatura

Problemas

Triste Equipe triste

Suando frio Reaproveitamento difícil

Problemas inesperados Não resolve todos os problemas

Ideia #3: Shadow DOM

Shadow root no inspetor do navegador

Ideia #3: Shadow DOM

Problemas

  • Suporte dos navegadores
  • Não isola JavaScript
  • Depende de JavaScript

Ok, we can deal with that ?

Web Components podem ser progressively enhanced

<g-map lat="23.45" lon="67.89"></g-map>
não ❤ progressive enhancement
<img-slider>
	<img src="foto1.jpg">
	<img src="foto2.jpg">
	<img src="foto3.jpg">
</img-slider>

progressive enhancement

Demo

Web Components @ Elo7

<w7-component>
	<link rel='stylesheet' href='...'>
	<link rel='stylesheet' href='...'>
	<div class='conversation-content'>
		<ol class='conversation'>
			<!-- ... -->
		</ol>
	</div>
	<script src='...'></script>
	<script src='...'></script>
	<script src='...'></script>
</w7-component>
Arquitetura de front
Software Fora de Série - bit.ly/29Iw4Tu
var proto = Object.create(HTMLElement.prototype);
proto.attachedCallback = function() {
	var self = this;
	if (document.readyState === 'complete') {
		activateShadowDom(self);
	} else {
		document.addEventListener('DOMContentLoaded',
			function() {
				activateShadowDom(self);
			}, false);
	}
};
document.registerElement('w7-component', {
	prototype: proto
});
activateShadowDom(HTMLElement)
function activateShadowDom(el) {
	var root = el.createShadowRoot(),
		children = el.children;

	for (var i = 0, max = children.length; i < max; i++) {
		var child = children[i];
		if (isCSS(child)) {
			deactivateCSS(child);
			root.appendChild(transformCSSToImport(child));
		} else {
			root.appendChild(children[i].cloneNode(true));
		}
	}
}

O resultado

Sem JS
Com JS

CSS

w7-component {
	...
}
.attachment {
	...
}
							
Elo7
.bubble {
	...
}
.attachment {
	...
}
							
Componente

Não funciona sempre :(

CSS

w7-component {
	...
}
w7-component .attachment {
	...
}
:not(w7-component) .bubble {
	...
}
							
Elo7
.bubble {
	...
}
.attachment {
	...
}
							
Componente

No futuro

(quando todos suportarem Shadow DOM)
w7-component {
	...
}

+ (num <link> dentro de um <noscript>)

w7-component .attachment {
	...
}
:not(w7-component) .bubble {
	...
}

JS

No componente:

onComponentLoaded(['ajax', 'zoom'],
		function(component, ajax, zoom) {

	var root = component.get();
	var forms = root.querySelectorAll('form');
	// ...
	zoom.on('close', function() {
		component.remove();
	});
}

Isolamento do DOM

JS

No registrador do componente:

window.onComponentLoaded = function(deps, callback) {
	// 1. detectar tag do componente
	// 2. executar callback após carregamento das deps
};

JS

No registrador do componente:

window.onComponentLoaded = function(deps, callback) {
	var scriptTags = document.querySelectorAll(
			'w7-component > script:not([data-executed])');
	var component =
			new Component(scriptTags[0].parentNode);
	for (var i = 0; i < scriptTags.length; i++) {
		var scriptTag = scriptTags[i];
		scriptTag.setAttribute('data-executed', '');
	}
	// 2. executar callback após carregamento das deps
};

JS

No registrador do componente:

window.onComponentLoaded = function(deps, callback) {
	var scriptTags = document.querySelectorAll(
			'w7-component > script:not([data-executed])');
	var component =
			new Component(scriptTags[0].parentNode);
	for (var i = 0; i < scriptTags.length; i++) {
		var scriptTag = scriptTags[i];
		scriptTag.setAttribute('data-executed', '');
	}
	define(deps, function() {
		var loadedDeps = toArray(arguments);
		loadedDeps.unshift(component);
		callback.apply(null, loadedDeps);
	});
};
+ checagens de suporte a Web Components e setTimeouts

JS

Mas, e se, na mesma página...

<w7-component>
	<-- CSS, HTML -->
	<script src='doc-1.1.js'>
	</script>
</w7-component>
							
Componente B
(doc-amd v1.1)
<w7-component>
	<-- CSS, HTML -->
	<script src='doc-2.0.js'>
	</script>
</w7-component>
							
Componente B
(doc-amd v2.0)

Boom!

(escopo JS continua global)

Resumão

  1. Shadow DOM é massa
  2. Custom Elements é massa
  3. Web Components ❤ progressive enhancement
  4. Progressive enhancement é vida
  5. Ainda falta algo para escopo JS

Obrigado!

Luiz Corte Real

luiz.real@elo7.com @srsaude

Slides online: elo7.github.io/web-components-no-elo7