タイトルにある「縦横固定 Header なテーブル」というのは、 一番上の行と一番左の列が見出しのセルになっていて、縦横にスクロールしたときに それぞれの見出しが固定で中身のセルだけがスクロールするようなもの
それを IE 対応しながら React で実装したときのメモです。
IE を無視していいなら、css で position: sticky;
を使って一瞬で実装可能
Table を 2 つ使って左カラムとそれ以外を分けた
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
左カラム用のテーブルを、データを表示してるテーブルの縦スクロールに合わせて onScroll を設定して位置を動かした
[Violation]
というメッセージを見つけたググったら以下の記事にたどり着いた
Window.requestAnimationFrame() - Web API | MDN
ブラウザにアニメーションを行いたいことを知らせ、指定した関数を呼び出して次の再描画の前にアニメーションを更新することを要求します。このメソッドは、再描画の前に呼び出されるコールバック 1 個を引数として取ります。
ということで、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 の構造として正しくない