シナリオはほとんど常に同じで、スクロール可能なコンテナー内のデータ テーブルです。すべての行にはアクション メニューがあり、編集、複製、削除などのオプションを備えた小さなドロップダウンがあります。それを構築すると、単独で完全に動作するように見えますが、誰かがスクロール可能な div 内にそれを配置すると、物事はばらばらになります。私は、コンテナ、スタック、フレームワークという 3 つの異なるコードベースで、まさにこのバグを確認しました。これらはすべて異なります。ただし、バグはまったく同じです。 ドロップダウンはコンテナの端で切り取られます。または、論理的にその下にあるはずのコンテンツの後ろに表示されます。あるいは、ユーザーがスクロールするまでは正常に動作しますが、その後スクロールしてしまいます。 z-index: 9999 に到達します。役立つ場合もありますが、まったく役に立たない場合もあります。この矛盾は、より深い何かが起こっていることを示す最初の手がかりです。 この問題が繰り返し発生する理由は、3 つの別々のブラウザ システムが関係しており、ほとんどの開発者はそれぞれを単独で理解していますが、オーバーフロー、コンテキストのスタック、ブロックの包含など、3 つすべてが衝突したときに何が起こるかについて考えたことがないためです。

3 つすべてがどのように相互作用するかを理解すると、故障モードがランダムであるとは感じなくなります。実際、それらは予測可能になります。 実際にこの問題を引き起こす 3 つの要因 それぞれの項目を詳しく見てみましょう。 オーバーフロー問題 要素に overflow: hidden、overflow:scroll、または overflow:auto を設定すると、ブラウザーは、絶対に配置された子孫を含め、その境界を越えるすべてのものをクリップします。 .scroll-container { オーバーフロー: 自動; 高さ: 300ピクセル; /* これにより、ドロップダウンがクリップされます (ピリオド) */ }

.ドロップダウン { 位置: 絶対; /* 関係ありません -- .scroll-container によってクリップされたままです */ }

初めて遭遇したときは驚きました。私は、position:Absolute を使用すると、要素がコンテナーのクリッピングから逃れることができると考えていました。そうではありません。 実際には、これは、たとえその祖先がメニューを含むブロックでなくても、絶対配置されたメニューは、目に見えないオーバーフロー値を持つ祖先によって切り取られる可能性があることを意味します。クリッピングと位置決めは別のシステムです。両方を理解するまでは、それらは完全にランダムに見える方法で偶然衝突するだけです。

以下は、createPortal を使用した React の例です。

import { createPortal } から 'react-dom'; import { useState, useEffect, useRef } from 'react';

function Dropdown({anchorRef, isOpen, Children }) { const [位置, setPosition] = useState({ 上: 0, 左: 0 });

useEffect(() => { if (isOpen &&anchorRef.current) { const rect = anchorRef.current.getBoundingClientRect(); setPosition({ 上:rect.bottom + window.scrollY、 左:rect.left + window.scrollX、 }); } }、[isOpen、anchorRef]);

if (!isOpen) は null を返します。

return createPortal(

{子供たち}
、 文書.本文 ); }

そしてもちろん、アクセシビリティを無視することはできません。コンテンツの上に表示される固定要素は、引き続きキーボードから到達できる必要があります。フォーカス順序が自然に固定ドロップダウンに移動しない場合は、コードを使用して管理する必要があります。また、他のインタラクティブ コンテンツの上に配置されていて、無視することができないかどうかを確認することも重要です。それはキーボードのテストであなたを刺します。 CSS アンカーの位置: 私が考えるこの方向性 CSS アンカーの配置は、私が今最も関心のある方向です。最初に見たときは、どの程度のスペックが実際に使えるのかわかりませんでした。これにより、ドロップダウンとそのトリガーの間の関係を CSS で直接宣言でき、ブラウザーが座標を処理します。 .trigger { アンカー名: --my-trigger; }

.dropdown-menu { 位置: 絶対; 位置アンカー: --my-trigger; 上: アンカー(下); 左: アンカー(左); 位置トライフォールバック: フリップブロック、フリップインライン; }

Position-try-fallbacks プロパティにより、これを手動計算よりも使用する価値があります。ブラウザは諦める前に別の配置を試みるため、ビューポートの下部にあるドロップダウンは切り取られるのではなく、自動的に上に反転します。 ブラウザのサポートは Chromium ベースのブラウザで安定しており、Safari ではサポートが強化されています。 Firefox にはポリフィルが必要です。 @oddbird/css-anchor-positioning パッケージは、コア仕様をカバーします。予期していなかったフォールバックを必要とするレイアウトのエッジケースに遭遇したため、これを漸進的な拡張機能として扱うか、Firefox の JavaScript フォールバック。 つまり、有望ではありますが、まだ普遍的ではありません。ターゲットブラウザでテストします。 アクセシビリティに関する限り、CSS で視覚的な関係を宣言しても、アクセシビリティ ツリーには何も伝わりません。 aria-controls、aria-expanded、aria-haspopup — その部分はまだあなたの責任です。 要素を移動するだけで修正できる場合もあります ポータルにアクセスしたり、座標の計算を行ったりする前に、私は常に最初に 1 つの質問をします。このドロップダウンは実際にスクロール コンテナー内に存在する必要があるのでしょうか? そうでない場合は、マークアップを上位レベルのラッパーに移動すると、JavaScript や座標計算が不要になり、問題が完全に解消されます。 これは常に可能なわけではありません。ボタンとドロップダウンが同じコンポーネントにカプセル化されている場合、一方を他方を削除せずに移動することは、API 全体を再考することを意味します。しかし、それができれば、デバッグする必要はありません。問題は存在しないだけです。 最新の CSS ではまだ解決できないこと CSS はここで長い道のりを歩んできましたが、それでもがっかりさせる部分がまだあります。 立場: 修正および変換の問題はまだ存在します。これは意図的に仕様に含まれているため、CSS の回避策は存在しません。変換された要素でレイアウトをラップするアニメーション ライブラリを使用している場合は、ポータルまたはアンカーの配置が必要になります。 CSS アンカーの配置は有望ですが、新しいものです。前述したように、これを書いている時点では、Firefox にはまだポリフィルが必要です。予想していなかったフォールバックが必要となる、レイアウトのエッジケースに遭遇しました。現在、すべてのブラウザーで一貫した動作が必要な場合、依然として扱いにくい部分については JavaScript を使用することになります。 私が実際にワークフローを変更した追加機能は HTML Popover API で、現在はすべての最新ブラウザで利用可能です。ポップオーバー属性を持つ要素は、JavaScript の配置を必要とせずに、何よりもブラウザーの最上位レイヤーでレンダリングされます。

ツールチップ、開示ウィジェット、シンプルなオーバーレイなどには、エスケープ処理、クリック時に外部から閉じる、確実なアクセシビリティ セマンティクスが無料で提供されます。現時点で私が最初に到達したツールです。 とはいえ、ポジショニングが解決するわけではありません。重ね着を解決します。ポップオーバーをトリガーに合わせるには、アンカーの位置決めまたは JavaScript が必要です。 Popover API は階層化を処理します。アンカーの位置決めにより配置が処理されます。これらを組み合わせて使用​​すると、これまでライブラリで実行できていたことのほとんどがカバーされます。 あなたの状況に応じた意思決定ガイド これらすべての困難な道を経て、今私がその選択について実際にどのように考えているかを次に示します。

ポータルを使用します。トリガーがネストされたスクロール コンテナーの奥深くに存在する場合にこれを使用します。このパターンをテーブル アクション メニューに使用し、フォーカスの復元とアクセシビリティ チェックと組み合わせました。これは最も信頼性の高いオプションですが、追加の配線のために時間を確保してください。 固定位置を使用します。これは、バニラ JavaScript または軽量フレームワークを使用していて、祖先が変換やフィルターを適用していないことを確認できる場合に使用します。 1 つの制約が満たされていれば、セットアップもデバッグも簡単です。 ブラウザのサポートで許可されている場合は、CSS Anchor Positioning.Reach を使用してください。 Firefox のサポートが必要な場合は、@oddbird ポリフィルと組み合わせてください。これがプラットフォームの最終的な方向性であり、最終的には頼りになるアプローチとなるでしょう。 DOM を再構築します。アーキテクチャが許可しており、実行時の複雑さをゼロにしたい場合にこれを使用します。おそらく最も過小評価されている選択肢だと思います。 パターンを結合します。アンカーの配置を主なアプローチとして使用し、サポートされていないブラウザーの JavaScript フォールバックと組み合わせたい場合にこれを実行します。または、座標精度を高めるために getBoundingClientRect() と組み合わせた DOM 配置用のポータル。

結論 私はこのバグを 1 回限りの問題、つまりパッチを当てて次に進むべき問題として扱っていました。しかし、オーバーフロー クリッピング、コンテキストのスタック、ブロックの包含という、関係する 3 つのシステムすべてを理解できるまで十分に長く使用してからは、ランダムであるとは感じなくなりました。壊れたドロップダウンを見て、どの祖先が原因かをすぐに追跡できました。 DOM の読み方が変わったことが大きな収穫でした。 単一の正解はありません。私がたどり着いたのは、コードベースで何を制御できるかによって決まりました。祖先ツリーが予測不可能な場合はポータルです。クリーンでシンプルなときは位置を固定しました。何も妨げられないときに要素を動かしました。そしてアンカーの位置決め、できるところは。 最終的に何を選択するにせよ、アクセシビリティを最後のステップとして扱わないでください。私の経験では、まさにそのときがスキップされます。 ARIA の関係、フォーカス管理、キーボードの動作などは洗練されていません。それらは、物事を実際に機能させるための一部です。 私の GitHub リポジトリで完全なソース コードを確認してください。 さらに読む これらは、これに取り組んでいる間、私が繰り返し参照した参照です。

スタッキングコンテキスト (MDN) 「CSS アンカー ポジショニング ガイド」、フアン ディエゴ ロドリゲス 「ポップオーバー API の入門」、Godstime Aburu フローティング UI (floating-ui.com) CSS オーバーフロー (MDN)

You May Also Like

Enjoyed This Article?

Get weekly tips on growing your audience and monetizing your content — straight to your inbox.

No spam. Join 138,000+ creators. Unsubscribe anytime.

Create Your Free Bio Page

Join 138,000+ creators on Seemless.

Get Started Free