要約:<table data="データソース.tsv"></table> と一行で書けたら楽だな~と思い、テンプレートを作ってみました。Chromium43、Firefox38で動作確認。OSWindow7 32bit。
関連記事 » 2016820日(Web上で動く簡単なサンプルとデモ)
Contents


図(画像)と同じように、表もHTML外部のオブジェクトにしたい

「図表」と括って表現されるように、図・表とも文章を補強する一まとまりのオブジェクトなのに、HTMLでは画像(図)とテーブル(表)の扱いが全然違って違和感があります。表の生データを、文字と同じレベルでHTML内に直接書くって変。図と同様にデータソースだけ指定し、表の体裁はCSSで調整、データ並びとか可変的なビューはJavaScriptで操作する。という方がHTML内の文書構造が分かりやすく、構造と外見が分離され、表データの一体性が保たれて良いのでは。

Web Componentsとか他のマークアップ言語とかでは出来るかもしれませんが(適当)、ブラウザだけで出来たら便利なのでJavaScriptCSSで作ってみました。CSSの疑似クラスはいろいろな表組パターンができ、同じ体裁でデータソースだけ差し替えるのも楽です。

データソースの形式はとりあえずタブ区切りテキスト(TSV)のみ。行・列区切り以外に改行・タブがある形式は今回想定してません。CSVは、区切り文字以外のカンマや引用符の処理が大変なので後回し。便利なライブラリが見つかったら考えます。

ゆくゆくはHTML内の文章も項目別の外部テキストにして、HTMLはそれら素材をまとめる「タグのみ」で文書を作れたらいいなぁ。
昨日の「画像+テキスト」テンプレートはその準備の一環。Web Componentsがまさにこういう方向かもしれませんが、今のHTML+JavaScriptでも可能な所までやりたい。


ファイル一式と説明

tsv2table.zipにまとめました(2.2KB)。下の5ファイルあり、とりあえず全部同じフォルダに置いてHTMLをブラウザで開く。Chromiumでは、ローカルファイル読み込みを許可する起動オプション--allow-file-access-from-filesが必要。CSSTSVは単なるサンプルで、後者のデータは、先月PostgreSQL 9.5の新しいGROUP BYを紹介した時のから抜粋。先頭行が列名のヘッダ。


HTMLはこんな感じで、外部CSSJavaScriptを読み込み、tableタグでクラス(CSS用)とデータソースのTSVを指定するだけ。
<!DOCTYPE HTML><html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="table_type1.css">
    <link rel="stylesheet" href="table_type2.css">
    <script src="tsv2table.js"></script>
</head>
<body>
    <table class="type1" data="example.tsv"></table>
    <table class="type2" data="example.tsv"></table>
</body>
</html>


↓ 根幹となるJavaScript。ページ読み込み後にtableタグを走査し、data属性でTSVが指定されてる前提で一つずつ処理。一時的にobjectタグを生成して読み込み、最低限必要なtr、th、tdタグのみ付けてHTMLテーブルを作ります。最後にあるのは、FirefoxアドオンAuto Reload用のおまけ(TSVが更新されたらHTMLを自動リロード。
71日に書いた裏技?と同じ)
window.addEventListener('load', function() {
    var tbls = document.getElementsByTagName('table'),
        len = tbls.length,
        obj = document.createElement('object');

    for (var i = 0; i < len; i++) {
        var t = tbls[i],
            o = obj.cloneNode();
        o.setAttribute('tid', i);
        o.onload = function(e) { loadTsv(e) };
        o.data = t.getAttribute('data');
        document.body.appendChild(o);
    }
});

function loadTsv(e) {
    var o = e.target,
        tsv = o.contentDocument.body.textContent,
        rows = tsv.split(/[\n\r]+/),
        nrow = rows.length,
        tid = o.getAttribute('tid'),
        tbl = document.getElementsByTagName('table')[tid],
        ary = [],
        s = document.createElement('script');
    
    ary.push('<tr><th>');
    ary.push(rows[0].split(/\t/).join('<th>'));

    for (var i = 1; i < nrow; i++) {
        if (rows[i].length === 0) break;
        ary.push('<tr><td>');
        ary.push(rows[i].split(/\t/).join('<td>'));
    }
    tbl.innerHTML = ary.join('');

    // remove OBJECT element
    o.parentNode.removeChild(o);  
    
    // add dummy script tag for Firefox Auto Reload
    s.src = o.data;
    document.body.appendChild(s);
}


サンプルCSSの二つは疑似クラスの:nth-childを活用
(参考:Mozilla Developer Network)。あちこち体裁を整えたので長くなりましたが、単に罫線を引くだけならもっと簡単。詳細は明日書きます。
table_type1.cssSelectRawtextBitbucket
table.type1 {
    border-collapse: collapse;
    border-bottom: 2px solid black;
    border-top: 2px solid black;
    font-size: 12px;
    margin:2em 1em;
}

table.type1 tr {
    border-width: 0; 
    border-style: solid;
}

table.type1 tr:nth-child(2) td {
    padding-top: 0.5em;
}

table.type1 tr:last-child td {
    padding-bottom: 0.4em;
}

table.type1 tr:first-child {
    border-bottom: 4px double black;
}

table.type1 th {
    padding: 0.5em;
}

table.type1 td {
    padding: 0.2em 0.5em 0.1em 0.5em;
    white-space: nowrap;
}

table.type1 td:nth-child(6n+2) {
    padding-right: 1em;
    text-align: right;
}

table.type1 td:nth-child(6n) {
    padding-right: 1em;
    text-align: right;
}
table_type2.cssSelectRawtextBitbucket
table.type2 {
    border-collapse: collapse;
    font-size: 12px;
    margin:2em 1em;
}

table.type2 th {
    background: gray;
    color: white;
    padding: 0.3em 0.5em;
}

table.type2 th:first-child  {
    border-left: 1px solid gray;
}

table.type2 th:last-child {
    border-right: 1px solid gray;
}

table.type2 td {
    border: 1px solid gray;
    height: 4em;
    padding: 0.2em 0.5em 0.1em;
}

table.type2 tr:nth-child(2n) {
    background: whitesmoke;
}

table.type2 td:nth-child(6n+2) {
    text-align: center;
}

table.type2 td:nth-child(7n-2) {
    max-width: 12em;
}

table.type2 td:nth-child(6n) {
    text-align: center;
}


↓ 結果。一つ目がFirefox
38(冒頭の再掲)、二つ目がChromium43。デフォルトフォントが違う点を除き、同じ体裁になりました。CSSだけで、異なるスタイルの表が簡単にできます。


使い道、課題

PostgreSQLRはデータを作るのは自在ですが、それを「人に見せる表」にするのが結構面倒でした。以前はコピペしてExcelInDesignに持っていったり、Rで罫線入りの表を作ってPDF出力したり…。もうそれはやめて、データからの作表は基本この「TSVHTML」に一本化したい。どんなプログラムやアプリケーションでもTSV出力さえすれば同じスタイルの表になり、フォント指定とか整えて作れば全てのTSVに使えます。PDFが必要ならブラウザからPDF出力すればいいし。

一つ問題は、長い表を複数ページに出力・印刷する場合、ExcelInDesignのように「各ページに列名ヘッダを入れる」のが出来ない。もともとページ概念のないHTMLでそこまで求めるかという話ですが、ページ当たりの行数を指定してJavaScriptで表分割するとか、暇ができたら考えます。