PC幅はヘッダー固定のナビゲーションメニュー、スマホ幅はハンバーガーメニューのサンプルを作ってみた(備忘録)

前回書いた下記の記事のソースを流用して、今回は、PC幅ではハンバーガーメニューを使用せずに、最初からメニューが表に出ている場合を想定して、ヘッダーに固定させた状態で作成している。
また、スマホ幅はこれまでと変わらず、ハンバーガーメニューになるよう作ってみた。

前回の記事はこちら

今回は、前回の記事と重なっている部分の説明は省略することにするが、ソースは全て載せるので、コピペすれば実装できるかと思う。

では、実装に入っていきたいと思う。

htmlの記述について

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="../reset.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <header>
    <!-- ハンバーガーメニュー -->
    <!-- 線 -->
    <div id="bl_hamburgerLine" class="hp_displaySP">
      <span></span>
      <span></span>
      <span></span>
    </div>
    <!-- メニューの中身 -->
    <nav id="bl_hamburgerMenu">
      <ul class="bl_hamburgerMenu_list" id="bl_hamburgerLink">
        <li><a href="#menu1">menu1</a></li>
        <li><a href="#menu2">menu2</a></li>
        <li><a href="#menu3">menu3</a></li>
        <li><a href="#menu4">menu4</a></li>
        <li><a href="#menu5">menu5</a></li>
      </ul>
    </nav>
    <!-- //ハンバーガーメニュー -->

  </header>
  <main>
    <section>
      <div class="ly_mainvisual">
        <img src="img_mainvisual.jpg" alt="MV">
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu1">
        <h2>menu1</h2>
        <div class="md_textblock">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu2">
        <h2>menu2</h2>
        <div class="md_textblock">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu3">
        <h2>menu3</h2>
        <div class="md_textblock">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu4">
        <h2>menu4</h2>
        <div class="md_textblock">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu5">
        <h2>menu5</h2>
        <div class="md_textblock">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>

  </main>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  <script src="style.js"></script>
</body>
</html>

以前と変わった部分は、 class=”hp_displaySP”の部分の記述の追加である。後ほどcssでも触れるが、ハンバーガーメニューの線の部分はPC幅では使用しないため、この部分だけスマホ幅で表示させるためにクラスを追加している。このクラス名は自分がわかりやすい名前を使えればOKだ。

cssの記述について

@charset "utf-8";

/* ==========================
  初期設定
========================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}
img {
  width: 100%;
}

/* ==========================
  コンテンツの中身
========================== */
.ly_inner {
  width: 100%;
  max-width: 1080px;
  margin: 100px auto;
  padding: 20px;
  background-color: #ccc;
}
.ly_inner h2 {
  font-size: 150%;
  font-weight: bold;
  margin-bottom: 30px;
}
.md_textblock > * + * {
  margin-top: 10px;
}
/* ====================================
  ここからPC幅
==================================== */
@media screen and (min-width: 768px) {
  .hp_displaySP {
    display: none;
  }
  /* ナビゲーション */
  header {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 38px;
    background-color: #222;
  }
  .bl_hamburgerMenu_list {
    display: flex;
    text-align: center;
    align-items: center;
    width: 100%;
  }
  .bl_hamburgerMenu_list > li {
    width: 100%;
    padding: 10px 5px;
  }
  .bl_hamburgerMenu_list > li a{
    color: #fff;
  }

}

/* ====================================
  ここからスマホ幅
==================================== */
@media screen and (max-width: 767px) {
  .hp_displayPC {
    display: none;
  }
  /* ==========================
    ハンバーガーメニュー
  ========================== */
  header {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 60px;
  }
  /* 線 */
  #bl_hamburgerLine {
    position: absolute;
    right: 0;
    top: 0;
    width: 60px;
    height: 60px;
    overflow: hidden;
    margin: 0 10px;
    cursor: pointer;
  }
  #bl_hamburgerLine span {
    display: block;
    position: absolute;
    right: 0;
    background-color: #444;
    height: 2px;
    width: 100%;
    transition: .3s;
  }
  #bl_hamburgerLine span:first-of-type {
    top: 10px;
  }
  #bl_hamburgerLine span:nth-of-type(2n) {
    top: 30px;
  }
  #bl_hamburgerLine span:last-of-type {
    top: 50px;
  }
  /* クリックした時 */
  #bl_hamburgerLine.active {
    z-index: 1001;
  }
  #bl_hamburgerLine.active span {
    background-color: #fff;
  }
  #bl_hamburgerLine.active span:first-of-type {
    transform: rotate(135deg);
    top: 30px;
  }
  #bl_hamburgerLine.active span:nth-of-type(2n) {
    transform: translateX(100%);
  }
  #bl_hamburgerLine.active span:last-of-type {
    transform: rotate(-135deg);
    top: 30px;
  }
  /* ハンバーガー展開時の中身 */
  #bl_hamburgerMenu {
    opacity: 0;
    visibility: hidden;
    transition: .3s;
    position: absolute;
    width: 100%;
    height: 100vh;
    background-color: #444;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #fff;
    font-size: 24px;
    overflow-y: scroll;
  }
  .bl_hamburgerMenu_list li+li {
    margin-top: 20px;
  }
  /* クリックした時 */
  #bl_hamburgerMenu.active {
    z-index: 1000;
    opacity: 1;
    visibility: visible;
  }
}

メディアクエリを使用して、css内で記述を出し分ける

今回の変更点は、PCには横幅いっぱいのナビゲーションメニュー、スマホ幅にはハンバーガーメニューをだし分ける必要性が出てくる。

PCの場合

@media screen and (min-width: 768px){
	ここの中にいつも通りの記述方法でcssを指定する。波括弧も普段通り使える。
	インデントも揃えると綺麗に記述していける。
}

この記述自体を丸暗記する必要はないと個人的には思う。決まった書き方だと思っていただければいい。

@mediaはその後に指定するオプションために、必要なものと考えればいいかと思う。

続いてscreenは、画面のために指定が必要なものになる。最近は指定がなくてもいいという考えもあり、記述しない人も多い。

andで記述を続けて、(min-width: 768px)。これは現在見ているページ画面の幅が768px以上の時が対象となるという意味だ。

つまり、この記述の中にcssを記述していくことで、ページ画面の幅が768px以上の時だけを対象として反映させることができる。

スマホ幅の場合

@media screen and (max-width: 767px) {
	ここの中にいつも通りの記述方法でcssを指定する。波括弧も普段通り使える。
	インデントも揃えると綺麗に記述していける。
}

スマホ幅も考え方は同じだ。PCと比べて異なる点はminからmaxに記述が変わっているという点だ。

minは最小以上、maxは最大以下という認識で考えるといいかと思う。

今回は、ページ画面の幅が767px以下の場合が対象となると考えればいいかと思う。

ここでなぜこの767や768という数字を用いているかという話だが、これは世の中に出ているPCやタブレット、スマホのサイズを考慮して、この数字がバランス的にいいだろうという考えに落ち着いている。実際に現場でもこの数字をブレークポイントとして使うことが多い。

もっというと、この2つだけでは足りないという考えもあり、分けるならスマホ、タブレット、PCとで3つ必要だという考えもあるので、ページのデザインなどによって使い分けるのがおすすめだ。

特定の幅の時だけ表示させたい場合

先ほどメディアクエリの対応で幅によって記述することができるようになったため、ここに下記の記述をする。

PC幅だけ表示させたい場合

@media screen and (min-width: 768px){
	.hp_displaySP {
    display: none !important;
  }
}

スマホ幅だけ表示させたい場合

@media screen and (max-width: 767px) {
	.hp_displayPC {
    display: none !important;
	}
}

ポイントとしては、任意のクラスでいいので、例えばhp_displaySPと指定してスマホ幅にだけ表示させたいような時だ。PC幅には何も表示させたくないため、そのメディアクエリの中でdisplay: none !important;を指定すれば実装ができる。

この指定はスマホ幅だけ改行したい場合などにも使えるため、
などと指定して現場でも結構使用することが多い。

PCのナビゲーションについて

こちらはflexを用いて横に並べてヘッダーを高さ38pxで固定しているだけだ。詳細の解説は省きたいと思う。

javascriptの記述について

'use strict';

// ハンバーガーメニュー
document.addEventListener('DOMContentLoaded', function () {
  document.getElementById('bl_hamburgerLine').addEventListener('click', function () {
    this.classList.toggle('active');
    document.getElementById('bl_hamburgerMenu').classList.toggle('active');
  });
  // メニュークリックで閉じる
  $('#bl_hamburgerMenu > .bl_hamburgerMenu_list a[href]').on('click', function (event) {
    $('#bl_hamburgerLine').trigger('click');
  });
});

// スムーススクロール
window.addEventListener('DOMContentLoaded', function () {
  const anchorLinks = document.querySelectorAll('a[href^="#"]');
  const anchorLinksArr = Array.prototype.slice.call(anchorLinks);

  anchorLinksArr.forEach(function (link) {
    link.addEventListener('click', function (e) {
      e.preventDefault();
      const targetId = link.hash;
      const targetElement = document.querySelector(targetId);
      if (window.matchMedia('(max-width: 767px)').matches) {
        const targetOffsetTopSP = window.pageYOffset + targetElement.getBoundingClientRect().top; //ここに- 50 などと数値を入れるとヘッダー固定のスクロールが実現できる
        window.scrollTo({
          top: targetOffsetTopSP,
          behavior: "smooth"
        });
      } else if (window.matchMedia('(min-width:768px)').matches) {
        const targetOffsetTopPC = window.pageYOffset + targetElement.getBoundingClientRect().top - 38; //ここに- 50 などと数値を入れるとヘッダー固定のスクロールが実現できる
        window.scrollTo({
          top: targetOffsetTopPC,
          behavior: "smooth"
        });
      }
    });
  });
});

PC幅とスマホ幅によって条件を出し分ける

if (window.matchMedia('(max-width: 767px)').matches) {
        const targetOffsetTopSP = window.pageYOffset + targetElement.getBoundingClientRect().top; //ここに- 50 などと数値を入れるとヘッダー固定のスクロールが実現できる
        window.scrollTo({
          top: targetOffsetTopSP,
          behavior: "smooth"
        });
      } else if (window.matchMedia('(min-width:768px)').matches) {
        const targetOffsetTopPC = window.pageYOffset + targetElement.getBoundingClientRect().top - 38; //ここに- 50 などと数値を入れるとヘッダー固定のスクロールが実現できる
        window.scrollTo({
          top: targetOffsetTopPC,
          behavior: "smooth"
        });
      }

コードは長くなってしまうのだが、ifとelse ifを使って条件分岐している。
ifの中にはスマホ幅の条件を、else ifの中にはPC幅の条件を記述している。

window.matchMedia(‘(max-width: 767px)’).matches) は決まり文句みたいなものなので、特に暗記する必要はない。MDNで調べると説明や使い方が出てくる。

今回の場合は、下記の認識で捉えておくとよい

  • window.matchMedia(‘(max-width: 767px)’).matches)
    ⇨ビューポートの幅が767px以下の時(スマホ幅)
  • window.matchMedia(‘(min-width:768px)’).matches
    ⇨ビューポートの幅が768px以上の時(PC幅)

javascriptの場合、constは同じ名前を指定できないため、targetOffsetTopSP、targetOffsetTopPCで名前を分けて指定してる。

また、PC幅については、ヘッダーメニューの高さが38pxのため、メニューをクリックしてスクロールさせた時に、38px分の高さをマイナスしなければアンカーリンク先のメニューの上部に合わせることができないため、その処理のため、「-38」という処理を記述している。

それ以外の点は、前回の記事と変わらないため解説は省略する。この出しわけの記述は、他のところでも流用できるため、何かと便利だと思う。

今回はこれで以上にしたいが、学習していく中で、もう少し記述を省略できたりする方法が見つかれば、本記事も更新したいと思う。

また、下記の記事も本記事のソースをもとに作成した関連記事なので、興味のある方は読んでいただければと思う。

現場でよく使うjQueryを用いたハンバーガーメニューを作ってみた(備忘録)

2 返信

トラックバック & ピングバック

  1. […] PC幅はヘッダー固定のナビゲーションメニュー、スマホ幅はハンバーガーメニューのサンプルを作ってみた(備忘録) […]

コメントはクローズされています。