[js]レスポンシブ対応のヘッダーメニューを考える会

自分の中にある現状の最適解。

動作イメージ

汎用性のあるものとする。

まずメニューが並んでいて、一部下層メニューになってるものもある。

全部表示すると膨大になる場合があるから、下層メニューはアコーディオン的な機能をもたせる。

アコーディオンはPC表示だとマウスオーバーで、SP表示だとクリックで展開することとする(タブレットは一旦無視)。

HTML、CSS

極力あっさりめで。

HTMLはこんな感じ。

<header>
    <nav>
        <ul class="nav">
            <li><a href="">****</a></li>
            <li><a href="">****</a></li>
            <li><a href="">****</a></li>
            <li class="sub">
                <button type="button">****</button>
                <ul>
                    <li><a href="">****</a></li>
                    <li><a href="">****</a></li>
                    <li><a href="">****</a></li>
                </ul>
            </li>
            <li class="sub">
                <button type="button">****</button>
                <ul>
                    <li><a href="">****</a></li>
                    <li><a href="">****</a></li>
                    <li><a href="">****</a></li>
                </ul>
            </li>
        </ul>
    </nav>
</header>

リンクを仕込まない場合は<a>じゃなくて<button>の方が良いみたいなことを以前どっかで読んだのでその通りにしてある。この際、type=”button”を忘れると画面遷移が起きるので注意。

cssは要点だけ。

.sub > ul{ display:none;}

下層メニュー部はjsで開閉させるからデフォで非表示にしておく。

jsで非表示を仕込んだ場合、ページ読み込み時のjsが効いていないタイミングで展開されてダサいことになる。極力cssで済ませられる部分はcssで書くようにしたほうがいい。

js(jQuery)

こう。

$(function(){
    $('header li.sub button').on('mouseover', function(){
        if(!window.matchMedia('(max-width:767.98px)').matches){
            if(!$(this).hasClass('active')){
                $('header li.sub button').removeClass('active');
                $('header li.sub ul').fadeOut();
                $(this).addClass('active');
                $(this).next('ul').slideDown();
            }
        }
    }).on('click', function(){
        if(window.matchMedia('(max-width:767.98px)').matches){
            $(this).next('ul').slideToggle();
            $(this).toggleClass('active');
        }
    })
    $('header').on('mouseleave', function(){
        if(!window.matchMedia('(max-width:767.98px)').matches){
            setTimeout(function(){
                $(this).next('ul').slideUp();
                $(this).removeClass('active');
            },1500);
        }
    })
})
$(window).on('load resize', function(){
    if(!window.matchMedia('(max-width:767.98px)').matches){
        if($('header').hasClass('sp')){
            $('header').removeClass('sp');
            $('header li.sub button').removeClass('active');
            $('header li.sub ul').hide();
        }
    }else{
        if(!$('header').hasClass('sp')){
            $('header').addClass('sp');
        }
    }
})

書いてあるとおりといえばそれまでなんだけどね。

まず.on()を発火条件ごとに分けて、更にブラウザ幅を拾ってPC/SPの条件分岐を付けてある。
これによってSPなのにPCの動作がある、みたいな暴発は防げる。

classを付けてるのは地味にポイントで、単純に表示・非表示させるだけだったらなくても良いんだけど、記述の簡略化ができたり動作を制限するのに使えたりで何かと都合が良かったりする。

ちょっと凝ってるのはPC表示時の動作。

マウスオーバー時に展開するんだけど、非表示の発火対象はheaderにしてある。
これによって、展開後にカーソルがbuttonとか展開した下層メニューからズレても下層メニューが引っ込まなくなってる。マウス操作が雑な場合の対策として、これは割と有効。

あと、非表示のタイミングは即時じゃなくてsetTimeout()でディレイを掛けてある。
マウスを外すんじゃなくて外れちゃった場合にも、一定時間表示は続いてるので、ディレイの間に戻したら非表示はキャンセルできる。

デバイスによって発火方法が変わるっておしゃれでいいですよね。

タブレット対策

自分で使わないから実際の動作がわからないんだけど、マウスカーソルが存在しない、スマホと同じ動作になるんだったらもう、これで組んでしまえば間違いがない。

$(function(){
    $('header li.sub button').on('click', function(){
        $(this).next('ul').slideToggle();
        $(this).toggleClass('active');
    })
})

全部クリックで発火させちゃえばいい。
その代わり、マウス操作したときに工程が1つ増えるのでだるいよねっていうね。

まあ、大体のサイトの閲覧比率はスマホの方が多いんで、PCは少ないんで、これでいいと思うよ。

タッチ動作の判定とか

if (matchMedia('(pointer:coarse)').matches) {

}

デバイス判定とか

var use = navigator.userAgent;
if (use.indexOf('iPhone') > 0 || use.indexOf('Android') > 0 && use.indexOf('Mobile') > 0) {

} else if (use.indexOf('iPad') > 0 || use.indexOf('Android') > 0) {

} else {

}

あるけどさ、なんかもうめんどくさい。

動作だけじゃなくてデザインの切り替えとかも考えなきゃだし。

だるい。

コメント

タイトルとURLをコピーしました