JavaScriptでスクロールに合わせてナビゲーションメニューのアクティブを変更する

ワンカラムレイアウトでスクロールに合わせてグローバルナビゲーションのアクティブが変わるページ多いですよね。
このアクティブを変更するライブラリは多く存在しますが弊社サイトでも導入するとのことだったので作ってみました。

(デモ)

 

(弊社活用事例)

 

 

 

使い方

 

  1. jQueryと、Waypoints.jsを読み込みます。
    <script type="text/javascript" src="/PATH/jquery-1.11.3.min.js"></script>
    <script src="/PATH/noframework.waypoints.min.js"></script>
  2. ChangeScroll.jsをダウンロードし、読み込み込みます。
    <script src="/PATH/ScrollChange.js"></script>
    
  3. ページ内リンクを作成し、リンクの飛び先に「active-func」クラスを付与します。
    <a href="#contents1">大幅アップデート</a>
    <a href="#contents2">選ばれる理由</a>
    
    <section id="contents1" class="active-func">
       <p>大幅アップデート</p>
    </section>
    <section id="contents2" class="active-func">
       <p>選ばれる理由</p>
    </section>
  4. 設定は以上です。スクロールとリンクに対応して aリンクに「active」クラスが付与されます。
    ScrollChange.jsを開き、下記を変更することでオフセットの位置、active-funcやactiveクラス名などの設定を変更することが可能です。

    /**********************
    * Settings
    **********************/
        // Target class name
        target_name:'active-func',
    
        // Add class name
        active_class_name:'active',
    
        // Scroll offset
        header_offset:155,
    
        // 検出位置の差分(大きいほどターゲットより下にActive変更ポイントが来る)
        // 0は正しく動作しません。
      header_offset2:10,
    
        // Smooth scroll setting(On:1 Off:0)
        smooth_scroll:1,
    
        // Scroll speed
        scroll_speed:800,
    /**********************
    * End settings
    **********************/

 

検証ブラウザ

 

動作するブラウザ

IE10+ 、Chrome、Safari、Firefox

検証環境は下記の通りで全てで動作確認済

Windows7 IE10、IE11、Chrome、Firefox
Windows8 IE10
Windows8.1 IE11
Windows10 IE11

MacOS10.9 Chrome、Safari、Firefox

iPhone5 標準ブラウザ
iPhone6 標準ブラウザ

Xperia Z3 標準ブラウザ(Android5)
Xperia Z3 タブレット 標準ブラウザ(Android5)
 

 

解説

 

せっかくですのでどのような仕組みでこの機能が動作しているかを解説します。
JavaScriptに興味がある方のみお読みください。

 

スクロール検知について

スクロールの検知はWaypoints.jsで行っています。

  1. Waypoints.jsを読み込みます。
  2. new Waypointで、Waypointのインスタンスを起動
  3. element: にターゲットを指定します。ここでは idに basic-waypoint が付与された箇所を取得しています。
  4. handler:function(){ ここに処理を書く } に スクロールがbasic-waypointに来たときの処理を書きます。
var waypoint = new Waypoint({
  element: document.getElementById('basic-waypoint'),
  handler: function() {
    notify('Basic waypoint triggered')
  }
})

とても簡単に使うことができます。今回はスクロール位置を検出しactiveクラスを付与していますが、その他にも画面スクロールを検出し、なにかアニメーションを加えることなどできます。
 

クリック検知について

スクロールに合わせてactiveも変化しますが、アンカーをクリックした時にもActiveが変化します。
一般的にアクティブが変わるグローバルナビゲーションにはヘッダーの高さがあり、アンカーをクリックしたときにヘッダーの高さを足した分だけ下げる必要があります。(例では100px分下げています)

ヘッダーの高さ分下げないと、ヘッダーに対象のコンテンツが隠れてしまいあまりよろしくないですね。

クリックイベントを取得しスクロールするコードは下記です。

// #を含むリンクがクリックされた場合
// href属性に#が入っているリンクを .click() イベントハンドラで取得
$('a[href^=#]').click(function() {

        // href属性の内容を取得
        var href= $(this).attr('href');

        // href属性が # のみか、空だった場合は、htmlのDOM(トップ)をtargetに入れます。
        // 上記以外の場合href属性の値をDOMで取得します。
        var target = $(href == '#' || href == '' ? 'html' : href);

       // 移動先スクロール位置を数値で取得
       // target.offset().top Topから対象までのピクセル数
       // ScrollChange.header_offset プロパティにヘッダーの高さ
       // -5 は、ブラウザ間で多少移動位置がずれるのでその範囲を吸収するために
       // 「-5」を指定しています。コーディングした内容に合わせて調整ください。
        var position = target.offset().top - (ScrollChange.header_offset - 5);

        // jQueryのanimate() メソッドで対象位置までスクロール
        $('body,html').animate({scrollTop:position}, ScrollChange.scroll_speed);
        return false;
});

 

コメント付き全量コード

 

var ScrollChange = new Object();
ScrollChange = {
    /**********************
     * Settings
     **********************/
    // Target class name
    target_name:'active-func',

    // Add class name
    active_class_name:'active',

    // Scroll offset
    header_offset:155,

    // 検出位置の差分(大きいほどターゲットより下にActive変更ポイントが来る)
    // 0は正しく動作しません。
    header_offset2:10,

    // Smooth scroll setting(On:1 Off:0)
    smooth_scroll:1,

    // Scroll speed
    scroll_speed:800,
    /**********************
     * End settings
     **********************/

    CheckActiveClass:function(ei){
      // 現状 activeが付与されているかどうかチェック
      if($('.'+this.active_class_name+':eq(0)') == undefined){
          // ターゲットにクラス付与
          this.AddClass(ei);
      }else{
          // 現在のactiveを削除後、クラス付与
          this.RemoveClass().AddClass(ei);
      }
    },
    RemoveClass:function(){
        // activeが存在したら、クラスを削除
        $('.'+this.active_class_name).removeClass(this.active_class_name);
        return this;
    },
    AddClass:function(sp){
        // スクロールポイントのIDを取得する sp:スクロールポイント
        var target_class= 'a[href*="' + sp + '"]';
        // ターゲットのDOMを取得し、クラス追加
        $(target_class).addClass(this.active_class_name);

    }
}

$(function(){
    // active-funcが付いている箇所をn個取得する
    var w = $('.' + ScrollChange.target_name);
    var offset2 = ScrollChange.header_offset-ScrollChange.header_offset2;
    // active-funcの回数分ループ
    for (var i = 0; i < w.length; i++) {
      new Waypoint({
        element: w[i],
        handler: function() {
            ScrollChange.CheckActiveClass(this.element.id);
        },offset:ScrollChange.header_offset
      });
      new Waypoint({
        element: w[i],
        handler: function() {
            ScrollChange.CheckActiveClass(this.element.id);
        },offset:offset2
      });
    }

    // #で始まるアンカーをクリックした場合に処理
    if(ScrollChange.smooth_scroll){
      $('a[href^=#]').click(function() {
        // アンカーの値取得
        var href= $(this).attr('href');

        // 移動先を取得
        var target = $(href == '#' || href == '' ? 'html' : href);

        // 移動先スクロール位置を数値で取得
        var position = target.offset().top - (ScrollChange.header_offset - 5);

        $('body,html').animate({scrollTop:position}, ScrollChange.scroll_speed);
        return false;
      });
    }
});

 

以上です。せっかく作ったので公開してみました。

 

関連タグ: 

この記事をシェアする:

Author
この記事を書いた人:阿部 正幸

KDDIウェブコミュニケーションズ
クラウドホスティング事業本部 エバンジェリスト

CPIスタッフブログ編集長。ACE01,SmartReleaseをリリース後、現職の「エバンジェリスト」として、web制作に関する様々なイベントに登場

Line@登録よろしくお願いします

Web制作に関する情報や、CPIノベルティのプレゼント、サーバーの無償提供などを定期的に発信しています。
ぜひ、登録ください。