web-dev-qa-db-ja.com

window.widthがメディアクエリで設定されたビューポートの幅よりも小さいのはなぜですか

私はかなり困惑していますが、これを適切な言葉で説明する方法はまだわかりません。これまでのところ、ブレークポイントを使用してメディアクエリを使用および設定しました。使用されるブレークポイント変数は次のようになります。

$menustatictofixed: min-width 900px;

$ breakpoint-to-emsはtrueに設定されています。次のjQueryスニペットのピクセル値に基づいて、ページにすべてのブレークポイント変数を配置しました。

$('body').append('<div style="position: fixed; bottom: 0; right: 0; display: inline-block; font-weight: bold; padding: 5px 7px; border-radius: 5px 0 0 0; background: green; color: white; z-index: 9999;">Viewport width <span id="width"></span> px</div>');
var viewportWidth = $(window).width()
$('#width').text(viewportWidth);
$(window).resize(function() {
  var viewportWidth = $(window).width();
    $('#width').text(viewportWidth);
});

すべてが適切できれいに見えました。しかし、過去1〜2日間で、パターンの最後のブレークポイントを設定し、予測どおりに動作するようにするのに問題がありました。どういうわけか、そもそも私が論理的に非常に疑っている最初の場所でクリーンで細かいように見えたものは実際には不適切であり、何とか混乱しています。次の例を見てみると screenshot どういうわけか(Chromeの)ウィンドウの幅は、window.widthを利用するjQueryスニペットとは異なります。最終的にスクロールバーを除外するためにwindow.widthをwindow.innerWidthに置き換えても違いはありません。適切な結果を受け取る唯一の方法は、方程式に15ピクセルを追加することです。

var viewportWidth = $(window).width() + 15;

Window.widthが関数の間違った選択であるという方程式全体での唯一の問題であり、それを使用することをお勧めします。 document.documentElement.clientWidthまたは何か他の... ...そして、15ピクセルが何のために立っているのかについて、上記の問題を少し厄介な方法で修正しましたか?よろしくラルフ

25
rpk

答えはスクロールバーであり、解決策はトリッキーです。

メディアクエリは興味深いものです。それらはブラウザ間で正確に同じようには動作しません。つまり、それらを使用するのはそれほど簡単ではない場合があります。たとえば、OS Xの-webkit/-blinkおよびWin8のIE10では、スクロールバーがページにオーバーレイされます。ただし、-mozでは、スクロールバーはページにオーバーレイされません。ページの「表示可能領域」を取得する最良の方法は、Vanilla JavaScriptの次の行です(有効なHTMLドキュメントがあると想定しています)。

document.body.parentNode.clientWidth

これにより、body要素が見つかり、その親(有効なドキュメントではhtml要素になります)が見つかり、レンダリング後の幅がわかります。これにより、見たい幅がわかります。 これも無意味な幅です

実際のクライアントの幅が役に立たないのはなぜですか?ブラウザーによって異なるためではありません。これは、widthメディアクエリが実際にテストしているものではないためです。 widthメディアクエリのテストは、window.innerWidthです。これは、現在使用しているものです。

それで、この問題の解決策は何ですか?ソリューションは、デバイスベースのメディアクエリの代わりにコンテンツベースのメディアクエリを使用し、クエリ内に小刻みな部屋があれば問題ない(特に、小部屋が15pxの場合)と思います。まだ読んでいない場合は、記事 Vexing Viewports および A Pixel Identity Crisis を読んで、MQ定義で15pxが光る可能性がある理由を理解してください。これは世界で最悪のことではありません(フライする大きな魚はおそらくいるでしょう)。

したがって、結論として、引き続き$breakpoint-to-ems: trueを使用し、コンテンツがwindow.innerWidthに分割されるタイミングに基づいてメディアクエリを選択します。これが、クロスブラウザの問題を処理する唯一の健全な方法であるためです。さまざまな問題をざっと見たところ、ほとんどのブラウザーとOSがオーバーレイスクロールバーに移動しているように見えます(- Firefox 24 現在、すべてのOS Xブラウザーには、UbuntuのUnity UI が導入されています )なので、将来的に使いやすくするために、スクロールバーのオフセットを気にせず、ブラウザ間でサイトが少し異なるように見えても問題ないようにすることをお勧めします。イーサン・マーコットが雄弁に言っているように、覚えておいてください:

ウェブは本質的に不安定な媒体です

24
Snugug

これは私にとってうまくいきました: CSSメディアクエリとJavaScriptウィンドウの幅が一致しません

スクロールバーを含む$(window).width();を使用する代わりに、次のように内側の幅を取得します。

function viewport() {
    var e = window, a = 'inner';
    if (!('innerWidth' in window )) {
        a = 'client';
        e = document.documentElement || document.body;
    }
    return { width : e[ a+'Width' ] , height : e[ a+'Height' ] };
}

var vpWidth = viewport().width; // This should match your media query
49
Justin

それは_scrollbar's_が原因です

私が見つけた最も正しい解決策は、メディアクエリを使用して実際のウィンドウサイズをJavaScriptに渡すことです。次の手順を実行する必要があります。

  • 非表示の要素をページに追加し、
  • メディアクエリを使用して、その要素の_max-width_プロパティを変更します。
  • Javascriptを使用して、その要素の_max-width_プロパティを読み戻します。

たとえば、次の要素をページに追加します。

_<div id="currentMedia"></div>
_

次に、次のCSSルールを記述します。

_#currentMedia {
    display: none;
}

@media (max-width: 720px) {
    // Make arrows in the carousel disappear...

    #currentMedia {
        max-width: 720px;
    }
}
_

次に、JavaScript側から次のように記述できます。

_if (parseInt(jQuery("#currentMedia").css("max-width"), 10) <= 720) {
    // Code HERE..
}
_

また、値はカルーセルの非表示をトリガーする同じメディアクエリから取得されるため、スクロールバーのサイズに関係なく正確です。

最近の主要なすべてのブラウザでこのソリューションをテストしたところ、正しい結果が得られました。

どのブラウザでどのプロパティがサポートされているかについての大まかな概要が見つかります quirksmode.orgのこのページ

おそらく、ページ内の要素を(サポートされている場合はdocument.body、またはdocument.getElementByIdなどを使用して)取得し、offsetParentチェーンを歩いて最上位の要素を見つけ、その要素のclientWidthとclientHeightを調べるのがおそらく最善の策です。

innerWidth ドキュメント

.innerWidth()メソッドは、windowおよびdocumentオブジェクトには適用されません。これらについては、代わりに.width()を使用してください。

@ Snugugsの回答と同様ですが、私のユースケースではより効果的でした:

CSS(私はLESSを使用しているので、@ tabletMinと@desktopMinは他の場所で設定したブレークポイント変数に変換されます:

    #cssWidth {
        display:none;
        content:'mobile';
    }

    /* Responsive styles (Tablet) */
    @media (min-width: @tabletMin) {
        #cssWidth {
            content:'tablet';
        }
        /* Other tablet CSS... */
    }
    /* Responsive styles (Desktop) */
    @media (min-width: @desktopMin) {
        #cssWidth {
            content:'desktop';
        }
        /* Other Desktop CSS... */
    }

そしてJSでは:

    getView = function() {
        // If #cssWidth element does not exist, create it and prepend it do body
        if ($('#cssWidth').length === 0) {
            $('body').prepend($(document.createElement('div')).attr('id', 'cssWidth'));
        }
        // Return the value of the elements 'content' property
        return $('#cssWidth').css('content');
    };

次に、getView()関数はメディアクエリが参照する幅に応じて、文字列「mobile」、「tablet」、または「desktop」を返します。

これを拡張して、より多くのビューポート幅に合わせることができます。CSSに他の値を使用してルールを追加するだけです。

1
Gruffy

このリンクをたどる

function getWindowWidth() {
    var windowWidth = 0;
    if (typeof(window.innerWidth) == 'number') {
        windowWidth = window.innerWidth;
    }
    else {
        if (document.documentElement && document.documentElement.clientWidth) {
            windowWidth = document.documentElement.clientWidth;
        }
        else {
            if (document.body && document.body.clientWidth) {
                windowWidth = document.body.clientWidth;
            }
        }
    }
    return windowWidth;
}
0
Amidou Zabre

@ Snugugの回答は、これがなぜ起こっているのかについて非常に良い説明を提供し、@ TusharGuptaは、mediaqueryで検出された幅をJavaScriptに参照する優れた解決策も持っています。レイアウト変更をトリガーする幅

メディアクエリをJavaScriptのピクセル幅検出と同期する必要がある場合、問題に取り組む1つの方法は、JavaScriptで追加したクラスに基づいてCSSレイアウトの変更をトリガーすることです。

したがって、メディアクエリを記述する代わりに、クラスの下にネストされた宣言を記述します。

html.myMobileBP { ... }

そして、javascript/jQueryで、次のようにこのクラスを追加します。

if ($(window).width() < myMobileBPPixelSize) {
    $("html").addClass("myMobileBP");
}

これをさらに推進するには、JavaScriptサポートの欠如を検討する必要があります。さらにhtml.no-jsクラスにラップされているメディアクエリを定義し、JavaScriptで.no-jsクラスを削除して、JavaScriptクラスを介してブレークポイントを追加する上記のソリューションを適用します。

0
kontur