jQueryのscrollTop()にhtmlかbodyどちらを指定すればよいのか?

今まであまり深く考えていませんでしたが、 scrollTop() を使う対象要素に html を使うか body を使うか、どんな仕様になっているのか気になったので調べてみました。

両方指定すると


$('html, body').animate({
  scrollTop: 0
}, 500, function() {
  // ここのコールバックが2回呼ばれる
});

htmlbody かどちらが動作するかわからないので、このように両方記述していたと思います。しかし、これだと コールバック関数が2回呼ばれる 現象が起こります。これを解決するには、ブラウザが対応している方を指定する必要がでてきます。

検証してみた

html body document.scrollingElement
IE8~11 非対応
Edge body ( document.body
Chrome 43- 非対応
Chrome 44~60 body ( document.body
Chrome 61+ html ( document.documentElement
Firefox 47- 非対応
Firefox 48+ html ( document.documentElement
Opera 30- 非対応
Opera 31~47 body ( document.body
Opera 48+ html ( document.documentElement
Safari 7~8 非対応
Safari 9+ body ( document.body
Android 4.3- 非対応

htmlbody どちらなら動作するかざっと調べました。また、モダンブラウザでは、 document.scrollingElement という スクロールできる要素 を教えてくれるプロパティがあります。それについても、どのバージョンから対応しているか調べました。

html body document.scrollingElement
IE8~11 非対応
Chrome 43- 非対応
Firefox 47- 非対応
Opera 30- 非対応
Safari 7~8 非対応
Android 4.3- 非対応

document.scrollingElement が対応しているブラウザはそれを使えばいいので、非対応のみ抜き出してみました。IEFirefox 47-html で、それ以外は body とすれば良さそうですね。

JSで判定


var scrollableElement;
var firefox = navigator.userAgent.match(/Firefox\/([0-9]+)\./);

// document.scrollingElementに対応していればそれを使う
if ('scrollingElement' in document) {
  scrollableElement = document.scrollingElement;
}
// IEのとき
else if (/*@cc_on!@*/false || (!!window.MSInputMethodContext && !!document.documentMode)) {
  scrollableElement = document.documentElement;
}
// Firefox 47-のとき
else if (firefox && parseInt(firefox[1]) <= 47) {
  scrollableElement = document.documentElement;
}
// それ以外
else {
  scrollableElement = document.body;
}

先ほどの検証結果をもとに、条件分岐させます。 Firefox 47- についてはまあまあ古いので、対応しなくてもいいかもしれません。


var scrollableElement;
var firefox = navigator.userAgent.match(/Firefox\/([0-9]+)\./);

if ('scrollingElement' in document) {
  scrollableElement = document.scrollingElement;
} else if (/*@cc_on!@*/false || (!!window.MSInputMethodContext && !!document.documentMode)) {
  scrollableElement = document.documentElement;
} else if (firefox && parseInt(firefox[1]) <= 47) {
  scrollableElement = document.documentElement;
} else {
  scrollableElement = document.body;
}

// ページトップへ
$(scrollableElement).animate({
  scrollTop: 0
}, 500, function() {
  // コールバック
});

あとは、 scrollableElement を指定すればよいです。 scrollableElement があるおかげで今後新しいバージョンで変更があっても コードを修正する必要 がなくなるので非常にありがたいですね。