offset()の値が正しく取れない -Safari-

jQueryロゴ

今回は、追尾型のメニュー、特にページ途中にあるグローバルナビゲーションが、画面をスクロールをすると上部に固定されて、ページ下まで付随してくるモノを設置する時に起きやすい不具合を記載して行きたいと思います。

追尾型のメニュー(スクロールしてもついてくるメニュー)設置したけど、動作・反応する位置が早いとお困りの方に役立つと思います。

1.追尾型メニューのjsコード

jsファイル

$(function($) {
var nav = $('#fixed_menu'),
offset = nav.offset(); // メニュー部分の位置情報を取得
$(window).scroll(function () {
    if($(window).scrollTop() > offset.top) { // スクロールした距離が、ページ上部からナビまでの距離を超えた場合
        nav.addClass('fixed');
    }else { // スクロールした距離が、ページ上部からナビまでの距離より少ない場合
        nav.removeClass('fixed');
    }
});
});

通常、追尾型のメニューを設置する時のjsコードは上記のようなコードを使うコトが多いと思います。 ページの1番上の部分にグローバルナビゲーションがあれば、特別問題はないのですが、ページの途中にナビがある場合、特に画像がナビの上部にある場合は、ブラウザがSafari(iPad,iPhoneなども)だと早い位置で反応してしまい、上手く動作してくれません。

デモ

上記のデモは追尾型メニューのデモ画面です。
このデモページにIE,Firefox,Chromeなどでアクセスし、ページの下へとスクロールするとナビがある部分に達した時に正常にメニューがそこから追尾されるのですが、Safariでアクセスした場合、少しスクロールしただけで、上部にナビが貼り付いてしまいます。

※IE,Firefox,Chromeでもキャシュがない状態、初めてページを閲覧する際には不具合が起きます。

2.正常に動作しない原因

デモ画面を表示[マウス右クリック]→[要素を調査(Chromeなら検証)]で開発ツールを出してください。
どこかに「コンソール」と言う部分があると思うので、そこをクリックします。
その後、ブラウザをF5キーを押すなどして更新してください。

コンソール画面 Firefox

上記の画像はFirefoxの開発ツール画面です。
top:494.5166…と表示されている数字は上記jsコードの「// メニュー部分の位置情報を取得」と記載されている部分「nav.offset()」で取得したグローバルナビゲーションの位置をページの1番上からの距離として表示しています。

コンソール画面 Safari

上記の画像はSafariの開発ツール画面です。 top:28となっています。
つまり、Safariではグローバルナビゲーションの位置はページの1番上から28pxの距離にあります。と認識されている訳です。

ゆえに、Firefoxなどのブラウザでは494pxスクロールされた所で追尾が始まるのですが、Safariでは28px進んだ所で追尾が始まってしまう訳です。

この現象が起きてしまう原因は、画像や動画などのデータの読み込みが終わる前にoffset()の実行がされてしまうため、画像などが無いものとして距離を算出されてしまうのです。
よって、解決方法としましては画像などデータを全て読み終わってから実行する命令にすれば良い訳です。
それには、onloadを使用します。

jsファイル

$(window).on('load',function(){$(function($) {
var nav = $('#fixed_menu'),
offset = nav.offset();
$(window).scroll(function () {
    if($(window).scrollTop() > offset.top) {
        nav.addClass('fixed');
    }else {
        nav.removeClass('fixed');
    }
});
});
});

こんな感じです。

デモ

コンソール画面 Safari

このようにSafariでもtop:494となりました。

もちろんこのやり方であれば、IE,Firefox,Chromeのキャッシュがない状態でも不具合は起きません。

3.その他、offset()で考えられる不具合

CSSでzoomを使用している場合など、正しい値を取得するコトが出来ない場合があります。
その場合は今回のコードを使うと、このように書き換えます。

$(function($) {
var nav = $('#fixed_menu'),
offset = document.getElementById('fixed_menu').offsetTop;
$(window).scroll(function () {
    if($(window).scrollTop() > offset) {
        nav.addClass('fixed');
    } else {
        nav.removeClass('fixed');
    }
});
});

3行目がjQueryではなくネイティブのoffsetTopになっているのが、わかると思います。
zoomに限らず、思った値が取得出来ない時はネイティブに書き換えてみましょう。

Categories: jQuery タグ: