アンカーリンク〜様々な状況に対応できるJavascript記述方法〜
アンカーリンクにはページ内リンクや、ページをまたいで特定の場所にリンクさせる方法があります。特定の場所へリンクさせることができるので便利ですが、ヘッダーなどがフローティングでページ上部に固定されている場合は、何も対策をしないとアンカー位置がそれらのナビと重なってしまいます。今回はその対処法を紹介したいと思います。
デモページ
デモページは前提として、ヘッダーがフローティングしていて、各ページのタグに固有のidが指定されているものとします。
source1:ページ内リンク(ページ内アンカーリンクを押した際の挙動)
$(function () {
var headH = $("header").outerHeight(); //ヘッダーの高さを取得
var animeSpeed = 500; //アニメーションスピード
$("a[href^='#']").on({
"click": function () {
var href = $(this).attr("href");
var target = $(href == "#" || href === "" ? "html" : href);
var position;
position = target.offset().top - headH; //ターゲットまでの距離からヘッダーの高さを引く
$("body,html").stop().animate({
scrollTop: position
}, animeSpeed);
return false;
}
});
});
上記のソースは、デモページのヘッダー下にあるアンカー1、アンカー2、アンカー3と並んでいる部分に対する記述です。
よく見る形かと思いますが、大切な部分は上記ソースのコメントをつけた部分です。ヘッダーの高さを引かないとスクロールした際に要素とヘッダーがかぶってしまいます。ヘッダー以外の要素をフローティグさせている場合は、
var headH = $(“header”).outerHeight();
この部分のheaderを書き換えれば大丈夫です。これを書いておくだけで大丈夫なサイトも多いと思います。
source2:遷移先ページでアンカーが有る場合(#〇〇付きのURLでページを開いた際の挙動)
$(function () {
var headH = $("header").outerHeight();
var animeSpeed = 500;
var urlHash = location.hash; //URLのハッシュタグを取得
if (urlHash) { //ハッシュタグが有る場合
$("body,html").scrollTop(0);
setTimeout(function () { //無くてもいいが有ると動作が安定する
var target = $(urlHash);
var position = target.offset().top - headH;
$("body,html").stop().animate({
scrollTop: position
}, animeSpeed);
}, 0);
}
});
上記のソースは遷移先ページで動作させるものとなります。ページ遷移をし、その中で更にページ内遷移がある場合です。デモページで説明すると、例えばTOPページを表示している状態でページ1のナビアンカー1を押した場合などです。ページ遷移後にまずlocation.hash;でURLのハッシュタグを取得します。そしてif文を使い、ハッシュタグが有る場合には source1 と同様にスクロールを行います。
今回はsetTimeoutを使用していますが、使用しなくても構造上は問題ありません。しかし書いたほうが動作が安定するため今回は記述しています。これを記述しない場合、ページ読み込み時に変数target の値がおかしくなり、スクロール位置がおかしくなってしまうことがあります。setTimeout(~,0)は時間指定を0にしているため、setTimeoutを使用しない場合と同じ処理になると思っていたのですが、調べてみたところ処理を分割するのに使えるようでした。参考サイト
source3:現在表示しているページがTOPページか下層ページかによって処理を分岐。ページ内リンクと遷移先ページでアンカーになる場合の両方に対応。
$(function () {
var headH = $("header").outerHeight();
var animeSpeed = 500;
var id = $("body").attr("id");
//グロナビアンカー
$(".nav_anc").on("click", function () {
var currentUrl = location.pathname; //URLのパス名を取得
var url1 = currentUrl.split("/"); //パスを「/」区切りで分割
var targetUrl = $(this).children("a").attr("href"); //リンク先のURLを取得(リンク先はルートパスで指定)
var url2 = targetUrl.split("/"); //リンク先URLを「/」区切りで分割
var target;
var position;
$(".pulldown").hide();
//現在地がTOPページの場合
if (id === "toppage") { //bodyにid「toppage」が有る場合
if (url2[1].match("^#")) {
target = $(url2[1]);
position = target.offset().top - headH;
$("body,html").stop().animate({
scrollTop: position
}, animeSpeed);
return false;
}
}
//現在地が下層ページの場合
if (url1[1] === url2[1]) { //それぞれ「/」で区切ったものの1つ目が一致した場合
target = $(url2[2]);
position = target.offset().top - headH;
$("body,html").stop().animate({
scrollTop: position
}, animeSpeed);
return false;
}
});
});
デモページで説明すると、例えばページ1を表示していて、ページ1内のナビアンカー1をクリックした場合は source1 と同じ挙動、ページ2内のナビアンカー1をクリックした場合は source2 と同じ挙動をさせる指定です。対象のタグにnav_ancというクラスをつけています。この場合はlocation.pathname;でURLのパス名とaタグのhrefの値を取得して、それらの値を比較して、現ページと遷移先ページが一致していたら source1 と同じ挙動になります。一致していなければJSによる挙動は行わず、通常のページ遷移が行われ、遷移先のページで source2 が実行されます。ヘッダーナビをインクルードで読み込み、ヘッダーナビのリンク指定をルートパスで書かなければならない状況において、現ページによってアンカーリンクの処理を変えたいというときに役に立つかと思います。
まとめ
今回紹介した3つのソースは別の処理を行うものですので、全て記述が必要となります。今回ご紹介したJSはWEBサイトの全てのページで共通して使用されるものですので、commonのjsに書いておけば間違いないと思います。今回紹介したsource1・2は
var headH = $(“header”).outerHeight();
の部分だけをそのサイトの仕様に合わせて変更すれば実装できます。source3は少し調整が必要になりますが、そこまで難しくなく実装できるので、試してみてください。
WEBプログラマー / K.S