LoginSignup
6
9

More than 5 years have passed since last update.

Web Componentsに触れる

Last updated at Posted at 2018-10-14

0. WebComponentsって?

Web Components は、再利用可能なカスタム要素を作成し、ウェブアプリの中で利用するための、一連のテクノロジーです。

引用元:https://developer.mozilla.org/ja/docs/Web/Web_Components

一連のテクノロジーの主な中身
- HTML Templates
- HTML Imports
- Shadow DOM
- Custom Elements

これらを一つづつ、さらっと表面をなぞっていく
※見やすくするためにhtmlでは、html要素とhead要素を使っていません

1. HTML Templates

再利用可能なHTML要素

index.html
<body>
  <template>
    <h1>Hello HTML Templates</h1>
    <img src="">
  </template>
  <script src="main.js"></script>
</body>

この時点では、なにも表示されない

JSでtemplateをクローンしてbodyにappendChildする

main.js
const TEMPLATE = document.querySelector('template');
const CLONE = document.importNode(TEMPLATE.content, true);
document.body.appendChild(CLONE);


template自体を表示させるんじゃなくて、いったんcloneして生成するのがキモ。
img src(unknow)になってるのでsrcをつけてみる

main.js
const TEMPLATE = document.querySelector('template');
TEMPLATE.content.querySelector('img').src = 'https://dummyimage.com/99';
const CLONE = document.importNode(TEMPLATE.content, true);
document.body.appendChild(CLONE);

template要素.contentに対してquerySelectorする

2. HTML Imports

外部HTMLを読み込む

foo.html
<h1>Hello HTML Imports</h1>
index.html
<body>
  <link rel="import" href="foo.html">
  <script src="main.js"></script>
</body>
main.js
const IMPORT = document.querySelector('link[rel="import"]').import;
const HEADER = IMPORT.querySelector('h1');
document.body.appendChild(HEADER.cloneNode(true));

インポートしたfoo.htmlからh1を取り出してbodyにappendChildしている。

HTML TemplateをHTML Importsする

template.html*
<template>
  <h1>Hello HTML Templates from template.html</h1>
  <img src="">
</template>
index.html
<body>
  <link rel="import" href="template.html">
  <script src="main.js"></script>
</body>
main.js
//HTML imports
const IMPORT = document.querySelector('link[rel="import"]').import;

//HTML template
const TEMPLATE = IMPORT.querySelector('template');
TEMPLATE.content.querySelector('img').src = 'https://dummyimage.com/99';

//クローンを作成してbodyに生やす
const CLONE = document.importNode(TEMPLATE.content, true);
document.body.appendChild(CLONE);

※import元のhtmlの名前空間(idやclass)にはスコープが生成されるが、styleはそうでないので注意

template.html
<template>
  <style>
    h1 {
      color: red;
    }
  </style>
  <h1>Hello from template.html</h1>
</template>
index.html
<body>
  <h1>Hello from index.html</h1>
  <link rel="import" href="template.html">
  <script src="main.js"></script>
</body>
main.js
const IMPORT = document.querySelector('link[rel="import"]').import;
const TEMPLATE = IMPORT.querySelector('template');
const CLONE = document.importNode(TEMPLATE.content, true);
document.body.appendChild(CLONE);

index.htmlにも、template.htmlのスタイルが適用される。

styleの名前空間を汚したくない!
→Shadow DOMで解決

3. Shadow DOM

カプセル化してくれるやつ

index.html
<body>
  <h1>Hello</h1>
  <script src="main.js"></script>
</body>
main.js
const SHADOW = document.querySelector('h1').createShadowRoot();
SHADOW.textContent = 'Hello Shadow DOM';


一見何の変哲もないが、

ShadowDOMは表示だけを担当し、JSから参照する場合は、元々の値が返される。
使いみちは?
→ShadowDOMはstyleを含めたスコープを持つ(カプセル化と言う)
ということで、

HTML TemplateをHTML ImportsしてShadowDOMで出力する

template.html
<template>
  <style>
    h1 {
      color: red;
    }
  </style>
  <h1>Hello from template.html</h1>
</template>
index.html
<body>
  <h1>index.html</h1>
  <div id='for_shadow'></div>
  <link rel="import" href="template.html">
  <script src="main.js"></script>
</body>  
main.js
//HTML imports
const IMPORT = document.querySelector('link[rel="import"]').import;

//HTML template
const TEMPLATE = IMPORT.querySelector('template');
const CLONE = document.importNode(TEMPLATE.content, true);

//Shadow DOM
const SHADOW = document.querySelector('#for_shadow').createShadowRoot();
SHADOW.appendChild(CLONE);

//DOMアクセス
console.log(document.querySelector('#for_shadow').shadowRoot);
console.log(document.querySelector('#for_shadow::shadow h1'));


無事、styleの名前空間を分離することができた

4. Custom Elements

タグを自作

いきなり、全部の合わせ技

template.html
<template>
  <h1>Hello from template.html as x-card</h1>
</template>
index.html
<body>
  <link rel="import" href="template.html">
  <script src="main.js"></script>
</body>
main.js
//Custom Elementsを定義
const card = class extends HTMLElement {
  connectedCallback() { //お約束
    //html import
    const IMPORT = document.querySelector('link[rel="import"]').import;

    //html template
    const TEMPLATE = IMPORT.querySelector('template');
    const CLONE = document.importNode(TEMPLATE.content, true);

    //Shadow DOM
    this.createShadowRoot().appendChild(CLONE);
  }
};

//x-cardタグにcardクラスを登録
window.customElements.define('x-card', card);

//bodyに生成
document.body.appendChild(new card());

5. ブラウザの対応状況

2018/10/14時点ではブラウザの対応状況がマチマチなので注意
HTML ImportsとShadow DOMにいたっては草案(Working Draft: WD)段階

https://caniuse.com/#feat=template

https://caniuse.com/#feat=imports

https://caniuse.com/#feat=shadowdomv1

https://caniuse.com/#feat=custom-elementsv1

6
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
9