ターゲット位置:現在「スタック」状態にあるスティッキー要素


109

位置:スティッキーは一部のモバイルブラウザーで機能するようになりました。メニューバーをページと共にスクロールさせ、ユーザーがスクロールすると常にビューポートの上部に固定することができます。

しかし、現在「固定」されているときに、固定メニューバーを少しだけスタイル変更したい場合はどうでしょうか。たとえば、ページと一緒にスクロールするときはいつでもバーの角を丸くしたいかもしれませんが、バーがビューポートの上部にくっついたらすぐに、上部の丸い角を取り除き、その下に小さなドロップシャドウを追加しますそれ。

現在固着::stuckしている要素をターゲットにするための何らかの種類の疑似セレクター(など)はposition: sticky ありますか?または、ブラウザーベンダーはパイプラインでこのようなものを持っていますか?そうでない場合、どこでリクエストすればよいですか?

NB。モバイルでは通常scroll、ユーザーが指を離したときにイベントが1つしか取得されないため、JavaScriptソリューションは適していません。そのため、JSはスクロールのしきい値を超えた正確な瞬間を知ることができません。

回答:


104

現在「スタック」している要素に対して提案されているセレクターは現在ありません。Postionedレイアウトモジュールposition: stickyに定義されているいずれかの任意のそのようなセレクタを言及していません。

CSSの機能リクエストは、wwwスタイルのメーリングリストに投稿できます。私は信じて:stuck疑似クラスは、より多くの意味になり::stuckますが、彼らはその状態にある間の要素自体をターゲットに探していることから、擬似要素を。実際、:stuck疑似クラスについては先ほど説明しました。主な問題は、レンダリングされたスタイルまたは計算されたスタイルに基づいて照合しようとするすべての提案されたセレクター、つまり循環依存関係を悩ませる問題であることがわかりました。

以下の場合に:stuck疑似クラス、円形の最も単純な場合は、以下のCSSで起こります。

:stuck { position: static; /* Or anything other than sticky/fixed */ }
:not(:stuck) { position: sticky; /* Or fixed */ }

また、対処するのが難しいエッジケースがさらに多くなる可能性もあります。

特定のレイアウト状態に基づいて一致するセレクターを用意するのは良いことと一般的には同意されていますが、残念ながら、これらを実装するのは簡単ではありませ。私はこの問題の純粋なCSSソリューションにすぐに夢中になることはありません。


14
残念です。私もこの問題の解決策を探していました。セレクターのpositionプロパティは:stuck無視する必要があるというルールを簡単に導入するのは簡単ではないでしょうか?(leftright
つまり

5
それは単なる位置ではありません...がからに:stuck変化し、下にスクロールするを想像してください...固執するべきかどうか?またはを持つ要素を考えると、どこ多分変化し、したがって、要素のサイズ(したがって、それは固執するべき瞬間を変える)...top0300px150pxposition: stickybottom: 0:stuckfont-size
ローマ

3
github.com/w3c/csswg-drafts/issues/1660を参照してください。何かがスタック/アンスタックしたことを知るためのJSイベントが提案されています。これには、疑似セレクターがもたらす問題があってはなりません。
ルーベン

27
私は同じ循環問題が多くの既存の疑似クラス(たとえば:hoverが幅を変更し、:not(:hover)が再び変更する)でも発生する可能性があると考えています。:stuck疑似クラスが大好きで、開発者は自分のコードに循環的な問題がないようにする責任があると思います。
MarekLisý2017

12
まあ...私はこれを間違いとしては本当に理解していません-それwhileは無限ループを可能にするのでサイクルがうまく設計されていないと言っているようなものです:)しかしこれをクリアしてくれてありがとう;)
MarekLisýSep

25

IntersectionObserver状況によっては、適切にフラッシュするのではなく、ルートコンテナーの外側の1つか2つのピクセルに固執することが可能であれば、単純な方法でうまくいく場合があります。そうすれば、それが端を越えたところに座ったときに、オブザーバーが発砲し、私たちは立ち止まって実行します。

const observer = new IntersectionObserver( 
  ([e]) => e.target.toggleAttribute('stuck', e.intersectionRatio < 1),
  {threshold: [1]}
);

observer.observe(document.querySelector('nav'));

要素をコンテナの外にで貼り付けtop: -2px、次にstuck属性を介してターゲットにします...

nav {
  background: magenta;
  height: 80px;
  position: sticky;
  top: -2px;
}
nav[stuck] {
  box-shadow: 0 0 16px black;
}

ここの例:https : //codepen.io/anon/pen/vqyQEK


1
stuckクラスはカスタム属性よりも優れていると思います...選択する特別な理由はありますか?
collimarco

クラスも正常に機能しますが、これは派生プロパティであるため、これはそれよりも少し高いレベルに見えるだけです。属性は私にはより適切に思えますが、どちらにしても好みの問題です。
ラックマウント可能

ヘッダーが既に固定されているため、上部を60pxにする必要があるため、例を機能させることができません
FooBar

1
スタックしているものにトップパディングを追加してみてください。多分padding-top: 60pxあなたのケースでは:)
Tim Willis

5

Google Developersブログの誰かが、IntersectionObserverを備えた高性能なJavaScriptベースのソリューションを見つけたと主張しています。

ここに関連するコードビット:

/**
 * Sets up an intersection observer to notify when elements with the class
 * `.sticky_sentinel--top` become visible/invisible at the top of the container.
 * @param {!Element} container
 */
function observeHeaders(container) {
  const observer = new IntersectionObserver((records, observer) => {
    for (const record of records) {
      const targetInfo = record.boundingClientRect;
      const stickyTarget = record.target.parentElement.querySelector('.sticky');
      const rootBoundsInfo = record.rootBounds;

      // Started sticking.
      if (targetInfo.bottom < rootBoundsInfo.top) {
        fireEvent(true, stickyTarget);
      }

      // Stopped sticking.
      if (targetInfo.bottom >= rootBoundsInfo.top &&
          targetInfo.bottom < rootBoundsInfo.bottom) {
       fireEvent(false, stickyTarget);
      }
    }
  }, {threshold: [0], root: container});

  // Add the top sentinels to each section and attach an observer.
  const sentinels = addSentinels(container, 'sticky_sentinel--top');
  sentinels.forEach(el => observer.observe(el));
}

私はそれを自分で再現していませんが、この質問につまずいた人を助けるかもしれません。


3

ものをスタイリング(つまり、getBoudingClientRect、スクロールリスニング、リサイズリスニング)するためにjsハックを使用することのファンではありませんが、これが現在私が問題を解決している方法です。このソリューションでは、最小化/最大化可能なコンテンツ(<details>)、ネストされたスクロール、または実際にはすべての曲線ボールを含むページで問題が発生します。そうは言っても、問題が単純な場合の簡単な解決策です。

let lowestKnownOffset: number = -1;
window.addEventListener("resize", () => lowestKnownOffset = -1);

const $Title = document.getElementById("Title");
let requestedFrame: number;
window.addEventListener("scroll", (event) => {
    if (requestedFrame) { return; }
    requestedFrame = requestAnimationFrame(() => {
        // if it's sticky to top, the offset will bottom out at its natural page offset
        if (lowestKnownOffset === -1) { lowestKnownOffset = $Title.offsetTop; }
        lowestKnownOffset = Math.min(lowestKnownOffset, $Title.offsetTop);
        // this condition assumes that $Title is the only sticky element and it sticks at top: 0px
        // if there are multiple elements, this can be updated to choose whichever one it furthest down on the page as the sticky one
        if (window.scrollY >= lowestKnownOffset) {
            $Title.classList.add("--stuck");
        } else {
            $Title.classList.remove("--stuck");
        }
        requestedFrame = undefined;
    });
})

スクロールイベントリスナーはメインスレッドで実行されるため、パフォーマンスが向上します。代わりにIntersection Observer APIを使用してください。
懐疑的なジュール

if (requestedFrame) { return; }アニメーションフレームのバッチ処理のため、「パフォーマンスキラー」ではありません。交差オブザーバーはまだ改善です。
Seph Reed

0

要素の上にposition:sticky要素がある場合のコンパクトな方法。stuckCSSで一致できる属性を設定しますheader[stuck]

HTML:

<img id="logo" ...>
<div>
  <header style="position: sticky">
    ...
  </header>
  ...
</div>

JS:

if (typeof IntersectionObserver !== 'function') {
  // sorry, IE https://caniuse.com/#feat=intersectionobserver
  return
}

new IntersectionObserver(
  function (entries, observer) {
    for (var _i = 0; _i < entries.length; _i++) {
      var stickyHeader = entries[_i].target.nextSibling
      stickyHeader.toggleAttribute('stuck', !entries[_i].isIntersecting)
    }
  },
  {}
).observe(document.getElementById('logo'))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.