Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8807

[CSS/jsのみ]tableの行/列ヘッダーを固定する

$
0
0

はじめに

最近Webアプリ周辺の技術を学び始めた者です。普段は製造業で設計業務を担当しておりますが、社内システム構築用にいろいろと勉強しています。主に使うのは下記。

  • 言語 : python/C#
  • Web framework : django

記事に書き出すことで自身の理解も深まると考え、今回初投稿をさせて頂きます。

動機

休日に妻と共同でアプリ開発をしています(この開発記録もつけられたらなーと思っています)。
tableを多用するのですが、その際列ヘッダーと行ヘッダーを固定してtbodyのデータセルだけスクロールできないかなと考え、いろいろ調べていました。
便利なプラグインもたくさんありましたが、

  • 複数ヘッダーを固定できるものが限られていた
  • なるべく既存のtableに変更を加えたくない

という理由で手を出せず。position:stickyというステキなオプションがあるので、どうにかこれを使ってできないかと思い、やってみました。
なおこちらのstackoverflowの質問を参考にしました→Table with fixed header and fixed column on pure css

html

下記のようなtableと、wrapperとなるdivを用意します。class名はbootstrapを意識しています。

sample.html
<divclass="table-wrapper"><tableclass="table text-nowrap sticky-table table-borderless"><theadclass="thead-light"><trclass="fixed-header-0"><thclass="fixed-column-0">日付</th><th>1/1</th><th>1/2</th><th>1/3</th><th>1/4</th><th>1/5</th><th>1/6</th><th>1/7</th></tr><trclass="fixed-header-1"><thclass="fixed-column-0">曜日</th><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><thclass="fixed-column-0 table-light">AM</th><td></td><td></td><td></td><td>×</td><td>×</td><td></td><td></td></tr><tr><thclass="fixed-column-0 table-light">PM</th><td>×</td><td></td><td></td><td>×</td><td></td><td>×</td><td></td></tr><tr><thclass="fixed-column-0 table-light"></th><td></td><td></td><td></td><td></td><td></td><td></td><td>×</td></tr></tbody></table></div>

image.png

ゴールは上記tableの「日付」「曜日」行を固定しつつ、「AM」「PM」「夜」も常に表示することです。

wrapperにはclass=table-wrapper、tableにはclass=sticky-tableを指定します。また、固定したい行ヘッダーとなる各trには上から順にclass=fixed-header-n(nは0始まりの番号)を指定し、固定したい列ヘッダーとなる各thには左から順にclass=fixed-column-m(mは0始まりの番号)を指定します。
tableの左上の場所は行列方向に拘束したいので、tr.fixed-header-nおよびth.fixed-column-mの両方の指定が必要です。

ちなみにヘッダーにtable-lightやthead-lightで色を付けているのは、透明のままだとヘッダー固定したときに他のセルと重なってしまうからです。

css/js

下記のようなcssとjsを作成します。

sticky-table.css
/*ラッパー*/div.table-wrapper{overflow:scroll;max-height:200px;/*任意*/max-width:400px;/*任意*/}/*行ヘッダーを固定する。topの値はjsで動的に指定*/table.sticky-tabletheadtr[class*="fixed-header-"]th{position:-webkit-sticky;/* for Safari */position:sticky;/* tbody tdより手前に表示する */z-index:1;}/*行ヘッダーと列ヘッダーが重なる部分を固定する。top,leftの値はjsで動的に指定*/table.sticky-tabletheadtr[class*="fixed-header-"]th[class*="fixed-column-"]{/* 全てのセルより手前に表示する */z-index:2;}/*列ヘッダーを固定する。leftの値はjsで動的に指定*/table.sticky-tabletbodyth[class*="fixed-column-"]{position:-webkit-sticky;/* for Safari */position:sticky;/* tbody tdより手前に表示する */z-index:1;}

コメントを添えていますが、

div.table-wrapper{overflow:scroll;max-height:200px;/*任意*/max-width:400px;/*任意*/}

はwrapperの挙動です。max-height/max-widthは任意の値に設定してください。

続いて下記のような.jsを作成します。結局jqueryで書いてしまった。

sticky-table.js
//行ヘッダーに対しtopを設定height=0;for(vari=0;i<fixed_header_num;i++){$(".fixed-header-"+i+" th").css('top',height);height+=$(".fixed-header-"+i+" th").outerHeight();}//列ヘッダーに対しleftを設定width=0;for(varj=0;j<fixed_column_num;j++){$("th.fixed-column-"+j).css('left',width);width+=$("th.fixed-column-"+j).outerWidth(true);}

jsでは固定したい各ヘッダーに対し、「どこまでの位置に達したら上/左方向への移動を拘束するか」の値となるtop/leftの値を動的に設定しています。一番上のヘッダーはtop=0でよいのですが、二番目以降のヘッダーは自身の上にあるヘッダーの累積高さ分の値を設定しています。列ヘッダーも同様。

fixed_header_num, fixed_column_numはそれぞれ、固定したい行/列ヘッダーの数なのですが、これらは使用シーンに合わせて変わると思うので、グローバルで宣言することにします。

使用例

下記のツリー構造を仮定します。

sticky-table/
 ├ sample.html
 └ static/
   ├ css/
   │ └ sticky-table.css
   └ js/
      └ sticky-table.js
sample.html
<html><head><metaname="viewport"content="width=device-width,initial-scale=1"><metacharset="utf-8"/><title>ヘッダー固定</title><!--bootstrap--><linkrel="stylesheet"href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"crossorigin="anonymous"><!--sticky-table--><linkrel="stylesheet"href="static/css/sticky-table.css"></head><body><!--https://stackoverflow.com/questions/15811653/table-with-fixed-header-and-fixed-column-on-pure-css--><divclass="container"><divclass="table-wrapper"><tableclass="table text-nowrap sticky-table table-borderless"><theadclass="thead-light"><trclass="fixed-header-0"><thclass="fixed-column-0">日付</th><th>1/1</th><th>1/2</th><th>1/3</th><th>1/4</th><th>1/5</th><th>1/6</th><th>1/7</th></tr><trclass="fixed-header-1"><thclass="fixed-column-0">曜日</th><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><thclass="fixed-column-0 table-light">AM</th><td></td><td></td><td></td><td>×</td><td>×</td><td></td><td></td></tr><tr><thclass="fixed-column-0 table-light">PM</th><td>×</td><td></td><td></td><td>×</td><td></td><td>×</td><td></td></tr><tr><thclass="fixed-column-0 table-light"></th><td></td><td></td><td></td><td></td><td></td><td></td><td>×</td></tr></tbody></table></div></div><!--jquery+bootstrap--><script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"crossorigin="anonymous"></script><!--sticky-table--><script type="text/javascript">//固定するヘッダーの数varfixed_header_num=2;//固定するカラムの数varfixed_column_num=1;</script><script src="static/js/sticky-table.js"></script></body></html>

image.png

これで任意の数の行/列ヘッダーをcss/jsのみで固定することができます。

課題

Chromeでは動作確認しましたが、IEだとpolyfillが必要みたいです。
stickyfill

また、globalで変数指定が必要だったり、洗練されてないイメージもあるので、もっとうまいやり方がありましたらご教示頂ければ幸いです。

参考

Table with fixed header and fixed column on pure css
stickyfill


Viewing all articles
Browse latest Browse all 8807

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>