ノラWEB屋 野良人(のらんど)- 個人営業のWEB屋さん

レスポンシブデザインでスマホだけアコーディオン

2017年7月6日

PCでは全開で表示、スマホではアコーディオンで表示、ということをやります。

    スマホだけアコーディオンで開閉させる

    サンプルとしては岡山民謡太鼓保存会のグローバルナビがそうです。

    サンプルコード

    HTML
    <h3 class="title">クリックして開閉</h3>
    <ul class="toggle">
    	<li>なんらか</li>
    	<li>なんらか</li>
    	<li>なんらか</li>
    </ul>
    
    CSS
    .title{
    	pointer-events: none;
    }
    @media screen and (max-width: 640px){
    	.title{
    		pointer-events: all;
    	}
    	
    	.toggle{
    		display: none;
    	}
    }
    
    jQuery
    $(function(){
    	$('.title').click(function(){
    		$(this).next().slideToggle();
    	});
    });
    

    CSSの装飾は省いて動作に最低限必要な部分だけ載せてあります。

    デモ1 ※ブラウザ幅をアレしてご確認ください

    ポイント

    • jQueryのslideToggleメソッドで開閉のアクションを行います
    • クリックする.titleセレクタにpointer-eventsを設定して、PC表示ではクリックできないようにしておくことでスマホのみアコーディオンで開閉するようにします。

    狭いブラウザ幅でクリックして閉じてからブラウザ幅を変更したら閉じっぱなしになる問題への対応

    あんまり想定されない操作ではありますが…

    • PCでブラウザ幅をブレイクポイント以下に狭める
    • クリックでアコーディオンパネルを開→閉とする(この時jQueryによって要素のスタイル属性にdisplay:noneが追加される)
    • ブラウザ幅をブレイクポイント以上に広げる

    こういう操作をすると、PC表示なのにアコーディオンパネルが閉じたまま開くことができないという状態になってしまうため対応します。

    jsでリサイズのたびに判定して対応

    jQuery
    $(window).on('resize', function() {
    	if( 'none' == $('.title').css('pointer-events') ){
    		$('.toggle').attr('style','');
    	};
    });
    
    デモ2 ※ブラウザ幅をアレしてご確認ください

    ブラウザ幅の変更があったときに、CSSをみてPC表示のスタイルであればstyle属性を空にする処理を追加します。

    べつにjs側でブラウザ幅を取得して条件分岐してもいいんですが、CSSを発火条件にしておくと管理がしやすいのでこうしています。

    CSSのみでアコーディオン

    最初からjsを使わずにCSSだけで実装して上記の問題を回避するアプローチ。

    HTML
    <label class="title" for="box1">クリックして開閉</label>
    <input type="checkbox" id="box1">
    <ul class="toggle">
    	<li>なんらか</li>
    	<li>なんらか</li>
    	<li>なんらか</li>
    </ul>
    
    CSS
    .title{
    	pointer-events: none;
    }
    
    @media screen and (max-width: 640px){
    	.title{
    		pointer-events: all;
    	}
    	
    	.toggle{
    		height: 0;
    		padding: 0 8px;
    		transition: .2s;
    		overflow: hidden;
    	}
    	
    	input:checked + .toggle{
    		height: auto;
    		padding: 8px;
    	}
    }
    
    デモ3 ※ブラウザ幅をアレしてご確認ください
    • 開閉の挙動にはチェックボックスを使用し、チェックの有無で開閉をコントロールするよくあるやつです。
    • 開閉の動作はtransitionプロパティを使って、アニメーションぽく見せています。
    • 0→autoでtransition効果を付けることはできないため、heightが決まっていない場合はheightをビヨーンさせることができません。
    • ここでは上下のpaddingにtransitionを付けることでパネル全体が伸縮しているように見せています(ただし中身が長くなるとアニメーション感はなくなってしまう)。

    以下の記事を参考にさせて頂きました。

    CSSだけでサイズ可変・スマホ対応のアコーディオン | Webデザインラボ会

    追記:CSSのみでアコーディオンのベストプラクティスっぽいもの

    この記事を書いた直後にすげーいい解説記事がアップされていました。

    高さ可変!CSSアニメーションでなめらかアコーディオン | それいけ!フロントエンド
    • 高さを構築しているプロパティひとつひとつをアニメーション対象とする」という方法で、heightが決まっていない場合でもCSSのみで上手くビヨーンさせています。
    • 上記記事ではJSでclassの切替だけ行い、あとはCSSアニメーションで処理しています。すべてCSSのみでやる場合は、前項のサンプルコードといいとこ取りする必要があります。

    サンプルコード

    CSS
    .title{
    	pointer-events: none;
    }
    
    @media screen and (max-width: 640px){
    	.title{
    		pointer-events: all;
    	}
    	
    	.toggle{
    		height: 0;
    		padding: 0 8px;
    		transition: .2s;
    	}
    	
    	input:checked + .toggle{
    		height: auto;
    		padding: 8px;
    	}
    	
    	.toggle li{
    		padding: 0 8px;
    		line-height: 0;
    		visibility: hidden;
    		opacity: 0;
    		transition: 
    			padding .2s,
    			line-height .2s,
    			visibility .1s,
    			opacity .1s;
    	}
    	
    	input:checked + .toggle li{
    		padding: 8px;
    		line-height: 1.5;
    		visibility: visible;
    		opacity: 1;
    	}
    }
    

    ※HTMLは上のデモ3の項目ものと同じなので省略

    デモ4 ※ブラウザ幅をアレしてご確認ください

    デモ3と比べて動きが滑らかになり、要素の数が多くとも問題なくなりました。美しいことは良いことですね。

    まとめ的なこと

    • ブラウザ幅を変更する操作とかも無視して実装の手軽さを重視→デモ1のやつ
    • ブラウザ幅を変更する操作を考慮しつつjsでなるべく簡単に実装→デモ2のやつ
    • さらに手をかけて処理を軽くする→デモ4のやつか、その元ネタ
    • HTMLをいじれない場合(CMSとかから吐かれる既定のHTMLをCSSやJSでなんとかする案件)の場合→デモ1or2または4の元ネタ
    このエントリーをはてなブックマークに追加