0Pricing
React Academy · レッスン

Portalsでツールチップとドロップダウンメニューを作る

Portalsを使って、overflow-hiddenコンテナの外側にツールチップやドロップダウンをレンダリングします

「Portalsでツールチップとドロップダウンメニューを作る」はCoddyKit上の無料React Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはReact Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 React Academyコースには全4レッスンが含まれています。

ようこそ

このレッスンでは、React Portalを使ってdocument.bodyにレンダリングするツールチップとドロップダウンのコンポーネントを構築し、親要素のoverflow-hiddenとz-indexのスタッキングコンテキストによる制約を回避します。

オーバーフローの問題

トリガーボタンを基準に配置したツールチップは、祖先要素のいずれかに`overflow: hidden`または`overflow: scroll`があると切り取られます。Portalを使ってツールチップをdocument.bodyへ移動すれば、この問題を完全に解決できます。

配置戦略

Portalでレンダリングするツールチップを正しく配置するには、`getBoundingClientRect()`でトリガー要素の境界矩形を測定し、スクロール位置を考慮した絶対座標を計算します。
const rect = triggerRef.current.getBoundingClientRect();
const top = rect.bottom + window.scrollY + 8;
const left = rect.left + window.scrollX;

Tooltipコンポーネント

childrenをラップし、トリガーにrefを設定し、ホバーされたときに位置を計算したツールチップボックスをPortalするTooltipを構築してください。
function Tooltip({ content, children }) {
  const [visible, setVisible] = useState(false);
  const [pos, setPos] = useState({ top: 0, left: 0 });
  const ref = useRef();

  const show = () => {
    const r = ref.current.getBoundingClientRect();
    setPos({ top: r.bottom + window.scrollY + 4,
             left: r.left + window.scrollX });
    setVisible(true);
  };

  return (
    <span ref={ref} onMouseEnter={show} onMouseLeave={() => setVisible(false)}>
      {children}
      {visible && createPortal(
        <div style={{ position:'absolute', top: pos.top, left: pos.left }}
             className="tooltip">{content}</div>,
        document.body
      )}
    </span>
  );
}

スクロールとリサイズ時に位置を更新する

ツールチップの表示中にユーザーがスクロールすると、その位置は古くなります。`scroll`と`resize`イベントを監視して位置を再計算するか、ツールチップを閉じてください。
useEffect(() => {
  if (!visible) return;
  const update = () => setVisible(false);
  window.addEventListener('scroll', update, true);
  window.addEventListener('resize', update);
  return () => {
    window.removeEventListener('scroll', update, true);
    window.removeEventListener('resize', update);
  };
}, [visible]);

ドロップダウンメニューの構成

ドロップダウンメニューはツールチップと同じパターンに従います。トリガーボタンでメニューの表示と非表示を切り替え、メニューはPortal経由でレンダリングし、外側のクリックを検知するハンドラーで閉じます。

外側のクリックを検知するハンドラー

`document`に`mousedown`リスナーを設定して、メニューの外側でのクリックを検知します。クリックの対象をメニューとトリガーのrefと比較し、閉じるかどうかを判断してください。
useEffect(() => {
  if (!open) return;
  const handler = (e) => {
    if (!menuRef.current?.contains(e.target) &&
        !triggerRef.current?.contains(e.target)) {
      setOpen(false);
    }
  };
  document.addEventListener('mousedown', handler);
  return () => document.removeEventListener('mousedown', handler);
}, [open]);

ドロップダウンのキーボード操作

ArrowDown/ArrowUpでメニュー項目間を移動し、Enterで選択し、Escapeで閉じられるように実装してください。stateでフォーカス中のインデックスを追跡し、メニュー項目に対してプログラムから`.focus()`を呼び出します。

配置ライブラリを使う

本番環境では**@floating-ui/react**(旧称Popper.js)を使用してください。ビューポートの端に近いときの反転配置(flip)、ツールチップを表示領域内に保つ移動(shift)、矢印の配置を自動的に処理します。
import { useFloating, flip, shift } from '@floating-ui/react';

const { refs, floatingStyles } = useFloating({
  middleware: [flip(), shift()],
});

アクセシビリティ: ツールチップのARIA

ツールチップ要素に`role='tooltip'`を追加し、トリガーの`aria-describedby`でツールチップのidを指定してください。これにより、スクリーンリーダーがツールチップの内容を読み上げられます。
<button aria-describedby="tip1">Hover me</button>
{visible && createPortal(
  <div id="tip1" role="tooltip">Helpful text</div>,
  document.body
)}

確認問題

Portalでレンダリングされたツールチップの正しい画面上の位置は、どのように計算しますか?

復習

ポータルを使うと、ツールチップやドロップダウンを overflow-hidden の親要素から切り離して表示できます。getBoundingClientRect でトリガー要素の位置とサイズを測定し、絶対位置指定にはスクロール量を加え、外側のクリックやスクロールで閉じ、アクセシビリティのために ARIA 属性を追加します。

コース完了

おめでとうございます!**Reactのポータルとモーダル**を完了しました。これで、CSSのスタッキング制約に左右されず、アクセシブルで正しく配置されたモーダル、ツールチップ、ドロップダウンを構築できるようになりました。

よくある質問

「Portalsでツールチップとドロップダウンメニューを作る」レッスンは無料ですか?

はい。「Portalsでツールチップとドロップダウンメニューを作る」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、React Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 React Academyコースには全4レッスンが含まれています。

「Portalsでツールチップとドロップダウンメニューを作る」で何を学びますか?

Portalsを使って、overflow-hiddenコンテナの外側にツールチップやドロップダウンをレンダリングします ブラウザで直接実行するハンズオンコードでReact Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

React Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのReact Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「Portalsでツールチップとドロップダウンメニューを作る」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このReact Academyレッスンでコードを書いて実行できますか?

はい。すべてのReact Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. React Portalsとは?
  2. Portalsでモーダルコンポーネントを構築する
  3. モーダルのフォーカスとキーボードトラップを扱う
  4. Portalsでツールチップとドロップダウンメニューを作る
← React Academyに戻る