web-dev-qa-db-ja.com

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

position:sticky 一部のモバイルブラウザーで動作するようになったため、ページでメニューバーをスクロールできますが、ユーザーがスクロールするたびにビューポートの上部に固定できます。

しかし、スティッキーメニューバーが現在「スティッキング」状態になっているときに、少しスタイルを変更したい場合はどうでしょうか。たとえば、ページでスクロールするたびにバーの角を丸くしたい場合がありますが、ビューポートの上部に貼り付けたらすぐに、丸い角の上部を取り除き、その下に小さな影を追加しますそれ。

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

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

72
callum

現在、「スタック」している要素に対して提案されているセレクタはありません。 Postioned Layout module where position: stickyが定義されている場合、そのようなセレクターも言及されていません。

CSSの機能リクエストは www-styleメーリングリスト に投稿できます。 :stuck擬似クラスは、::stuck擬似要素よりも意味があると思います。なぜなら、その状態にある要素自体をターゲットにしたいからです。実際、:stuck疑似クラスが議論されました しばらく前 ;主な複雑さは、レンダリングまたは計算されたスタイルに基づいて一致しようとする提案されたセレクター、つまり循環依存関係を悩ませるものであることがわかりました。

:stuck疑似クラスの場合、循環の最も単純なケースは次のCSSで発生します。

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

また、対処が困難なEdgeケースがさらに多くある可能性があります。

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

73
BoltClock

状況によって、適切にフラッシュするのではなく、ルートコンテナの外側のピクセルまたは2つにくっつくことが許される場合、単純なIntersectionObserverがトリックを行うことができます。そのようにして、それがEdgeのすぐ上にあるとき、オブザーバーが起動し、オフになって実行されます。

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

3
rackable

Jsハックを使用してスタイリング(getBoudingClientRect、スクロールリスニング、リサイズリスニング)を行うことはあまり好きではありませんが、これが現在私が問題を解決している方法です。このソリューションには、最小化/最大化可能なコンテンツ(<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;
    });
})
2
Seph Reed

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));
}

私はそれを自分で複製しませんでしたが、誰かがこの質問につまずくのを助けるかもしれません。

1
neo post modern