htmlテーブルをレスポンシブに
目次
画面幅に合わせて配置が変わるテーブル
どうも、高島です。
表を、画面サイズに合わせて狭めたり広げてみせたり、ということを試してみました。 画面幅に合わせて、3列、2列、1列と変化します。
これ、IEでは正常に動きませんでした。 Chromeでは動きました。あしからず。
ソースはこんな感じです。
<!doctype html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>ファイルアップロード</title> <style type="text/css"> table{ max-width: 1139px; width:100%; } table,th,td{ border-collapse: collapse; border:1px solid #333; vertical-align: baseline; margin:0; padding:0; } table tr{ display: inline; float: left; } table th{ background-color: #CCCCCC; } .department{ width:80px; } .num{ width:80px; } .name{ width:80px; } .position{ width:120px; } .detail.department{ background-color: #CCCCCC; } </style> <script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script> <script> $(document).ready(function(){ // 初期表示処理 tableResize(); //リサイズされたときの処理 $(window).resize(function() { // ダミー行を消し、隠し列を表示に戻す $('#datatable tr[data-department="dummy"]').remove(); $('#datatable td.department').css('display','table-cell'); tableResize(); }); // リサイズ時イベント function tableResize(){ // テーブル幅をtr幅で割る switch (Math.floor($("#datatable").width() / 370)) { case 0: case 1: console.log('1行に1人表示'); break; case 2: console.log('1行に2人表示'); addDummyTr(2); break; case 3: default: console.log('1行に3人表示'); addDummyTr(3); break; } } function addDummyTr(humanCnt){ // ヘッダ部調整用の仮行 var DUMMY_DETAILHEADER_TMPL ='<tr data-department="dummy"><th class="header department">所属</th><th class="header num">社員番号</th><th class="header name">氏名</th><th class="header position">役職</th></tr>'; var dummy_detailheader = ""; // ヘッダに調整用の行を追加する。 for (var i = 1; i < humanCnt; i++) { dummy_detailheader = dummy_detailheader + DUMMY_DETAILHEADER_TMPL; } $('#datatable thead tr:last').after(dummy_detailheader); // ヘッダ2番目以降の所属列を消す $('#datatable thead th.department:gt(0)').css('display','none'); // 明細部調整 // まず、所属を全て取得し一意にする。 var departmentList = []; $('#datatable tbody tr').each(function(index, element){ departmentList.push($(element).data('department')); }); departmentList = departmentList.filter(function (x, i, self) { return self.indexOf(x) === i; }); console.log('departmentList:' + departmentList); var DUMMY_DETAILROW_TMPL ='<tr data-department="dummy"><td class="department"></td><td class="num" style="color: transparent;">dummy</td><td class="name"></td><td class="position"></td></tr>'; // 例として、1行に2人の場合は、所属1人だとスキマができるため、スキマ埋めのためのdummy行を追加する。 for(let v of departmentList) { // スキマ埋めすべきdummy行数を求める。 let addDummyRowCnt = 0; let departmentLength = $('#datatable tbody tr[data-department="' + v + '"]').length if(departmentLength <= humanCnt) { // 例:所属2人で、1行に3人表示の場合は、1人分のスキマ埋めが必要。 addDummyRowCnt = humanCnt-departmentLength } else { // 例:所属4人で、1行に3人表示の場合は、2行になるため、この所属の必要な領域は3×2=6となる。2人分のスキマ埋めが必要。 addDummyRowCnt = (Math.ceil(departmentLength / humanCnt) * humanCnt) % departmentLength; } // スキマ分、ダミー行を追加する。 for(let i = 0; i < addDummyRowCnt; i++) { $('#datatable tbody tr[data-department="' + v + '"]:last').after(DUMMY_DETAILROW_TMPL); } } // 1行に2人以上の場合、表示上の左から1番目の所属列は残し、後の所属列は消す。 if(humanCnt > 1){ // 1行あたりの表示人数の倍数ではない行(見た目上、右側に移った行)は所属列を消す $('#datatable tbody tr').filter(function(index) { return index % humanCnt >= 1; }).find('td.department').css('display','none'); } } }); </script> </head> <body> <form action="" method="post"> <table id="datatable"> <thead> <tr> <th class="header department">所属</th> <th class="header num">社員番号</th> <th class="header name">氏名</th> <th class="header position">役職</th> </tr> </thead> <tbody> <tr data-department="tokyo"> <td class="detail department">東京</td> <td class="detail num">3</td> <td class="detail name">田中</td> <td class="detail position">課長</td> </tr> <tr data-department="tokyo"> <td class="detail department">東京</td> <td class="detail num">11</td> <td class="detail name">鈴木</td> <td class="detail position">主任</td> </tr> <tr data-department="tokyo"> <td class="detail department">東京</td> <td class="detail num">16</td> <td class="detail name">内海</td> <td class="detail position">パート</td> </tr> <tr data-department="tokyo"> <td class="detail department">東京</td> <td class="detail num">5</td> <td class="detail name">田口</td> <td class="detail position">パート</td> </tr> <tr data-department="nagoya"> <td class="detail department">名古屋</td> <td class="detail num">14</td> <td class="detail name">富田</td> <td class="detail position">主任</td> </tr> <tr data-department="osaka"> <td class="detail department">大阪</td> <td class="detail num">7</td> <td class="detail name">松永</td> <td class="detail position">主任</td> </tr> <tr data-department="osaka"> <td class="detail department">大阪</td> <td class="detail num">14</td> <td class="detail name">井ノ瀬</td> <td class="detail position">パート</td> </tr> <tr data-department="fukuoka"> <td class="detail department">福岡</td> <td class="detail num">21</td> <td class="detail name">牧田</td> <td class="detail position">主任</td> </tr> <tr data-department="fukuoka"> <td class="detail department">福岡</td> <td class="detail num">6</td> <td class="detail name">水島</td> <td class="detail position">リーダー</td> </tr> <tr data-department="fukuoka"> <td class="detail department">福岡</td> <td class="detail num">16</td> <td class="detail name">堀川</td> <td class="detail position">パート</td> </tr> <tr data-department="kagosshima"> <td class="detail department">鹿児島</td> <td class="detail num">27</td> <td class="detail name">大迫</td> <td class="detail position">主任</td> </tr> </tbody> </table> </form> </body> </html>
考え方
- テーブルのtrタグをinlineにし、行を横並びに出来るようにする。
- 所属ごとに人数が違うので、javascriptに動的に空白行を追加したりして、ちょうどよい見た目にする。
やり方
ポイントは、cssでtrタグをdisplay: inline;にしていること。
table tr{ display: inline; float: left; }
これでtrが横に並びます。 画面横幅に合わせて勝手に折り返してくれます。
とはいえ、所属によって人数が異なるので、名古屋所属の人が東京の行に表示されたりしておかしくなります。 そこで空白行を埋めることで、調整します。
テーブルの幅を検知して、何列表示になるのか判断します。
// リサイズ時イベント function tableResize(){ // テーブル幅をtr幅で割る switch (Math.floor($("#datatable").width() / 370)) { case 0: case 1: console.log('1行に1人表示'); break; case 2: console.log('1行に2人表示'); addDummyTr(2); break; case 3: default: console.log('1行に3人表示'); addDummyTr(3); break; } }
trにはデータ属性departmentを定義しており、どこの所属かわかるようにしています。 javascriptのaddDummyTr関数内で、所属ごとの人数から、必要な空行を追加します。
横幅に合わせて、ちょうどよくなるように、空白行が追加されました。
次に、ヘッダ行も表示します。 ヘッダのtrタグの後ろに、ヘッダをさらに追加してしまいます。 1行に2人表示する画面幅なら、ヘッダを1行追加し、 3人表示するなら、ヘッダを2行追加します。
// ヘッダ部調整用の仮行 var DUMMY_DETAILHEADER_TMPL ='<tr data-department="dummy"><th class="header department">所属</th><th class="header num">社員番号</th><th class="header name">氏名</th><th class="header position">役職</th></tr>'; var dummy_detailheader = ""; // ヘッダに調整用の行を追加する。 for (var i = 1; i < humanCnt; i++) { dummy_detailheader = dummy_detailheader + DUMMY_DETAILHEADER_TMPL; } $('#datatable thead tr:last').after(dummy_detailheader);
これでヘッダも付きました。
所属列は要らないので消します。
// ヘッダ2番目以降の所属列を消す $('#datatable thead th.department:gt(0)').css('display','none');
departmentはヘッダの所属列についたクラスです。 gt(0)で、2番目以降が選択対象になります。 それをdisplay:noneにして見えなくします。
これで完成です。
あとがき
同じことを実現するにも、様々なやり方があるでしょうし、外部のモジュールを取り入れたら、あっさり実現できたと思います。
今回の書き方が、きれいな正当なやり方だとは思っていませんが、お勉強兼ねてやってみました。