- 投稿日:2020-02-17T17:14:42+09:00
Shadow DOM の特性を知って、使いこなすコツ
最初に
この記事では普通の DOM を 「Light DOM」 と呼び 「Shadow DOM」 と明確に区別して扱います。
Shadow DOM とは?
- Shadow DOM は Web Components の標準仕様の一部ではありますが実はブラウザーには結構前から実装されているものです。
<video>
タグなどは Shadow DOM を使っているが Web Components の標準仕様に入るまでは自由に使うことはできませんでした。
Shadow DOM とは? (2)
そして Edge も Chromium の仲間入りをしたことで Shadow DOM も含めての全ての Web Components の標準仕様がモダーンなブラウザーで使えるようになりました!! ?
Shadow DOM とは? (3)
- Shadow DOM を使えば Light DOM から独立した DOM の木構造を作成することができます
- そしてそれを Light DOM の木構造に追加すると一緒にレンダーできます。
- (言葉だけでは伝えきれないのでこれを見てください。)
Shadow DOM のいいところ
- DOM の木構造が独立していると言うのは Shadow DOM のキーポイントです。
- これを生かすと他のやり方では完璧にできないことができるようになります。
Shadow DOM のいいところ (2)
- Shadow DOM にある Node には
document.querySelector()
などではアクセスできないので他のコードからの動作を阻止できます。
Shadow DOM のいいところ (3)
- Shadow DOM 内の CSS は全て scoped CSS になっているので大きな利点はいくつあります
- Shadow DOM 内の CSS は Light DOM に影響しません。
- Light DOM 内の CSS は Shadow DOM に影響しません。*
- これらのお陰で Shadow DOM 内の CSS では
button
や#someid
みたいなすごく簡単な CSS Selector を書けるようになります。 これがあるからこそ「Shadow DOM は CSS を単純にする」と言えます、全ての範囲が決まっています。
Shadow DOM のいいところ (4)
- 先ほどの説明にアスタリスクがあったと気づいたと思います。
- 実を言うと Light DOM から Shadow DOM を全くカスタマイズできなくすると使い道がすごく限られて行くのです。
- なので条件次第では Light DOM から Shadow DOM をカスタマイズすることができます。
CSS がらみのものになると言葉だけでは伝え切れないことが多くなるのでここからがちょっとしたクイズをしようと思います!
準備はいいですか?
Q1 〜どの CSS が受け継がれますか?〜
これが最初の Shadow DOM のマークアップ
<style> .some-class { font-family: Arial; font-size: 14px; color: blue; } </style> <div> This is some text </div> <div class="some-class"> And here's some more text </div>そしてこれは Light DOM のマークアップ
<style> .container { font-family: Helvetica; font-size: 16px; color: green; padding: 2em; } .container .some-class { color: red; } </style> <div class="container"> <!-- 先ほどのShadow DOM はこのWeb Componentの中にあります --> <question-one></question-one> </div>さて、これはどうレンダーされますか? ネタバレは禁止です。?
答え
なんで?
- Shadow DOM にあるノードは特別なスタイルは掛けられているわけではありませんので
font-family
やcolor
など普通なら受け継がれるプロパティは Shadow DOM の親から受け継がれます。- しかし、見ての通り
some-class
ではfont-family
とfont-size
とcolor
を指定しているからそこの部分はそのプロパティを受け継がない。- Light DOM にある
.container .some-class
の selector は Light DOM から Shadow DOM に直接影響できないからなにもしないことに気づいたのなら追加点をあげますよ。
Q2 〜host はカスタマイズできますか?〜
次の Shadow DOM はこれです
<style> /* この selector は shadow DOMの元に当てられます */ :host { display: block; width: 30px; border: 1px dotted black; } </style> <div>1</div> <div>2</div> <div>3</div>そしてこれは今回の Light DOM
<style> .some-class { display: flex; flex-flow: row-reverse; width: 100%; border: 1px solid red; } </style> <question-two></question-two> <question-two class="some-class"></question-two>またしても、これはどうレンダーされますかね? 準備はいいですか?
答え
なんで?
- Shadow DOM の子供へのスタイルとは違って host レベルのスタイルは Light DOM から影響できます。
- 要は host のため Shadow DOM で定義するスタイルはデフォルトでしかないのでとあるスタイルを変えて欲しくなければ Shadow DOM 内にコンテナ用のノードを追加してそっちにスタイルを当てた方がいいです。
Q3 〜CSS 変数は適用されますか?〜
Shadow DOM はこっちになります
<style> div { color: var(--my-color, blue); } </style> <div> Some text </div>そして Light DOM はこれです
<style> .custom-color { --my-color: red; } </style> <question-three></question-three> <question-three class="custom-color"></question-three>この場合は色がどうなるのでしょうか?
答え
なんで?
- CSS カスタムプロパティ (ほとんどの人が CSS 変数と呼びます)は Shadow DOM 内のコンテンツを自由にスタイルを掛けられるものの一つです。
- もちろん、この問題の通り Shadow DOM の CSS でどの変数をどこで使うかを定義しないと適用されないがこれを使うと簡単にカスタマイズができるようになります。
CSS 変数をもっと詳しく知りたい人はこちらの記事を読むといいです。
Q4 〜コンポネントの子供はどうなりますか?〜
次の質問の Shadow DOM が来ます!
<style> /* ::slotted(selector) は指定された slot に追加されたノードに適用されます */ header ::slotted(*) { text-decoration: underline; } article ::slotted(div.special) { color: turquoise; } </style> <section> <header> <h3><slot name="header">Default Header</slot></h3> </header> <article> <slot></slot> </article> </section>Light DOM もここにあります!
<style> .some-class .header { color: red; } .some-class div { color: mediumvioletred; } </style> <question-four> <div>1</div> <div class="special">2</div> <div>3</div> </question-four> <question-four class="some-class"> <div>4</div> <div class="special">5</div> <div>6</div> <span class="header" slot="header"> Second Element Header </span> </question-four>これは少し内容が濃い目なのでゆっくり解いてみてください。
答え
なんで?
- これは初めて Shadow DOM に触れる機会ならこれを解けなくても全然問題ないです、これからがんばって理由を説明してみます。
- Shadom DOM には特別な
<slot>
と言うタグが存在しましてこれを使うとこの Shadow DOM の子ノードがどこに追加されるかを指定できます。- React の
children
か Vue の slot を使ったことがあるならそれに近いものです。Vue の slot は実は Shadow DOM のを真似ていたりします。
- そして Shadow DOM 内にある CSS では追加された子ノードを
::slotted()
を使ってスタイルを当てられます。- しかし、見ての通り、先ほどの host レベルのスタイルと同様、
::slotted()
を通して当てるスタイルはデフォルトでしかなく Light DOM から当てられているスタイルとマージされますが Light DOM のものの方は優先されます。
Q5 〜部分的なカスタマイズは可能ですか?〜
ラストの Shadow DOM です!!
<style> :host { display: flex; } img { width: 64px; height: 64px; } div { font-family: Arial; color: seagreen; } </style> <img part="avatar" src="https://via.placeholder.com/64x64.jpg?text=Avatar" /> <div part="name"> Some Name </div>そしてラストの Light DOM です
<style> question-five.custom-part::part(avatar) { border-radius: 50%; } question-five.custom-part::part(name) { font-family: Verdana; color: mediumpurple; } </style> <question-five></question-five> <question-five class="custom-part"></question-five>最後の問題はどうレンダーされますか?
答え
なんで?
- ここからは最先端技術の領域に入ります、前の 4 つの質問は全てのモダーンブラウザーで使える技術ですが、この最後の質問は Safari ではまだ対応されていない(すでに Technology Preview の方に入っているからもう少ししたら出ると思います)。
- ちなみに Chromium ベースの Chrome や Edge や Opera などと Firefox はすでに対応しています。
- このデモでは Shadow Parts の標準仕様を使います、その名の通り、Shadow DOM の中にパーツを定義することができます。
- パーツを定義した場合、Light DOM から
::part()
を使ってスタイルを当てることができます。- ここまで来れば今から言うことは予想できると思いますが、Shadow Parts も slot や host レベルと同じデフォルト用のスタイルでしかなく Light DOM から当てるスタイルの方が優先されます。
最後に
Shadow DOM はとても協力な標準仕様ですけど一見からじゃ見えない個性を持っています。
このクイズを通して「Shadow DOM の仕組みが分かった!」とか「これで Web Components の使い方がもっと良くなった!」とか「これで Web Components をもっと良く作れる!」とかになっていれば幸いです。
もっと知りたい人は
デモのコード
記事
- 投稿日:2020-02-17T10:18:47+09:00
画像の代わりにaltのテキストを表示する
スマホの時だけ画像非表示にしてくださいってたまに言われます
本当はスマホ用の画像を用意した方がいいのですが時間や予算がまったくなかったりするときのための方法
元の状態
See the Pen
wvaGyLX by sphenisc (@sphenisc)
on CodePen.
完成版
See the Pen
ZEGWrgd by sphenisc (@sphenisc)
on CodePen.
imgタグには疑似要素を付けられないのでimgの上のh4にafterを付けてJSで imgのalt → afterのdata-label へと中身を移してからCSSのafterのcontentとして表示しています。
alt属性はそもそも画像が表示できない場合の代替テキストを書くための場所なのでSEO的にも問題なしJSなしでやりたい場合
See the Pen
jOPqzPd by sphenisc (@sphenisc)
on CodePen.
- 投稿日:2020-02-17T04:12:34+09:00
0から分かるCSSだけでフェードアウトしてみた
はじめに
お久しぶりです!今回は小ネタみたいなものですが、サイトに取り入れると少しかっこよくなる
「CSSによるフェードアウト」です!!
とても簡単なのでちらっと見ていってください(^^)/この記事を読むとどうなるの?
→CSSでのフェードアウトの方法が分かる
どうやってやるの
①HTMLを作る(当たり前ですね)
何をするにしてもCSSを使うので、まずはHTMLを適当に作りましょうね。
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <!--CSSの読み込みを忘れないようにしましょうね--> <link rel="stylesheet" href="stylesheet.css"> <title>Document</title> </head> <body> <div class = fadeout> <img src = "画像やgifのURL"> </div> </body> </html>②アニメーションを使おう
CSSでアニメーションを使うのであれば2つのことをする必要があります。
以下のコードを見ながら理解していきましょう!stylesheet.css.fadeout { animation: fadeOut 2s; /*keyframesで命名したものを使う。2秒間で消える*/ animation-fill-mode: both; /*0%の時と100%の時の状態を保つ*/ } @keyframes fadeOut { 0% { opacity: 1; /*初めに存在する*/ } 100% { opacity: 0; /*最後に消える*/ } }.fadeoutの部分について
animation: fadeOut 2s;
ここがアニメーションの全てです!
読んでの通り、アニメーションとして二秒間でフェードアウトしますよっていうコードです。
animation-fill-mode: both;
これはフェードアウトする前とした後の状態をずっと継続させるというコードです。
これをかかないとフェードアウトしても消えた状態が維持されません。ここまでは大丈夫でしょうか?
これだけでできたら楽勝なのですが流石にそこまで単純ではありません。
fadeOut
の部分が何を表すのかを書かなければなりません。keyframes部分について
keyframesというものを使ってどのようなアニメーションをさせるのかを決めないといけないのです。
次のように書きます@keyframes 任意の名前 { 0% { opacity: 1; } 100% { opacity: 0; } }今回はフェードアウトをするのでfadeOutという名前をつけたというだけです。
0%と100%というのは「最初」と「最後」を表しています。
opacityは「存在するかしないか」を表していて、1は存在、0は存在しないということです。最後に
久しぶりに書いた記事ですがいかがだったでしょうか
皆さんもフェードアウトを使ってサイトをかっこよくしましょう!!
- 投稿日:2020-02-17T00:43:07+09:00
【jQuery】hamburgerMenu メモ
メモ
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>hamburgerMenu</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <header> <div class="navToggle"> <span></span><span></span><span></span> </div> <div id="globalMenuSp"> <ul id="manu" class="menu"> <li><a href="#cando" target="_top" class="menu_cando">Home</a></li> <li><a href="#application" target="_top" class="menu_application">About</a></li> <li><a href="#manual" target="_top" class="menu_manual">Skills</a></li> <li><a href="#inquiry" target="_top" class="menu_inquiry">Portfolio</a></li> </ul> </div> </header> <script src="js/jquery-3.4.1.min.js"></script> <script src="js/hamburger.js"></script> </body> </html>style.css#globalMenuSp { position: fixed; z-index: 4; top: 0; left: 0; color: #000; text-align: center; transform: translateY(-100%); transition: all 0.6s; width: 100%; padding-top:-100px; } #globalMenuSp ul { background: #20b2aa; margin: 0 auto; padding: 0; width: 100%; display:inherit; } #globalMenuSp ul li { font-size: 1.1em; list-style-type: none; padding: 0; width: 100%; border-bottom: 1px solid #fff; } #globalMenuSp ul li:last-child { padding-bottom: 0; border-bottom: none; } #globalMenuSp ul li a { display: block; color: #fff; padding: 1em 0; } #globalMenuSp.active { transform: translateY(0%); } /*ハンバーガー用CSS*/ .navToggle { display: block; position: fixed; right: 13px; top: 12px; width: 42px; height: 51px; cursor: pointer; z-index: 5; text-align: center; } .navToggle span { display: block; position: absolute; width: 30px; border-bottom: solid 3px #20b2aa; -webkit-transition: .35s ease-in-out; -moz-transition: .35s ease-in-out; transition: .35s ease-in-out; left: 6px; } .navToggle span:nth-child(1) { top: 9px; } .navToggle span:nth-child(2) { top: 18px; } .navToggle span:nth-child(3) { top: 27px; } .navToggle span:nth-child(4) { border: none; color: #eee; font-size: 9px; font-weight: bold; top: 34px; } .navToggle.active span:nth-child(1) { top: 18px; left: 6px; border-bottom: solid 3px #eee; -webkit-transform: rotate(-45deg); -moz-transform: rotate(-45deg); transform: rotate(-45deg); } .navToggle.active span:nth-child(2), .navToggle.active span:nth-child(3) { top: 18px; border-bottom: solid 3px #eee; -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); transform: rotate(45deg); }jquery-3.4.1.min.jsを別途読み込む必要がある
hamburger.js$(function() { $('.navToggle').click(function() { $(this).toggleClass('active'); if ($(this).hasClass('active')) { $('#globalMenuSp').addClass('active'); } else { $('#globalMenuSp').removeClass('active'); } }); }); $('#manu a[href]').on('click', function(event) { $('.navToggle').trigger('click'); });