1. ホーム
  2. Web プログラミング
  3. CSS/HTML

CSS非制御位置固定の説明

2022-01-19 01:46:10

無効な位置:固定

多くの場合、position:fixed は失敗します。MDN はこの状況を一文にまとめています。

要素の祖先が none 以外の transform 属性を持っている場合、位置決めコンテナはビューポートからその祖先に変更されます。

えっ!こんなことが可能なのでしょうか?上の文の意味が分からない人もいるかもしれませんが、平たく言うと、position:fixed を持つ要素は、その祖先が none 以外の transform 値を持つ場合、その祖先に対して相対的に位置決めされるということです。

では、なぜこのようなことが起こるのでしょうか。ビューポートの相対的な位置決めについてはどうでしょうか?

この疑問から、スタッキングコンテキスト(Stacking Context)という考え方に行き着きます。上記の問題を説明するには、2つのステップを踏む必要があります。

1. none でない変換値は、Stacking Context と Containing Block を作成することになります。

2. スタッキングコンテキストの作成は、その子要素の固定配置に影響します。position:fixed の子要素は、ビューポートではなく、この親要素に基づいて位置決めされます。

Stacking Context -- スタッキングコンテキスト

おやおや、スタッキングコンテキストという新語がありますが、何でしょう?

スタッキングコンテキスト。スタッキングコンテキストとは、ウィンドウやウェブページに面したユーザーに対して仮想的なz軸上に広がるHTML要素の3次元的な概念で、HTML要素は自身のプロパティに基づく優先順位でスタッキングコンテキストの空間を占有する。

この概念は抽象的であり、スタッキングコンテキストを生成する要素は、その要素のカスケード関係や位置関係に影響を与えることを覚えておくと、簡単に理解することができます。

スタッキングコンテキストを生成する要素が、その要素のカスケード関係にどのように影響するかについては、こちらの記事 What do you know about stacking level and stacking context を参照してください。

そして、この記事では、Stacking Contextを生成する要素がその要素の位置関係に影響を与えることに言及しています。これによると、スタッキングコンテキストが生成されると、その要素はその子の固定的な位置関係に影響を与えるということです。position:fixedの子要素は、ビューポートに基づく位置ではなく、その親要素に基づく位置になります。

そこで質問ですが、スタッキングコンテキストを生成できる要素はすべて、その子のposition:fixedが、ビューポートに対する相対位置ではなく、それに対する相対位置になるのでしょうか?

スタッキングコンテキストの作成方法

これを行うには、まず、要素がスタッキングコンテキストを生成することを可能にするすべてのメソッドを見つけます。

では、ある要素をトリガーにしてスタッキングコンテキストを形成するにはどうすればよいのでしょうか。その方法は以下の通りです(MDNより参照)。

1. ルート要素(HTML)。

2.z-indexが"auto"以外の値で絶対/相対位置指定。

3. フレックスアイテム(flex item)の z-index が "auto" 以外の場合、すなわち、親要素の display: flex|inline-flex

4. opacity 属性の値が 1 未満の要素(opacity の仕様を参照)、その

5. transform 属性の値が "none" でない要素。

6. mix-blend-mode 属性が "normal" でない要素。

7. フィルタの値が "none" でない要素。

8. 透視値が "none" でない要素。

9. isolation 属性が "isolate" に設定されている要素。

10. 位置:固定

11. CSSのプロパティは、直接値を指定しなくても、すべてwill-changeで指定されます

12. -webkit-overflow-scrolling 属性が "touch" に設定されている要素。

次に、上記の属性スタイルのいずれかが設定されているすべての要素が、子要素の position: fixed を無効にする機能を持っているかどうかを確認したいと思います。

そのために、最新のBlinkカーネルをベースに以下のようなちょっとした実験をしてみました。それは、つつくことができます。

固定位置に対するカスケードコンテキストの効果(ブラウザによって異なる可能性あり)

h1 {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 999;
    height: 10vh;
    line-height: 10vh;
    color: #333;
    font-size: 1.5vw;
}

select {
    height: 10vh;
    line-height: 10vh;
    font-size: 1vw;
    outline: none;
    border: 1px solid #333;
}

.container {
    width:10vw;
    height: 10vw;
    // transform: translate3d(5vw, 5vw, 0);
    // will-change: transform;
    background: rgba(255, 100, 100, .8);
}

.fixed {
    position: fixed;
    top: 1vw;
    left: 1vw;
    right: 1vw;
    bottom: 1vw;
    background: rgba(100, 100, 255, .8);
}

.g-absolute {
    position: absolute;
    z-index: 10;
}

.g-flex {
    display: flex;
    z-index: 10;
}

.g-opacity {
    opacity: .5;
}

.g-transform {
    transform: translate3d(0, 0, 0);
    // transform: scale(1);
}

.g-blend {
    mix-blend-mode: screen;
}

.g-filter {
    filter: blur(5px);
}

.g-perspective {
    perspective: 1000px;
}

.g-isolation{
    isolation: isolate;
}

.g-fixed {
    position: fixed;
}

.g-willChange {
    will-change: transform;
}

.g-scrolling {
    -webkit-overflow-scrolling: touch;
}

.g-backface {
    backface-visibility: hidden;
}

.g-preserve3d {
    transform-style: preserve-3d;
}

.g-contain {
    contain: paint;
}

<div class="container"> 
  <div class="fixed"> </div>
</div>

(function () {
  let old = "";
  let dom = $(".container");

  $('#select').on("change", function (e) {
    let current = $(this).val();
    dom.removeClass(old).addClass(current);
    old = current;
  });
})();

2 つの親子 div を設定し、子要素を固定配置し、親要素を修正してカスケード コンテキストを生成することによって、子要素の固定配置がビューポートに対して相対的でなくなったかどうかを確認します。

<div class="container">
  <div class="fixed"> </div>
</div>

初期CSSです。

.container {
    width:10vw;
    height: 10vw;
    background: rgba(255, 100, 100, .8);
}
 
.fixed {
    position: fixed;
    top: 1vw;
    left: 1vw;
    right: 1vw;
    bottom: 1vw;
    background: rgba(100, 100, 255, .8);
}

position:fixedが失敗する究極の理由とは?

上記の最新の Blink カーネルで実験したところ、カスケード コンテキストを生成するすべての要素が position:fixed を無効にするわけではなく、transform だけが position:fixed を無効にするわけでもないことが判明しました。

そのため、MDNによるposition:fixedの追加記述は完全ではありません。現在、以下の3つのアプローチはいずれもposition:fixedに位置するベースライン要素を変更しています(本記事の焦点です)。

1. transform属性がnoneでない要素

2. 遠近法が none 以外の値を持つ要素

3. 任意のCSSプロパティがwill-changeで指定される。

コアによって異なるパフォーマンス

もういいですか?いいえ、まだです。他のカーネルでどのように動作するか見てみましょう。

上記の結論は、最新のChromeブラウザ(Blinkカーネル)で出したものですが、検証の結果、MAC下のSafari(WebKitカーネル、Version 9.1.2(11601.7.7)) とIE Trident/カーネル、Edgeブラウザの位置:固定のパフォーマンスでは上記のいずれの変更もないことが判明しています

ですから、position: fixedの位置決めベース要素の変更に遭遇した場合は、一般論ではなく、より具体的に、適応すべきブラウザに基づいて調整するようにする必要があるのです。

position:fixedのその他の問題点

もちろん、position: fixedはモバイルではヘッダーとボトムモジュールの位置関係で問題があります。また、position: fixedでinputを使用した場合の問題もありますが、これは多くの記事で紹介されており、解決策も多いので、今回は割愛します。

以上、CSS uncontrolled position fixedについて詳しく説明しましたが、CSS uncontrolled position fixedについては、スクリプトハウスの他の関連記事にもご注目ください!CSS uncontrolled position fixedの詳細については、こちらをご覧ください。