ぺらサイトでは、見出しのアンカーリンクにメニューからスムーススクロールする構成をよく見かけます。
ぺらサイト以外でもページ内の見出しにアンカーリンクをつけ、ページ外からリンクを張るケースは少なくありません。
その時に問題になることがいくつかあります。
- 固定ヘッダーがあるとアンカーのとび先がヘッダーに隠れてしまう
- アンカーリンクにページ名が入っている場合、ページ内リンクでもスムーススクロールのjsスクリプトがエラーになる
- ページ外のアンカーリンクへのスムーススクロールができない
smooth-scroll.jsでは対応できないの?
固定ヘッダーに対応しているjs pluginとしてsmooth-scroll.jsが良く紹介されています。
確かに、smooth-scroll.jsを使うと、ほとんどの問題は解決できるし、自前でコードを書かなくていいのはメリットが大きい。
しかし、smooth-scroll.jsで唯一問題となる点があった。
ページ外アンカークリック⇒固定ヘッダーが考慮されず隠れてしまう点である。
- 固定ヘッダーがあるとアンカーのとび先がヘッダーに隠れてしまう問題
⇒〇ページ内は固定ヘッダー対応可能
⇒✖ページ外は固定ヘッダー考慮されない - アンカーリンクにページ名が入っている場合、ページ内リンクでもスムーススクロールのjsスクリプトがエラーになる問題
⇒〇ページ内、ページ外もOK - ページ外のアンカーリンクへのスムーススクロールができない問題
⇒✖ページ外のスムーススクロールは未対応
①固定ヘッダーがあるとアンカーのとび先がヘッダーに隠れてしまう
アンカーリンクをクリックすると、とび先はページ先頭に表示されます。しかし、固定ヘッダーがある場合はアンカーのとび先が固定ヘッダーの下に隠れてしまいます。
この問題を解消するのは、CSSで行う方法とJSで行う方法があります。個人的にはCSSの方法は見出しの前後のリンクをクリックできなくなるなど、制限があり使いにくい印象があります。
JSによる方法は、JSのなかで飛び位置を固定ヘッダー分ずらす処理を行うものです。
通常、スムーススクロールするJSの中でとび先をずらすことで実現できます。
②アンカーリンクにページ名が入っている場合、ページ内リンクでもスムーススクロールのjsスクリプトがエラーになる
①を踏まえて、アンカーリンクのスムーススクロールを行うJavascriptは検索すると簡単に見つかります。
例えば以下のようなものです。
$(function(){
$('a[href^="#"]').click(function(){
let speed = 500;
let href= $(this).attr("href");
let target = $(href == "#" || href == "" ? 'html' : href);
let position = target.offset().top;
$("html, body").animate({scrollTop:position}, speed, "swing");
return false;
});
});
しかしこのスクリプトは、リンクにページ名などが付いている場合はエラーになります。
ページ名を付けないと外部ページのリンクが張れません。グローバルメニューやフッターなどにアンカーリンクを置く場合はページ名が必要になりますので、そちらでもエラーになります。
ページ名が入っていてもエラーにならないスムーススクロールするJavascriptが必要になります。
$(function(){
$('a[href*=#], area[href*=#]').not(".noScroll").click(function() {
var speed = 400, // ミリ秒(この値を変えるとスピードが変わる)
href = $(this).prop("href"), //リンク先を絶対パスとして取得
hrefPageUrl = href.split("#")[0], //リンク先を絶対パスについて、#より前のURLを取得
currentUrl = location.href, //現在のページの絶対パスを取得
currentUrl = currentUrl.split("#")[0]; //現在のページの絶対パスについて、#より前のURLを取得
//#より前の絶対パスが、リンク先と現在のページで同じだったらスムーススクロールを実行
if(hrefPageUrl == currentUrl){
//リンク先の#からあとの値を取得
href = href.split("#");
href = href.pop();
href = "#" + href;
//スムースクロールの実装
var target = $(href == "#" || href == "" ? 'html' : href),
position = target.offset().top, //targetの位置を取得
body = 'body',
userAgent = window.navigator.userAgent.toLowerCase();
if (userAgent.indexOf('msie') > -1 || userAgent.indexOf('trident') > -1 || userAgent.indexOf("firefox") > -1 ) { /*IE8.9.10.11*/
body = 'html';
}
$(body).animate({
scrollTop: position
}, speed, 'swing', function() {
//スムーススクロールを行ったあとに、アドレスを変更(アドレスを変えたくない場合はここを削除)
if(href != "#top" && href !="#") {
location.href = href;
}
});
return false;
}
});
});
③ページ外のアンカーリンクへのスムーススクロールができない
javascriptのスムーススクロールは、ページ外のアンカーリンクのスムーズスクロールには対応していません。
これはよく知られた制限なのですが、解決方法も知られています。
//固定ヘッダーの指定
var headerHeight = $('#header').outerHeight();
// ページ外アンカーのページ付きリンクへのスムーススクロール
var urlHash = location.hash;
if(urlHash) {
$('body,html').stop().scrollTop(0);
setTimeout(function(){
var target = $(urlHash);
var position = target.offset().top - headerHeight;
$('body,html').stop().animate({scrollTop:position}, 500);
}, 100);
}
全ての問題を解決するアンカースムーススクロールスクリプト
いままでの①~③までの解決策をまとめたのが以下のスクリプトにになります。
脱Jqueryと叫ばれていますが、直ぐに動くものを作る方が100倍エライと私は思っていますので、Jqueryでのソリューションは今でも使えると思っています。
//固定ヘッダーの指定
var headerHeight = $('#header').outerHeight();
// ページ外アンカーのページ付きリンクへのスムーススクロール
var urlHash = location.hash;
if(urlHash) {
$('body,html').stop().scrollTop(0);
setTimeout(function(){
var target = $(urlHash);
var position = target.offset().top - headerHeight;
$('body,html').stop().animate({scrollTop:position}, 500);
}, 100);
}
// ページ内アンカーのページ付きリンクへのスムーススクロール
$(function(){
$('a[href*="#"], area[href*="#"]').not(".noScroll").click(function() {
var speed = 400, // ミリ秒(この値を変えるとスピードが変わる)
href = $(this).prop("href"), //リンク先を絶対パスとして取得
hrefPageUrl = href.split("#")[0], //リンク先を絶対パスについて、#より前のURLを取得
currentUrl = location.href, //現在のページの絶対パスを取得
currentUrl = currentUrl.split("#")[0]; //現在のページの絶対パスについて、#より前のURLを取得
//#より前の絶対パスが、リンク先と現在のページで同じだったらスムーススクロールを実行
if(hrefPageUrl == currentUrl){
//リンク先の#からあとの値を取得
href = href.split("#");
href = href.pop();
href = "#" + href;
//スムースクロールの実装
var target = $(href == "#" || href == "" ? 'html' : href),
position = target.offset().top - headerHeight; //targetの位置を取得
$('body,html').stop().animate({scrollTop:position}, 500);
return false;
}
});
});
アンカーリンクが正常に動かない時は?
マウスのホイールが止まっていないと正常に動きませんので、マウスのホイールが停止していることを確認してみましょう。
まとめ
ペラサイトの場合はあまり問題にならないアンカーリンクですが、ページが増えてくると一気に問題が噴出します。
そのようなときには今回の記事を思い出して使えそうなら使ってみてください。