ページ内リンクへプラグインを使わずにアニメーションで移動するスムーズスクロールに関するメモ。
画面をスクロールさせる場合 animate メソッドと scrollTop プロパティを利用するが、どのセレクタに対して実行すべきかはブラウザ(WebKit かそれ以外)により異なる。
WebKit : body 要素
それ以外 : html 要素
$('body').animate({ scrollTop: 0 }); // WebKit
$('html').animate({ scrollTop: 0 }); // WebKit以外
以下のように両方を指定して全てのブラウザに対応することができるが、単純なスクロールだけなら問題はないが、両方指定するとコールバックを指定している場合、コールバックが2回呼ばれてしまう。
$('html, body').animate({ scrollTop: 0 });
そのためブラウザを判定する必要があるが、jQuery 1.9 になって $.browser が使えなくなっているので、「使用したい機能が機能するか」で判定する。
参考:「jQuery 1.9 で $.browser が使えなくなってしまった対策」
スクロールさせることができる要素が html か body かを判定するには、実際にどちらの要素でスクロールするかを調べる。
var isHtmlScrollable = (function(){
var html = $('html'), top = html.scrollTop();
var elm = $('<div/>').height(10000).prependTo('body');
html.scrollTop(10000);
var rs = !!html.scrollTop();
html.scrollTop(top);
elm.remove();
return rs;
})();
//スクロールのアニメーション
$(isHtmlScrollable ? 'html' : 'body').animate({scrollTop:100});
以下は全て上記の関数(isHtmlScrollable)の記述を前提。
href 属性の値が「#」の場合、ページトップへアニメーションで移動する。
jQuery(document).ready(function($) {
$('a[href=#]').click(function () {
$(isHtmlScrollable ? 'html' : 'body').animate({
scrollTop: 0
}, 500);
return false;
});
});
注意
但し、href 属性の値が「#」でも、上記のアニメーションを適用したくないものもあるので、それらに関しては not() を使って除外する。例えばこのサイトの場合、ナビゲーションの「その他」は階層メニューになっているので、上記を使用すると階層メニューが機能しなくなるので、以下のようにする。それらがあまりに多い場合は、アニメーションするものには、クラスを付けるなど考える必要があるかもしれない。
jQuery(document).ready(function($) {
$('a[href=#]').not('#header-nav a.dropdown-toggle').click(function () {
$(isHtmlScrollable ? 'html' : 'body').animate({
scrollTop: 0
}, 500);
return false;
});
});
ある要素の id 属性へのリンクへの移動。<a href=”#someElement” > のような場合にも対応するには以下のようにしてみる。
jQuery(document).ready(function($) {
$('a[href^=#]').not('アニメーションさせない a 要素').click(function(){
var hrefval= $(this).attr('href');
var targetelement = hrefval == "#" ? $('html') : $(hrefval);
var positiontop = targetelement.offset().top;
$(isHtmlScrollable ? 'html' : 'body').animate({
scrollTop: positiontop
}, 500);
return false;
});
});
ある程度以上スクロールすると「先頭へ」というリンクを表示させ、クリックするとページ先頭にアニメーションで移動する。
jQuery(document).ready(function($) {
$('#footer').after('<div class="tothetop"><a href="#">先頭へ</a></div>');
var topBtn = $('.tothetop');
topBtn.hide();
$(window).scroll(function () {
if ($(this).scrollTop() > 500) {
topBtn.fadeIn();
} else {
topBtn.fadeOut();
}
});
//ページ内リンクとページトップへの移動
$('a[href^=#]').not('アニメーションさせない a 要素').click(function(){
var hrefval= $(this).attr('href');
var targetelement = hrefval == "#" ? $('html') : $(hrefval);
var positiontop = targetelement.offset().top;
$(isHtmlScrollable ? 'html' : 'body').animate({
scrollTop: positiontop
}, 500);
return false;
});
});
「ページ先頭へ」というリンクのCSSの例
div.tothetop {
position: fixed;
right: 5%;
bottom: 5%;
z-index: 1500;
}
div.tothetop a {
display: block;
font-weight: bold;
color: #FFF;
padding: 10px;
margin: 0;
background-color: #AAA;
font-size: 0.8em;
outline: none;
text-decoration: none;
/* Firefox v1.0+ */
-moz-border-radius:6px ;
/* Safari v3.0+ and by Chrome v0.2+ */
-webkit-border-radius:6px ;
/* Firefox v4.0+ , Safari v5.0+ , Chrome v4.0+ , Opera v10.5+ and by IE v9.0+ */
border-radius:6px ;
}
div.tothetop a:hover {
color: #9CF;
background-color: #666;
}
以下は移動する距離に応じてスピードを調整(速くなり過ぎないなど)して、ページ内リンクへの移動の場合は、ナビゲーションが常に表示されているため、位置を調整する例。
jQuery(document).ready(function($) {
$('a[href^=#]').not('アニメーションさせない a 要素').click(function(){
var hrefval= $(this).attr('href');
var positiontop;
var speed;
if(hrefval == "#") {
positiontop = 0;
speed = 200 + $(this).offset().top /30;
}else{
var targetelement = $(hrefval);
positiontop = targetelement.offset().top -120;
speed = 200 + (positiontop + 120) / 30;
}
$(isHtmlScrollable ? 'html' : 'body').animate({
scrollTop: positiontop
}, speed);
return false;
});
});
ページ内リンクではなく、外部ページでもスムーズスクロールする方法。
var isHtmlScrollable = (function(){
var html = $('html'), top = html.scrollTop();
var elm = $('<div/>').height(10000).prependTo('body');
html.scrollTop(10000);
var rs = !!html.scrollTop();
html.scrollTop(top);
elm.remove();
return rs;
})();
var hash = window.location.hash;
if (hash != "") {
var target = $(hash);
var pos = target.offset().top - 100;
$(isHtmlScrollable ? 'html' : 'body').animate({
scrollTop: pos
}, 500, "swing");
}
注意する点としては、ページ内リンクの場合はページが全て読み込まれている(jQuery の処理なども終了している)が、外部ページの場合は記述する位置によっては、他の jQuery の処理などに影響されて期待した位置にスクロールされない可能性がある。その場合は、記述する位置を最後の方に移して見るのも1つの方法。