こんにちは、北陸のwebコーダーdaimaです。
本日は、ナビゲーションをコンパクトに纏められる
おしゃれなハンバーガーメニューの実装方法をご紹介します。
まずは下記のデモページをご覧ください。
最終更新2020/07/04 : ページ内リンクのクリックした際に自動でメニューを閉じた上で目的地点までスムーズスクロールを行う記述を追加しました。
今回はこのように、至ってシンプルな
ハンバーガーメニューを作成します。
動作確認済ブラウザはChrome、IE、FireFox、safariの
最新バージョン(記事執筆時点)です。
ソースコードと解説
<div class="header"> <div class="el_humburger"> <span class="top"></span> <span class="middle"></span> <span class="bottom"></span> </div> <div id="uq_spNavi" class="uq_spNavi"> <div class="uq_spNavi_screen"> <nav class="navigation"> <div class="navigation_item"><a href ="https://spreadsheep.net/post-240/">記事に戻る</a></div> <div class="navigation_item"><a href ="#inner_link">ページ内リンク</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> <div class="navigation_item"><a href ="#">CONTENT</a></div> </nav> </div> </div> </div> <div class="wrap"> <div class="content"> I LOVE HUMBURGUR MENU </div> <div id="inner_link" class="content2"> INNER LINK </div> </div>
HTMLソースです。
ハンバーガーボタンとナビゲーションの
マークアップを行っています。
/*基本レイアウトの調整*/ body { margin: 0; } .wrap { padding: 60px 0 0 0; background-color: #f8e0f7; } .content { text-align: center; font-size: 22px; color: #bf86ce; height: 2000px; padding: 60px; } .content2{ font-size: 22px; color: #bf86ce; padding-top: 100px; padding-bottom:1000px; text-align: center; } /*ハンバーガーボタン*/ .header { position: fixed; top: 0; left: 0; width: 100%; height: 60px; background-color: #be9dc9; } .el_humburger { position: absolute; top: 18px; right: 18px; width: 26px; height: auto; padding-top: 1px; box-sizing: border-box; z-index: 10000; transition: all 0.2s ease-in-out; cursor: pointer; pointer-events: auto; } .el_humburger > span { display: block; width: 100%; margin: 0 auto 6px; height: 3px; background: #000; transition: all 0.2s ease-in-out; } .el_humburger > span:last-child { margin-bottom: 0; } .js_humburgerOpen .el_humburger > span { background: #000; } .js_humburgerOpen .el_humburger > span.top { transform: translateY(9px) rotate(-45deg); } .js_humburgerOpen .el_humburger > span.middle { opacity: 0; } .js_humburgerOpen .el_humburger > span.bottom { transform: translateY(-9px) rotate(45deg); } .el_humburgerButton.el_humburgerButton__close { top: 2%; right: 2%; } .el_humburgerButton__close > span { display: block; width: 35px; margin: 0 auto; height: 4px; background: #fff; } .el_humburgerButton__close > span.el_humburgerLineTop { transform: translateY(5px) rotate(-45deg); } .el_humburgerButton__close > span.el_humburgerLineBottom { transform: translateY(-6px) rotate(45deg); } /*ナビゲーション*/ .uq_spNavi { display: none; } .uq_spNavi.js_appear { position: fixed; top: 0; left: 0; bottom: 0; right: 0; display: block; z-index: 9999; } .uq_spNavi_screen { position: absolute; top: 0; left: 0; bottom: 0; right: 0; background-color: rgba(255, 255, 255, 0.96); z-index: 0; margin-top: 0px; padding-top: 0px; overflow: auto; -webkit-overflow-scrolling: touch; } .navigation { padding: 80px 0 0 0; text-align: center; } .navigation_item { font-ize: 18px; margin-bottom: 20px; } .navigation_item > a { color: #000; text-decoration: none; } .js_fixed { position: fixed; width: 100%; height: 100%; }
CSSソース。
ハンバーガーボタンの動作と
ナビゲーション表示時の
レイアウト指定を行っています。
ここでのミソは、
.navigation_screenに記載されている
-webkit-overflow-scrolling: touch;というプロパティ。
これを指定しておくと、
スマホでナビゲーション内部を
スワイプしたときの動作が滑らかになります。
// スクロール可能な要素を判別する関数 function getFirstScrollable(selector){ var $scrollable; $(selector).each(function(){ var $this = $(this); if( $this.scrollTop() > 0 ){ $scrollable = $this; return false; }else{ $this.scrollTop(1); if( $this.scrollTop() > 0 ){ $scrollable = $this; return false; } $this.scrollTop(0); } }); return $scrollable; } //スムーズスクロール処理を行う関数 function smoozScroll($target){ if($('body.js_humburgerOpen').length){ spNavInOut.switch(); } //変数のセッティング var $win = $(window), $doc = $(document), $scrollElement = getFirstScrollable("html,body"), mousewheel = "onwheel" in document ? "wheel" : "onmousewheel" in document ? "mousewheel" : "DOMMouseScroll"; var top; // 現在のウィンドウ幅を取得 var w = $(window).width(); // $targetが空の場合はキャンセル if( $target.size() < 1 ) return false; // スクロール先の位置を調整 top = $target.offset().top; top = Math.min(top, $doc.height() - $win.height()); // スクロール中は手動スクロールをキャンセルする $doc.on(mousewheel, function(e){ e.preventDefault(); }); // 移動先へアニメーションする $scrollElement.stop().animate({scrollTop: top - 0}, 600, "linear", function(){ $doc.off(mousewheel); }); }; $(function(){ var $win = $(window), $doc = $(document); //アンカーリンクをクリックした時にアクションを起こす処理 $doc.on("click", "a[href^=\\#]", function(e){ /*if($(this).data('option')){ if($(this).data('option') === "close_selector"){ $('.js_pullDownParent').removeClass('js_fire'); } }*/ var $target = $(this.hash), top; smoozScroll($target); return false; }); }); //ハンバーガーメニュー $(function(){ $('.el_humburger').on('click',function(){ spNavInout(); }); $('.navigation_item a').on('click',function(){ spNavInout(); }); }); //spナビ開く処理 function spNavIn(){ $('body').removeClass('js_humburgerClose'); $('body').addClass('js_humburgerOpen'); $(".uq_spNavi").addClass("js_appear"); $(".uq_spNavi").css({opacity:0}); $(".uq_spNavi").animate({ opacity: 1 },200); scrollBlocker(true); } //spナビ閉じる処理 function spNavOut(){ $(".uq_spNavi").animate({ opacity: 0 },200) $('body').removeClass('js_humburgerOpen'); $('body').addClass('js_humburgerClose'); setTimeout(function(){ $(".uq_spNavi").removeClass("js_appear"); },200); scrollBlocker(false); } //spナビ開閉処理 function spNavInout(){ if($('body.spNavFreez').length){ return false; } if($('body').hasClass('js_humburgerOpen')){ spNavOut(); } else { spNavIn(); } } //ナビ向けスクロール無効化処理 var scrollBlockerFlag; function scrollBlocker(flag){ if(flag){ scrollpos = $(window).scrollTop(); $('body').addClass('js_fixed').css({'top': -scrollpos}); scrollBlockerFlag = true; } else { $('body').removeClass('js_fixed').css({'top': 0}); window.scrollTo( 0 , scrollpos ); scrollBlockerFlag = false; } }
そして最後がjavascipt(jQuery)ソース。
ここでやっていることは大きく分けて次の3つです。
1.ハンバーガーメニュー内のページ内リンクをクリックした場合にスムーズスクロールとメニューを自動で閉じる処理を行う(1~77行目)
2.bodyにナビゲーション判別用のクラス「js_humburgerOpen」があるか判定し、
なければクラスの追加とナビゲーションの表示処理、
あればクラスの削除とナビゲーションの非表示処理を行う。(80~126行目)
3.自作関数scrollBlockerで、
ナビゲーションが開いているときに、
body及びhtmlがスクロールしないように固定する。(128~142行目)
特に3の部分で少し凝った処理をしていて、
まずナビオープン時のy位置を取得し、
次にbodyにクラス「js_fixed」を付与して
bodyのpositionを一時的にfixedに変更しています。
そして最後にcssのtopでbodyのy位置を
ナビオープン時のスクロール量分ずらし、
見た目上の表示位置を合わせることによって
スクロールの固定を実現しているのです。
この処理上、ナビオープン時の
yスクロール量は必ず0になります。
ですので、当ナビを導入して
万が一他の処理に不具合が起きた場合は
まずこの点を疑ってみてください。
さいごに
ハンバーガーメニューの実装処理、
上手くいきましたでしょうか。
ハンバーガーメニューはおしゃれで大変便利ですが、
ユーザビリティの観点から見ると、
年配の方には一見してメニューだと
分かり辛いという指摘もしばしば聞かれます。
ですので、年配のユーザーが多い
サイトに導入する場合は
ハンバーガーの直下に「メニュー」の
表記を足すなどのひと工夫を行うと良いでしょう。
今回ご紹介したのは、あくまでも
必要最低限の枠組みだけですので、
仕組みが理解出来たらぜひ、
あなただけのオリジナルのメニューに
カスタマイズしてみてください。
それでは!
コメント
初めまして!質問がありコメントさせていただきました。
sp時にハンバーガーメニューを取り入れたいのですが、ハンバーガーメニューをクリックしてもnavigationが表示されません、、、
もし思い当たる原因などありましたら教えていただけると幸いです。
よろしくお願いいたします。
赤堀様、コメントありがとうございます。
管理人のdaimaです、お返事遅くなりごめんなさい。
navigationが表示されないということですが
原因の特定にはこちらで実際のコードを確認する必要があります。
ですので、もし差し支えなければ
コードデータかサイトURLをフォームからお伝えください。
daimaさん
navigationを左から右にスライド表示ってできるのでしょうか?
よろしくお願い申し上げます。
yajioさんご訪問ありがとうございます。
こちら、javasctriptのanimateメソッドで動きをつけていますので
そちらをcssと合わせて調整すればスライド表示にも変更可能かと思います。
初めまして、石川と申します。
こちらのハンバーガーメニューの導入がとても分かりやすく良く使用しています。
ページ内アンカーリンクの先に閉じるようにすることは出来るのでしょうか?
jsの知識がなく苦戦しています。
石川さんはじめまして、コメントありがとうございます。
こちら、ページ内リンクのクリック時にハンバーガーメニューを自動で閉じるようにしたいというご要望だと解釈し、
記事ページ(https://spreadsheep.net/post-240/)に同様の処理内容を追記しました。(認識間違っていたらお知らせください)
お返事遅くなって申し訳ないのですが、よろしければ一度ご確認ください。