HOME

複雑な表はクライアントに作らせむ(Javascript)

2016-4-14(Thu)

はじめに

エディタで html の表を作るとき colspan や rowspan がからんでくると、とたんにソースファイルが読みづらくなる。

そこで、<table></table> 内のコメントとして

<table>
<!--
.       →         日本         オーストラリア
爬虫類  →         ヘビ          →
哺乳類  有胎盤類   ネコ          →
↓       有袋類    (なし)     カンガルー
-->
</table>

こんなふうにテキストの表を書いておき、これをクライアント側で Javascript を使い TD 要素や TR 要素に展開し、

のように表示させることを考えた。

ダウンロード

commentable.js 第 2 版(for UTF-8)

ライセンスは GPL

使い方は、html の <head></head> 内とかに

<script type="text/javascript" src="お好きな場所の/commentable.js"></script>

みたいな感じに書いて commentable.js を読み込むようにするだけ。

※この commentable.js は UTF-8 でエンコーディングされているよ。他のコーディングシステムを使っている場合に問題になるのは、ファイル中の → と ↓ だ。この 2 文字をお使いのコーディングシステムに合わせて変換しよう。

変更履歴

2016-4-14 第 1 版:つくった

2016-4-15 第 2 版:既存の tr, td と混用できるようにした。たんに commentable.js を読み込むだけでいいようにした。

2016-4-17 第 2.7 版:生成される要素にいくつかの属性がセットされるようにした。

表の書式

まあ、「はじめに」に載せた例を見ると、何となく使えるんじゃないかと思う。「あれっ」と思ったら、以下の仕様を読まうです。

[1] 行は改行で、列は2つ以上の連続したスペースで区切る

入力されるデータは縦横にセルが並んだ表と解釈される。このとき、行は改行によって、列は2つ以上の連続したスペース(U+0020 いわゆる半角スペース)によって区切られていると仮定される。なお、一つだけのスペースは一般の文字として扱われる。

<table>
<!--
1 2    3
4      5
-->
</table>

タブは 2 つ連続したホワイトスペースとみなされる。したがって、連続した 2 つのタブは 4 つの連続したスペースと同等で、これは 1 個のデリミタとして働くだけだ。空白のセルを作りたい場合は、[8]参照。

行頭と行最後にあるスペースは無視される。

<table>
<!--
1     2
      3     4
-->
</table>

[2] 入力される表の列数は 1 行目に合わせられる

表のコラム数は、各 <!-- ... --> ごとに、1 行目を基準として定まる。

入力において 2 行目以後に含まれるセルの数が 1 行目に含まれるセルの数を越えるとき、1 行目に合わせてセルの数が切り詰められる。

<table>
<!--
1     2
3     4      5
-->
</table>

一方、2 行目以後に含まれるセルの数が 1 行目に含まれるセルの数に満たないとき、空のセルが詰め込まれる。

<table>
<!--
1     2      3
4     5
-->
</table>

[3] → でセルを右方向に拡張する

<table>
<!--
1      →
2      3
-->
</table>

ただし、入力データ中のセルの内容が → のみからなる場合に限る。

[4] ↓ でセルを下方向に拡張する

<table>
<!--
1      2
↓     3
-->
</table>

ただし、入力データ中のセルの内容が ↓ のみからなる場合に限る。

[5] → や ↓ はそれぞれ連続して使うことができる

<table>
<!--
1      →    →
2      3     4
↓     5     6
↓     7     8
-->
</table>

→の左側のセルは、表示するデータを持つセルか、→でなくてはならず、↓の上側のセルは、表示するデータを持つセルか、↓でなくてはならない。

つまり、→の左に位置するセルが↓であっったり、↓の上に位置するセルが→であってはならない。また、最左列では → は使えず、1 行目では↓は使えない。

[6] 同一のセルに → と ↓ を適用すると 2x2 以上のサイズを持つセルを作れる

<table>
<!--
1     2     3    4
5     6     →   →
7     ↓    +    +
-->
</table>

このとき、入力データ中で不要となるセルは、それぞれ + を 1 つだけ書いたセルで埋めておかなくてはならない。

[7] 入力データ中のセルの中身が # で始まる場合 TD でなく TH 要素となる

このとき、# 自身は削除される。

<table>
<!--
#1    #2
#3    4
-->
</table>

[8] 入力データ中のセルの頭にあるピリオド(.)は出力されない

したがって、入力データ中のセルがピリオドのみからなる場合は、空のセルが出力される。

<table>
<!--
.    1
2    3
-->
</table>

また、これを利用して、→ や ↓ や # やピリオド自身をエスケープすることができる

<table>
<!--
.#    .↓
.→    ..
-->
</table>

なお、# の解釈と # 自体の削除は、ピリオドの解釈より前に行われるので、#. は空の TH 要素を作る。これに対して .# では、そもそも # が解釈されないので、たんに # が入った TD 要素が作られる。

どこに入力データを書くか(詳細)

表データは、<table></table> の中のコメントとして書く。このとき、 コメントの 1 行目と最終行は無視される。つまり、

<table>
<!--コメント内最初の行であるこの部分は無視されて、
この部分だけ表データとして用いられる
最後の行であるこの部分も無視される。-->
</table>

したがって、1 行ないし 2 行からなるコメントは、その全体にわたって無視されるので、本来のコメント用途として用いることができる。

コメントとして書いた表データは、1 つかそれ以上の <tr></tr> としてその場に展開されるので、あらかじめ用意した tr、td 要素と混在させることができる。ただし、この場合は、<tbody></tbody> を設けたほうがいいかもしれない。たとえば、次の如し。

<table>
<caption>これはキャプション</caption>
<tbody>
<tr><td>手書きしたセル</td><td>手書きしたセル</td></tr>
<!--
自動生成されるセル   自動生成されるセル
-->
<tr><td>手書きしたセル</td><td>手書きしたセル</td></tr>
<!--
自動生成されるセル   自動生成されるセル
-->
</tbody>
</table>

自動生成された要素にセットされる属性

css セレクタで特定のセル(やセルの集合)を捕えやすいように、自動生成された TR, TD, TH 要素にはいくつかの属性がセットされる。

たとえば、2 つ目の入力ブロックにおける 3 行目のデータを格納した TR と、その中の 4 列目にあるデータ「2016年」を格納した TD または TH は、次のような属性名と属性値を持つ。

colspan, rowspan がある場合に役立つ属性

次のような表があったとする。

<table id="animals">
<!--
動物     ネコ     cat
↓       イヌ     dog
↓       サル     monkey
-->
</table>

ここで、ネコ、イヌ、サルと並んだ列のバックグラウンドをピンクにしようとしたいとする。スタイルシートで各 TR の 2 番目の子を選択すべく

TABLE[id="animals"] TD:nth-child(2) { background-color: pink }

のように書くと、

ということになり、これは期待したものと違う。これは、html ツリーの中で、2 つ目と 3 つ目の TR の最初の子は、それぞれ「イヌ」と「サル」になっているからである。いったん html に展開してしまった以上、css セレクタを使ってこの課題を成し遂げることはできない。

さいわい commentable.js は、自分が生成した TD, TH 要素に col とう名の属性をセットし、当該要素の中にあるデータがもともと入力ブロック内で何列目に書かれていたものなのかという情報を保持している。そこで、

<style type="text/css">
TABLE[id="animals"] TD[col="2"] { background-color: pink }
</style>

のようにスタイルシートに書けば、目的が達成できる。

同様に、TD, TH 要素が入力ブロック中の何行目にあったデータを載せているのかは、row 属性を調べればよい。

一つの表で入力が複数ある場合に役立つ属性

前に書いた通り、一つの <table></table> の中に、複数の入力ブロック(<!-- --> のこと)を置くことができ、あらかじめ書いておいた <tr></tr> や <td></td> と混ぜて使うことができる。

<table id="my_table2">
<!--
1  2  3
4  5  6
-->
<tr><td>7</td><td>8<td><td>9<td></tr>
<!--
10  11  12
13  14  15
-->
</table>
789

上の表で、7 8 9 と並んだ行は自動生成されたものではなく、はじめからコーディングされていたものである。

ここで、自動生成された TR, TD, TH の各要素は、block という属性に、その要素の中身が元々何番目の入力ブロックにあったデータなのかという情報を保持している。したがって、たとえば二番目の入力ブロックにあったデータを載せた部分だけバックグラウンドをピンク色にしたいようなときは

<style type="text/css">
TABLE[id="my_table2"] TD[block="2"]   { background-color: pink; }
</style>

とすると、

789

のようにして実現できる。

禁断の属性

次のような表を作成したとする。

名前に「家」がつく将軍だけ赤字にしたい。css セレクタでは、テキストノードの中身を使った要素の選び出しをすることができない。

しかし、commentable.js で作成した表では、スタイルシートに

TABLE[id="tokugawa"] TD[text*="家"] { color: red }

と書くだけで

のように実現できる。これは、TD 要素がその中のテキストとそっくり同じ内容を text という属性の値として保持しているからである。

数の右揃え

数の入ったセルを右揃えしたいとする。

<table id="align_test">
<!--
794年      平安京遷都    7(な)9(く)4(よ)ウグイス平安京
1853年     ペリー来航    1(い)8(や)で5(ご)3(ざ)るよペリーさん
1192296年  鎌倉幕府開幕  1(い)1(い)9(く)2(に)2(つ)9(く)6(ろう)鎌倉幕府
-->
</table>

というコードは、ふつう次のような表を作り出す(一列目も左寄せ)。

0〜9 で始まるセルは、先頭の数を除いた部分が num 属性の値としてセットされる。したがって、「数」 + 「年」でできているセルを右寄せしたいならば、

<style type="text/css">
TABLE#align_test TD[num="年"] { text-align: right }
</style>

とやればよい(一列目だけ右寄せになった)。

一般的な装飾

HTML を書くのが楽になっても、それなりの見栄えのある表にするのは、ちょっと手間だ。そこで、<head> で一回読み込めば、<table class="ここに クラス名を いくつか 書く"> だけで、何種類かの整った表が手早く作れる仕組み作ってみた。くわしくは「CSS で手早く作表」を参照。

なお、このページの表は、2 以上の colspan を持つ行が中央揃えになっているが、これはこの commentable.css を使った結果、自動的にセンタリングさたものだ。

おあそび

まったくのお遊びである。

以下、セルの高さを確保するために和文のホワイトスペースを使っている。ソースを読みにくくするのでこんなことはお勧めできないが、CSS を書かずにセルの高さを確保したかったんだ。(色を塗ったのは CSS の仕業)

<table>
<!--
   →  →  →  →   
      →  →     ↓
↓        →  ↓  ↓
↓  ↓  ↓     ↓  ↓
↓  ↓     →  →  ↓
↓     →  →  →  → 
-->
</table>

<table>
<!--
      →     →     →     →   
   →     →     →     →     →   
      →     →     →     →   
   →     →     →     →     →   
      →     →     →     →   
-->
</table>

<table>
<!--
      →        
   →     ↓     
      ↓     →  
   ↓     →     
↓     →     ↓  
-->
</table>

おわり

Follow @kabipanotoko