position:sticky 一部のモバイルブラウザーで動作するようになったため、ページでメニューバーをスクロールできますが、ユーザーがスクロールするたびにビューポートの上部に固定できます。
しかし、スティッキーメニューバーが現在「スティッキング」状態になっているときに、少しスタイルを変更したい場合はどうでしょうか。たとえば、ページでスクロールするたびにバーの角を丸くしたい場合がありますが、ビューポートの上部に貼り付けたらすぐに、丸い角の上部を取り除き、その下に小さな影を追加しますそれ。
::stuck
andが現在保持されているターゲット要素への疑似セレクター(例:position: sticky
)はありますか?または、ブラウザベンダーはパイプラインにこのようなものを持っていますか?そうでない場合、どこでリクエストすればよいですか?
NB。モバイルでは、ユーザーが指を離したときに通常scroll
イベントを1つしか取得しないため、JavaScriptのソリューションはこれには適していません。そのため、JSはスクロールのしきい値を超えた正確な瞬間を知ることができません。
現在、「スタック」している要素に対して提案されているセレクタはありません。 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ソリューションを手に入れることはできません。
状況によって、適切にフラッシュするのではなく、ルートコンテナの外側のピクセルまたは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;
}
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;
});
})
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));
}
私はそれを自分で複製しませんでしたが、誰かがこの質問につまずくのを助けるかもしれません。