スクロールとナビゲーションを連動させて現在地点を表示するスクリプト

スクロールすると現在の位置に合わせてナビゲーションの表示を変化させるスクリプトの作り方を紹介します。

別の言い方をすると、

ページ内リンクでのナビゲーションがあり、スクロール位置がそのナビゲーションが示す地点までくると、ナビゲーションの表示が変化するものです。

ランディングページのように長いWEBサイトのナビゲーションに便利なエフェクトになります。

jQueryを使った作り方になります。
では解説していきます。

出来上がりのイメージ

まずは先ほど言葉で説明したものを図にして、具体的にイメージしてみます。

図のように、現在ページのどこを見ているのかがナビゲーションの表示で判ります。

このような表現は色々なWEBサイトでみかけますし、テンプレートなどではデフォルトで設定されているものも多いでしょう。

これを簡単なスクリプトで実現します。

スクリプトを書く前に動作手順を明確にする

実際にスクリプトを書く前に、どのような機能を作れば実現できるかを言葉で考えていきます。

HTMLにある要素

まず、HTML上の要素は以下の2種類で考えます。
1.ナビゲーション(ul > li >a)
2.コンテンツエリア(div)

ナビゲーションとコンテンツエリアが複数あり、お互い同数とします。

取得する値

取得しておきたい値はこちらになります。

・現在のスクロール位置
・コンテンツエリアの始まりの位置
・コンテンツエリアの終わりの位置

動作を最小限で考えた場合

これらの要素と値を使い、最小限の動作を考えるとこのようになります。

「現在のスクロール量が、コンテンツエリアAの始まりの位置を超えたら、ナビゲーションAにclass=”now”を付与し、終わりの位置を超えたらclass=”now”を削除する」

この動作で、ナビゲーションAとコンテンツエリアAの対応関係は出来上がります。

さらに、これをAだけでなく、B C …といくつでも設置できるようにします。

複数のナビゲーションとコンテンツエリアがあると、お互いの対応関係を証明しないといけません。
つまり、ナビAにclassが付くのはコンテンツAのとき、 ナビBにclassが付くのはコンテンツBのとき、としたいわけです。

この対応関係は、ナビのaタグにあるhrefの値と、コンテンツのidの値を比較し合致していればclassが付くようにします。
ページ内リンクですから、この2つはhrefの#を除けば同じ文字列になっているはずです。

実際のスクリプトの書き方

これまで文字と図で解説した機能は、以下のスクリプトで動きます。

HTML

<!-- navi -->
<ul class="navi_list">
<li class="navi_item"><a href="#Box1">ナビ1</a></li>
<li class="navi_item"><a href="#Box2">ナビ2</a></li>
<li class="navi_item"><a href="#Box3">ナビ3</a></li>
</ul>

<!-- box1 -->
<div class="content_box" id="Box1">Box1</div>

<!-- box2 -->
<div class="content_box" id="Box2">Box2</div>

<!-- box3 -->
<div class="content_box" id="Box3">Box3</div>

javaScript(jQuery)

//ナビとエリアとその数を取得
var naviItem = $('li.navi_item');
var boxItem = $('div.content_box');
var naviNum = naviItem.length;
var boxNum = boxItem.length;
//いくつ増えてもいいように配列を用意
var naviItemElm = new Array();
var boxItemElm = new Array();
var itemKey = new Array();
var boxKey = new Array();
var distance = new Array();
var height = new Array();
var endPoint = new Array();

//スクロールするたびに以下が起動
$(window).on('load scroll', function() {
  //スクロール量を取得
    $scrollTopDistance = $(window).scrollTop();
  //ナビの数だけ以下を繰り返す
    for(var a = 0; a < naviNum; a++){
        naviItemElm[a] = naviItem.eq(a);
        boxItemElm[a] = boxItem.eq(a);
    //hrefから#の文字を除く
        itemKey[a] = naviItemElm[a].find('a').attr('href').replace(/#/g,"");
        boxKey[a] = boxItemElm[a].attr('id');
    //-20で反応する位置を調整
        distance[a] = boxItemElm[a].offset().top - 20;
        height[a] = boxItemElm[a].outerHeight();
        endPoint[a] = distance[a] + height[a];
       //もし、hrefとidが同じで、現在スクロールがボックスの開始地点より大きく、終了地点より小さい場合、class="now"を付与する。
        if(itemKey[a] == boxKey[a] && $scrollTopDistance > distance[a] && $scrollTopDistance < endPoint[a]){
            naviItem.eq(a).addClass('now');
        }else if($scrollTopDistance < distance[a] || $scrollTopDistance > endPoint[a]){
            naviItem.eq(a).removeClass('now');
        }
    }
});

CSS

CSSはご自由に書いてください。
ナビに付与されるclassはnowと設定していますが、これも変更可能です。

スクリプトのポイント

単純なスクリプトですが、ポイントは以下のことがあげられます。

  • いくつ増えても対応できるように配列を使用
  • ナビのhrefとコンテンツエリアのidを比較するため、hrefから#を除いて取得
  • forループで変数aを配列の[a]と.eq(a)にあてて複数ある要素を個別に処理

まとめと注意点

いかがでしょうか。

冒頭にも述べましたが、ランディングページなどの長いWEBページで活用できる機能だと思います。

注意点、というか弱点は以下の点です。

1.スクロールするたびにナビの数だけこの処理が回るので、処理の回数が多くなる。

2.ページ最下部までスクロールしても、 コンテンツエリアの始まり部分がページの頭に来ない場合、クラスが付与されない。
コンテンツエリアの下にフッターなどがあれば問題ありません。

これらに注意しつつ、使用してみてください。

この記事をシェアする

  • Twitterにシェア
  • facebookにシェア
  • LINEにシェア
  • POCKETにシェア