モバイル用に: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>