https://qiita.com/parada/items/70a9c78594d1328e5df5
の続きです
あらすじ
css gridを利用しhtmlからdivを駆逐しようとしたが、限界が来たのであった。
注意
Chromeでしか確認してないので、他ブラウザで見ている方々は微笑みましょう。
Web Componentsを使う
はいどうぞ。
<hitokoto-card role="article">
<img slot="image" src="http://placehold.jp/200x200.png">
<h1 slot="name">アニマル田 トン吉 さん</h1>
<h2 slot="osusume-head">この物件のおすすめポイント</h2>
<p slot="osusume">24時間ゴミ捨て</p>
<h2 slot="hitokoto-head">ひとこと</h2>
<p slot="hitokoto">吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕えて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思...</p>
<normal-button role="button" data-event-name="modal-open" data-modal-name="modal-tonkichi" slot="button">続きを見る</normal-button>
<normal-modal role="dialog" data-modal-name="modal-tonkichi" slot="modal">
<img slot="image" src="http://placehold.jp/400x200.png">
<p slot="text">吾輩は猫である。名前はまだ無い。<br>どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕えて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。<br>ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。<br>掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。この時妙なものだと思った感じが今でも残っている。<br>第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。その後猫にもだいぶ逢ったがこんな片輪には一度も出会わした事がない。のみならず顔の真中があまりに突起している。そうしてその穴の中から時々ぷうぷうと煙を吹く。どうも咽せぽくて実に弱った。<br>これが人間の飲む煙草というものである事はようやくこの頃知った。吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。<br>吾輩はここで始めて人間というものを見た。<br>しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕えて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。その後猫にもだいぶ逢ったがこんな片輪に</p>
</normal-modal>
</hitokoto-card>
どうですか、div無くなってるでしょう。
※他の要素では使ってるんじゃないの?と疑う方はCodePenをご覧ください
See the Pen with Web Components by haradabox (@haradabox) on CodePen.
コレを可能にしてくれるのがWeb Componentsなんですねえ。divとcssはどこに消えた?
js側に書いてあります。
class Card extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.innerHTML = `
<style>
.card {
border-radius: 8px;
display: grid;
grid-template-columns: 200px 1fr;
grid-template-areas:
"image main";
padding: 16px;
border: 2px solid;
background: #e3f6f5;
}
.card__image {
grid-area: image;
width: 200px;
height: 200px;
overflow: hidden;
border-radius: 8px;
}
.card__main {
grid-area: main;
padding-left: 16px;
}
.card__text {
margin-bottom: 16px;
}
.card__button {
display: flex;
justify-content: flex-end;
}
</style>
<div class="card">
<div class="card__image">
<slot name="image">
<img src="http://placehold.jp/200x200.png">
</slot>
</div>
<div class="card__main">
<heading-2>
<slot name="name">
匿名希望 さん
</slot>
</heading-2>
<heading-3>
<slot name="osusume-head">
おすすめポイント
</slot>
</heading-3>
<div class="card__text">
<slot name="osusume">
24時間ゴミ捨て
</slot>
</div>
<heading-3>
<slot name="osusume-head">
ひとこと
</slot>
</heading-3>
<div class="card__text">
<slot name="hitokoto">
吾輩は猫である。
</slot>
</div>
<div class="card__button">
<slot name="button"></slot>
</div>
<slot name="modal"></slot>
</div>
`;
}
}
customElements.define("hitokoto-card", Card);
ポイント
customElements.define
class Card extends HTMLElement {
}
customElements.define("hitokoto-card", Card);
と書くことで、カスタム要素が登録できます。
登録すると
The parser, whenever it sees the flag-icon tag, (略), which we then use to set the element's internal state and update its rendering (when appropriate).
というように、今回の例で行けば、htmlパーサーがhitokoto-card
タグに会うたびに(インスタンスが作成され)要素ごとの状態がセットされていく、、わけですね
Shadow DOM
Shadow DOM では、要素に追加されているが、その実際の子とは分離されている、スコープが設定された DOM ツリーを作成します。このスコープが設定されたサブツリーは Shadow ツリーと呼ばれます。
とあります。
今回は、
DOM | 書くタグ |
---|---|
通常のDOM | 意味のあるタグ |
Shadow DOM | div(意味のあるタグはslotで渡す) |
とすることで、divをShadowツリー内部に追いやっています。
まとめ
div(が通常のDOMツリーから)無くなった!やった!
appendix
Web Components の真面目な話(参考記事)
Web Components
https://developer.mozilla.org/ja/docs/Web/Web_Components
コンポーネントを構築
https://developers.google.com/web/fundamentals/web-components/
Web Components のカスタムイベントとメソッド
https://tech.basicinc.jp/articles/160
Shadow DOM 201 CSS とスタイリング
https://www.html5rocks.com/ja/tutorials/webcomponents/shadowdom-201/
Web Components のここが好き
- 独自のタグを作っちゃおうぜという気概
- styleのスコープが閉じられている
Web Components のここが好きになれない
slotの宣言がカスタム要素のトップレベルでしかできない
roleを書かずに済ませたかった。。。
例えばこうしたかった。
<hitokoto-card>
<article>
<img slot="image" src="http://placeimg.com/200/200/animals">
...
</article>
</hitokoto-card>
https://html.spec.whatwg.org/multipage/dom.html#attr-slot
にも「トップレベルじゃないと認識されないですよ」とは書いてないような、、(ネストされても認識される、とも書いてないが)
Experimental多し
怖くてChrome以外で確認していません。
Element.attachShadow()
https://developer.mozilla.org/ja/docs/Web/API/Element/attachShadow#Browser_compatibility
CustomElementRegistry.define()
https://developer.mozilla.org/ja/docs/Web/API/CustomElementRegistry/define#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E4%BA%92%E6%8F%9B%E6%80%A7
styleのカプセル化がどんな塩梅かよく分からない
reset.cssについて、今のところ効いてるっぽいのでShadowRootの外に一個置くだけで楽しています。
ただ、ShadowRootの外に置いたら効かないのが正、、のはず、、
https://www.html5rocks.com/ja/tutorials/webcomponents/shadowdom-201/
このページで h3 に定義されている他のスタイルルールが、デモのコンテンツ内部に入り込んでいません。これは セレクターが Shadow Boundary を超えられない ためです。
原因不明。
チラツキ
head内でフツーに読んでhtmlのパースをブロックするとか、asyncで読むけど適用されていない要素はローディング表示にするとか、綺麗に表示させる努力は必要そう