モバイル用に:targetや:checkedでつくるPure HTML5/CSS3なアコーディオンについて(追記 :checked版でAndroid4.x対応の改良コード)
Posted: Updated:
Pure HTML5/CSS3のロマン
ただしモバイルデバイスに限る。
しばしば話題にあがるHTML5/CSS3による気合戦法のアコーディオン(ないしクリッカブルなパーツ)を、モバイルデバイスでの動作をiOSおよびAndroidを中心に記述・検証してみました。
今のところメジャーなのは、:target戦法と:checked戦法だと思うので、それぞれ試しています。これらの擬似クラスを使って、状態制御を行いつつセレクタで表示状態のスタイルを適用します。
:target戦法
:target擬似クラス+アンカーリンクで状態を制御しながら、子要素としてコンテンツを表示させる方法。わりと手軽に使えるので、使い所さえ選べば良いのかも。
サンプル
コード全体
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type="text/css"> .content { height: 0; overflow: hidden; -webkit-transition: height 0.3s ease-in-out; transition: height 0.3s ease-in-out; } .pane:target .content { height: 50px; } </style> </head> <body> <div id="pane1" class="pane"> <a href="#pane1">Pane1</a> <div class="content"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </div> </div> <div id="pane2" class="pane"> <a href="#pane2">Pane2</a> <div class="content"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </div> </div> <div id="pane3" class="pane"> <a href="#pane3">Pane3</a> <div class="content"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </div> </div> </body> </html>
欠点
アンカーリンクを使っているため、アンカーの表示対象が画面内におさまっていないとページ内リンクとしてスクロールが発生します。スマートフォンのような限定的な解像度でスクロールが発生すると表示がトビます。さらに、transitionを使おうものなら、transitionEnd前にスクロールが発生してしまうためスクロール位置もひどいです。
実用性は高くありません。これなら素直にJSでクラスのadd/removeでトグルしたほうが良いでしょう。もちろんアニメーション部分は、使えるブラウザならCSSで表現すれば良いと思いますが。
:checked戦法
:checkd擬似クラスで状態を制御して、間接セレクタを利用してbody部分の表示・非表示を制御します。ブラウザの実装さえ追いつけば、わりと有力なのでは。iOS用にlabel要素のポインターイベントをonclick属性で励起させています。めんどい。
サンプル
コード全体
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type="text/css"> .body { height: 0; overflow: hidden; -webkit-transition: height 0.3s ease-in-out; transition: height 0.3s ease-in-out; } [type="radio"] { display :none; } #pane1:checked ~ #body1, #pane2:checked ~ #body2, #pane3:checked ~ #body3 { height: 50px; } </style> </head> <body> <input id="pane1" type="radio" name="accordion" value="1" /> <label id="label1" for="pane1" onclick="">Pane1</label> <div id="body1" class="body"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </div> <input id="pane2" type="radio" name="accordion" value="2" /> <label id="label2" for="pane2" onclick="">Pane2</label> <div id="body2" class="body"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </div> <input id="pane3" type="radio" name="accordion" value="3" /> <label id="label3" for="pane3" onclick="">Pane3</label> <div id="body3" class="body"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </div> </body> </html>
欠点
:checkedのpseudoは、モダンブラウザであれば対応しているはずですが、IE6-8やAndroid4.x未満で非対応です。挙動は悪くないのですが、ブラウザ要件がデスクトップ・モバイル共に厳しめなのが実用性を乏しくしています。
と言ってたら更に、Android 4.0.3のエミュレータで見てみたら:checkedと間接・隣接セレクタの組み合わせに問題(参考)がありました。
冗長になるので上のサンプルには反映させませんでしたが、次項のようなコードであれば上記の問題を回避して、iOSと同様の動作が得られます。事実上、iOS5.xとAndroid4.xに対応した:checked戦法の改良版です。
:checked戦法の改良版(Android 4.x対応)
:checked + div { font-size: inehrit; }
あたりがおまじないです。これで隣接div内のスタイル描画更新が発生するので、中のlabel要素やコンテンツ部分のdiv要素にもスタイル更新が届きます。
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type="text/css"> .body { height: 0; overflow: hidden; -webkit-transition: height 0.3s ease-in-out; transition: height 0.3s ease-in-out; } [type="radio"] { display :none; } :checked + div { font-size: inherit; } #pane1:checked ~ div #body1, #pane2:checked ~ div #body2, #pane3:checked ~ div #body3 { height: 50px; } </style> </head> <body> <input id="pane1" type="radio" name="accordion" value="1" /> <div> <label id="label1" for="pane1" onclick="">Pane1</label> <div id="body1" class="body"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </div> </div> <input id="pane2" type="radio" name="accordion" value="2" /> <div> <label id="label2" for="pane2" onclick="">Pane2</label> <div id="body2" class="body"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </div> </div> <input id="pane3" type="radio" name="accordion" value="3" /> <div> <label id="label3" for="pane3" onclick="">Pane3</label> <div id="body3" class="body"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </div> </div> </body> </html>