スクロールすると現在の位置に合わせてナビゲーションの表示を変化させるスクリプトの作り方を紹介します。
別の言い方をすると、
ページ内リンクでのナビゲーションがあり、スクロール位置がそのナビゲーションが示す地点までくると、ナビゲーションの表示が変化するものです。
ランディングページのように長い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.ページ最下部までスクロールしても、 コンテンツエリアの始まり部分がページの頭に来ない場合、クラスが付与されない。
コンテンツエリアの下にフッターなどがあれば問題ありません。
これらに注意しつつ、使用してみてください。