ykokw.xyz blog logo

Blog Posts

No results for 'undefined'Powered by Algolia

タイトルにある「縦横固定 Header なテーブル」というのは、 一番上の行と一番左の列が見出しのセルになっていて、縦横にスクロールしたときに それぞれの見出しが固定で中身のセルだけがスクロールするようなもの

Image from Gyazo

それを IE 対応しながら React で実装したときのメモです。

結論から先に

position: sticky; を使う

粘着位置指定要素 stickily positioned element とは、 position の計算値が sticky である要素です。これは包含ブロックがフロールート (又はその中でスクロールするコンテナー) 内の指定されたしきい値 (例えば top に設定された auto 以外の値など) を達するまでは相対的な配置として扱われ、包含ブロックの反対の端が来るまでその位置に「粘着」するものとして扱われます。

引用)position - CSS: カスケーディングスタイルシート | MDN

これを th 要素に適用すればできます。

thead th {
  position: -webkit-sticky; /* for Safari */
  position: sticky;
  top: 0;
}

tbody th {
  position: -webkit-sticky; /* for Safari */
  position: sticky;
  left: 0;
}

引用)html - Table with fixed header and fixed column on pure css - Stack Overflow

Table を2つ使う

  • 諸事情で IE 対応が避けられなかったので、Table を2つ用意した
  • 左カラム用のテーブルを、データを表示してるテーブルの縦スクロールに合わせて onScroll を設定して位置を動かした

    • css の top 指定の代わりに、transform で translateY の指定をすることで多少なめらかになったがスクロール時のカクつきやズレが直せなかった

Image from Gyazo

requestAnimationFrame を使う

  • たまたま chrome の開発者ツールで [Violation] というメッセージを見つけた
  • ググったら以下の記事にたどり着いた

  • その中に requestAnimationFrame を使うように書かれてたので調べた
  • Window.requestAnimationFrame() - Web API | MDN

    • ブラウザにアニメーションを行いたいことを知らせ、指定した関数を呼び出して次の再描画の前にアニメーションを更新することを要求します。このメソッドは、再描画の前に呼び出されるコールバック 1 個を引数として取ります。

    • setTimeout / setInterval だとフレーム更新タイミングとずれてしまうのを回避できる
    • 非アクティブなタブになった場合にアニメーション処理の優先度が下がる(ブラウザによって制御される

ということで、lodash の debounce の代わりに requestAnimationFrame を使った

+let isScrolling = false;
+
 const TableBodyWithRef: React.FC<{ handleScroll: Function }> = ({
   handleScroll,
   children,
@@ -107,7 +113,19 @@ const TableBodyWithRef: React.FC<{ handleScroll: Function }> = ({
     tbodyRef && tbodyRef.current && handleScroll(tbodyRef.current.scrollTop);
   };
   return (
-    <tbody ref={tbodyRef} onScroll={debounce(onScroll, 10)} className="TableBodyWithRef">
+    <tbody
+      ref={tbodyRef}
+      onScroll={() => {
+        if (!isScrolling) {
+          window.requestAnimationFrame(() => {
+            onScroll();
+            isScrolling = false;
+          })
+        }
+        isScrolling = true;
+      }}
+      className="TableBodyWithRef"
+    >

これでカクつきとズレを軽減できた

課題

  • Table を2つ使っているので HTML の構造として正しくない

    • そのためアクセシビリティが落ちているはずなので対策したい

This content is built with Gatsby