MONOについているペロっと出入りするメニュー Headroom.jsを解説します

サイトのメニューは一番個性が出るところで、使い勝手にかなり影響するため、いろいろと検討されると思います。

カラーミーショップでは、MONOテンプレートのヘッダーメニューが凝っていて、上下に出入りする挙動がおもしろいです。

ご自身のサイト作りに取り入れたり、MONOテンプレートをカスタマイズ時の参考になりますので解説します。

Headroom.jsの機能

MONOテンプレートのヘッダーメニューは、Headroom.jsというJavaScriptを使って実装しています(MONOではjQueryプラグインとして使用していますが、単体でも動きます)。

下スクロール時には、ヘッダーメニューは画面外にあって、上スクロール時にヘッダーメニューが下りてくる挙動です。デモサイトで確認してください。

MONOデモサイト

スマートフォンサイトは画面が狭いので、ヘッダーメニューを固定せずに、スクロール時にメニューを画面外・画面内に出入りさせて、極力広い表示スペースを確保しています。

Headroom.jsに必要なコード

サンプルとして、MONOテンプレートから必要なコードを抜粋します。
共通HTML内の一番下のほうに記述があります。

<script src="//img.shop-pro.jp/tmpl_js/83/headroom.min.js"></script>
<script src="//img.shop-pro.jp/tmpl_js/83/jQuery.headroom.js"></script>
<script>

  // breakpoint
  var breakpointPhone = 600 - 1,
  breakpointTablet = 600,
  breakpointPC = 960;

  // headroom.jsの処理
  function headroomPC() {
    var fixedHeaderHeight = $('#js-fixed-header').outerHeight();
    var startPos = fixedHeaderHeight + 100;
    $('.js-fix-height').css('padding-top', fixedHeaderHeight);
    $('#js-fixed-header').headroom({
      tolerance: {
        down : 2,
        up : 8
      },
      offset: startPos,
      classes: {
        pinned: 'is-fixed',
        unpinned: 'is-hidden',
        top: 'is-top',
        notTop: 'isnot-top'
      },
    });
  }

  (function () {
    if ($('.js-header-logo').size() === 0) {
      headroomPC();
    } else {
      var $logoImageCopy = $('<img>');
      $logoImageCopy.on('load', function() {
        headroomPC();
      });
      $logoImageCopy.attr('src', $('.js-header-logo img').attr('src'));
    }
    $(window).on('resize', function () {
      $('#js-fixed-header').headroom('destroy');
      headroomPC();
    });
  }());

  (function () {
    var spHeaderPos = $('#notTop').offsetTop;
    $('#js-sp-header').headroom({
      tolerance: {
        down : 2,
        up : 8
      },
      offset: spHeaderPos,
      classes: {
        unpinned : 'is-hidden',
        notTop: 'is-fixed'
      }
    });
  }());

</script>

上から順にコード解説

JavaScriptの読み込みが2つ。
Headroom.jsの本体jQueryプラグインとして使うためのjsファイルです。

<script src="//img.shop-pro.jp/tmpl_js/83/headroom.min.js"></script>
<script src="//img.shop-pro.jp/tmpl_js/83/jQuery.headroom.js"></script>
<script>

  // breakpoint
  var breakpointPhone = 600 - 1,
  breakpointTablet = 600,
  breakpointPC = 960;

ブレイクポイントを変数に代入します(あとでその変数を使っています)。
MONOはブラウザの横幅600px、960pxにブレイクポイントがあります。
ここは設置するサイトに合ったブレイクポイントに変更します。

function headroomPC()はオプションを設定して、Headroom.jsを実行する関数です(他から呼び出して実行する形)。

関数名から察するとおり、PC用のHeadroomパラメータです。
セレクタ #js-fixed-header がパソコン用ヘッダーメニューだからです(スマートフォン時にはdisplay: none;になります)。

  // headroom.jsの処理
  function headroomPC() {
    var fixedHeaderHeight = $('#js-fixed-header').outerHeight();
    var startPos = fixedHeaderHeight + 100;
    $('.js-fix-height').css('padding-top', fixedHeaderHeight);
    $('#js-fixed-header').headroom({
      tolerance: {
        down : 2,
        up : 8
      },
      offset: startPos,
      classes: {
        pinned: 'is-fixed',
        unpinned: 'is-hidden',
        top: 'is-top',
        notTop: 'isnot-top'
      },
    });
  }


ヘッダーメニューは状態に応じて、position: absolute;やposition: fixed;になります。absoluteやfixed はレイヤーが浮いているような状態なので、スライドショー以下の要素が下にめり込みます。
その対応として、ヘッダーメニュー要素の高さ(fixedHeaderHeight)を計算してpadding-topを入れています。

パラメータ offsetは、ページ読み込み直後に垂直方向に指定しただけ下にスクロールしたら、class="is-hidden" を付けて、transformY(-100%);でヘッダーメニューを画面外の遠くに飛ばします。
offset: 0ではメニューの出入れ時にカクつくので、これくらいでいいと思います。


(function () { ~ }()); は読み込み後に実行する即時関数です。
ロゴ画像の有無と、ロゴ画像がある場合は読み込み待ちをしてから、headroomPC関数を呼び出しています。

リサイズ時はいったん破棄して、ヘッダーメニューの高さの計算をしなおしてから、Headroomを再実行します。
resizeイベントが発火するのは基本的にはパソコン閲覧時のみです。

  (function () {
    if ($('.js-header-logo').size() === 0) {
      headroomPC();
    } else {
      var $logoImageCopy = $('<img>');
      $logoImageCopy.on('load', function() {
        headroomPC();
      });
      $logoImageCopy.attr('src', $('.js-header-logo img').attr('src'));
    }
    $(window).on('resize', function () {
      $('#js-fixed-header').headroom('destroy');
      headroomPC();
    });
  }());


最後がスマートフォン用のHeadroom.jsを実行するパラメータです。
セレクタ #js-sp-header がスマートフォン用ヘッダーメニューだからですね。

$('#notTop').offsetTop;はundefined(id="notTop"が存在しない)だったので、2行目のコードは不要だと思います(その場合はoffset: 0, でいいはず)。

  (function () {
    var spHeaderPos = $('#notTop').offsetTop;
    $('#js-sp-header').headroom({
      tolerance: {
        down : 2,
        up : 8
      },
      offset: spHeaderPos,
      classes: {
        unpinned : 'is-hidden',
        notTop: 'is-fixed'
      }
    });
  }());

</script>

Headroom.jsに必要なCSS

jQueryのコードが書き終われば、次にCSSの準備が必要です。
オプションのclassesを参照して、状態によってクラスが付きます
(セレクタで指定している要素に付きます)。

pinned … ヘッダーメニューが表示されている状態
unpinned … ヘッダーメニューが表示されていない状態
top … ページ先頭にある状態(offset分が考慮される)
notTop … 下にスクロールした状態(offset分が考慮される)

      classes: {
        pinned: 'is-fixed',
        unpinned: 'is-hidden',
        top: 'is-top',
        notTop: 'isnot-top'
      },


MONOテンプレートのパソコン閲覧時のスタイル抜粋します。

.p-fixed-header {
    position: absolute;
    z-index: 1000;
    top: 0;
    width: 100%;
    transition: transform .2s;
    transform: translateY(0%);
}

.p-fixed-header.is-hidden {
    transform: translateY(-100%);
}

.p-fixed-header.is-fixed {
    position: fixed;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .2);
}

.p-fixed-header.is-top {
    position: absolute;
    box-shadow: none;
}


これまでコードを読んできて、セレクタの指定で区別がありました。
同じ要素を指定しているにもかかわらず、
jQueryのセレクタには#js-fixed-headerを指定し、
CSSのセレクタには.p-fixed-headerを指定する使い分けになっています。
入門書を読むと、このあたりのことを解説してあります。


引き続き、MONOテンプレートを例に説明します。

MONOテンプレートのヘッダーメニューはページ先頭にあるときと、スクロール中に表示するヘッダーメニューの内容が異なります。

ページ先頭時には下記のようなクラス(is-top)が付いています。

<div id="js-fixed-header" class="p-fixed-header headroom is-top headroom--not-bottom">
ページ先頭にあるヘッダーメニュー(フルサイズ)


下へスクロールしてからヘッダーメニューを呼び出すと、コンパクトなサイズで表示されます。is-topがなくなり、isnot-topというクラスになっています。

<div id="js-fixed-header" class="p-fixed-header headroom headroom--not-bottom isnot-top is-fixed">
ページ途中のヘッダーメニュー(コンパクト)


MONOテンプレートでは、下にスクロールときにヘッダーメニューをコンパクトにしていますが、大きなロゴが無くなって、「ホーム」に置き換わっています。

ロゴは、isnot-topでdisplay: none;して

.isnot-top .p-global-header .p-global-header__logo {
    display: none;
}


isnot-topで「ホーム」をdisplay: inline-block;しています。

.isnot-top .p-global-header .p-global-header__home {
    display: inline-block;
}


このようにしてクラスの有無で状態を把握してレイアウトを変更したりするわけです。

MONOテンプレートは、他にも is-fixedやis-hiddenというクラスも付きます。状態によって該当するクラスが付きますので、状態(〇〇なとき)によってレイアウトを変えたい場合に便利です。


デベロッパーツールでソースを見ると、スクロールの位置でclass="~"内が増減する様子が見られます。ソースを見ると理解がはかどります。

おわりに

Headroom.jsの公式ページにサンプルが一式あります。
設置する際に理解を深めたいときはそちらを参考にどうぞ。

余談ですが、MONOテンプレートはカラーミーショップをするのに向いていると思います。
商品詳細ページに、商品のストーリーを書けるくらいのスペースが空いていますので、商品の良さをお客様に訴求しやすいと思います。

サイドバーがない点がMODEとの大きな違いですが、昨今はスマートフォンファーストなので、以前ほどサイドバーを重要視しなくてもよいと思います。

執筆者

えいじ@naeco.jp この記事を書いた人

メーカー系情報システム部門出身の個人事業主。
自作するのが好きですぐに試したくなる、凝り性なWebエンジニア。
カラーミーショップ、モールなどのECについて記事にしています。

ご相談・お問い合わせはこちら