Vanilla JSで実装するハンバーガーメニューの開閉

ハンバーガーメニューをクリックするとナビメニューが開く

jQueryだと.fadein() .fadeout() 一発で実装できます

これをVanilla JSで実装となると結構つまづきました

この記事の目次

最初のコード

<header class="l-header p-header js-header">
  <div class="p-header__inner l-inner"> 
   <div class="p-header__wrapper">
      <h1 class="p-header__logo">
        <a href="./index.html">header logo</a>
      </h1>
      <nav class="p-header__nav js-drawer-menu" id="drawer-menu">
        <ul class="p-header__items">
          <li class="p-header__item p-header-item js-drawer-link"><a href="#concept">concept</a></li>
          <li class="p-header__item p-header-item js-drawer-link"><a href="#service">service</a></li>
          <li class="p-header__item p-header-item js-drawer-link"><a href="#works">works</a></li>
          <li class="p-header__item p-header-item js-drawer-link"><a href="#contact">contact</a></li>
        </ul>
      </nav>
      
      <!-- SPハンバーガーアイコン -->
      <button type="button" class="p-header__hamburger p-hamburger js-hamburger">
        <span class="p-hamburger__line">
          <span class="p-hamburger__hidden"></span>
        </span>
      </button>
    </div>
  </div>
</div>
/* ハンバーガードロワーメニュー(VanillaJS) */
const hamburger = document.querySelector(".js-hamburger");
const drawer = document.querySelector(".js-drawer-menu");
const body = document.body;

function fadeIn(target, duration, ease) {  
  var motion = "fadeIn";
  target.style.display = 'block';
  animation(target, duration, ease, motion);
}

function fadeOut(target, duration, ease) {
  var motion = 'fadeOut';
  animation(target, duration, ease, motion);
  target.style.display = 'none';
}

function animation(target, duration, ease, motion) {
  if (/^-?[0-9]+$/.test(duration)) {
      duration = duration + 'ms';
  } else {
      duration = '400ms';
  }
  target.style.animation = [motion, "forwards", duration, ease].join(" ");
}
@keyframes fadeIn {
  0% {
      opacity: 0;
  }

  100% {
      opacity: 1;
  }
}

@keyframes fadeOut {
  0% {
      opacity: 1;
  }

  100% {
      opacity: 0;
  }
}

ただこのコードだとフェードアウトしたときにアニメーションが走らずただ消えるだけでした

animationendの追加

そこで以下のように変えてみました

function fadeOut(target, duration, ease) {
  var motion = 'fadeOut';
  animation(target, duration, ease, motion);
  target.addEventListener('animationend', function() {
    target.style.display = 'none';
  });
}

アニメーションが終わったらnoneにするようにしましたが、アニメーションが走ってから消えるようになりました

これでOK!…とはならず、2回目以降はすぐにメニューが消える現象が発生しました

原因は「アニメーションが終わったらイベントを実行する」が生きているため、メニューが開いた瞬間にnoneになるためです

noneの部分を関数化

function fadeOut(target, duration, ease) {
  function display_none() {
    target.style.display = 'none';
  }
  var motion = 'fadeOut';
  animation(target, duration, ease, motion);
  target.addEventListener('animationend', display_none);
}

ただこれはさっきとやっていることは同じです。当然結果も一緒です

addEventListener関数の引数に{once: true}を渡してもイベントが生き続けました(なぜ…)

このままだと2回目以降メニューを開いてもイベントが生きているので速攻で消されてしまうので別の手立てを考えるしかありません

イベントを追加

そこで以下のように新たな記述を追加します

const hamburger = document.querySelector(".js-hamburger");

//中略
  
function fadeOut(target, duration, ease) {
  function display_none() {
    target.style.display = 'none';
  }
  var motion = 'fadeOut';
  animation(target, duration, ease, motion);
  target.addEventListener('animationend', display_none);
  //再度メニュー開いた際のdisplay_noneイベントの除去
  hamburger.addEventListener('click', function() {
    target.removeEventListener('animationend', display_none);
  });
  //opacityリセット
  window.addEventListener('resize', ()=> {
    var window_width = window.innerWidth;
    if(window_width >= md) {
      target.removeEventListener('animationend', display_none);
      target.style.animation = "";
    }
  });
}

①再度ハンバーガーメニューをクリックした際にdisplay_noneイベントを消してあげます

②PC画面にした際にanimationをリセットします

本来ならもっとうまい具合に出来る気がします…

こういうのはどう?とかそういった情報あったら教えていただけるとたいへん助かります。。

フェードの関数実行

いよいよフェードイン・フェードアウトの関数を実行します

const hamburger = document.querySelector(".js-hamburger");
const drawer = document.querySelector(".js-drawer-menu");

//中略

hamburger.addEventListener('click', function() {
  if(hoge) {
    fadeIn(drawer);
  } else {
    fadeOut(drawer);
  }
});

あとはハンバーガーをクリックした際に条件をつけてあげて、フェードイン・フェードアウトの記述を行います

fadeOut(drawer , 3000, "ease-in");のように引数を入れてあげれば、animation-duration(秒数)やanimation-timing-function(速度)を指定することも可能です

この記事が気に入ったら
フォローしてね!

シェアしてね!!
  • URLをコピーしました!

著者

Webコーダー。
ドライブとコーヒーとガジェットが好き。
「Code Widgit」は「Code With it」と「gadget」を合わせた造語。

この記事の目次
閉じる