JavaScript ライブラリなしでTreeTableを実装する

## 始めに

開発現場によってはjQueryやAngular等のフレームワーク、ライブラリの使用が禁止されているところがあると聞いたことがあります。また、使用するフレームワークによって実装方法がバラついてしまうことがよくあると思います。
そういった状況下でも、問題なく開発が進められるように、フレームワーク、ライブラリを一切使用せずTreeTableを自作してみました。
作成したTreeTableは[gitHub](https://github.com/yunomichawan/TreeTableView)に公開しております。


## 動作ブラウザ

・Google Chrome
・Microsoft Edge
・IE11

この3つ以外のブラウザでは未検証です。注意ください。

## ソースの準備

ソースは[gitHub](https://github.com/yunomichawan/TreeTableView)に公開しております。
構成としては至ってシンプルで下記の3つのみです。
動作確認は上記で記載したブラウザがあれば確認することができます。

・サンプル用のhtml
・jsファイル
・cssファイル


## TreeTable使用方法

### サンプルで確認

サンプルは2つTreeTableを用意しました。
1つは一番シンプルな作成方法、もう1つはカスタマイズした作成方法になります。
ここではシンプルに作成した場合のサンプルについて確認します。

```html
<script type="text/Javascript">
    // test data1
    const sampleDatas1 = [
        { Prefectures: 'tokyo', Municipality: null, town: null, name: '東京都', value: 100 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: null, name: '千代田区', value: 200 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 1, name: 'A町', value: 300 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 2, name: 'B町', value: 400 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 3, name: 'C町', value: 500 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 4, name: 'D町', value: 600 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 5, name: 'E町', value: 700 },
        { Prefectures: 'tokyo', Municipality: 'chuou', town: null, name: '中央区', value: 800 },
        { Prefectures: 'tokyo', Municipality: 'chuou', town: 1, name: 'F町', value: 1000 },
        { Prefectures: 'tokyo', Municipality: 'chuou', town: 2, name: 'G町', value: 1100 },
        { Prefectures: 'tokyo', Municipality: 'minato', town: null, name: '港区', value: 1200 },
        { Prefectures: 'tokyo', Municipality: 'minato', town: 1, name: 'H町', value: 1300 },
        { Prefectures: 'tokyo', Municipality: 'minato', town: 2, name: 'I町', value: 1400 },
    ];
    
    window.onload = function () {
    	// 設定1
        var settingJson1 = {
            // 作成する列(左から順に作成)
            columns: ['name', 'value'],
            // 階層識別列(親要素 - 子要素 - 孫要素)
            levelColumns:['Prefectures', 'Municipality', 'town']
        };
        
        // インスタンス生成
        var treeTable1 = new treeTable(this.document.getElementById('sample-tree-table1'), settingJson1);
        
        // TreeTable作成
        treeTable1.create(sampleDatas1);
	}
</script>

<div>
    <table id="sample-tree-table1">
        <thead>
            <tr>
                <td>名称</td>
                <td>値</td>
            </tr>
        </thead>
    </table>
</div>

```

上記はシンプルに作成するパターンです。作成する際はシンプルですが、データがやや複雑に見えます。
サンプルで作成したデータは3階層のデータ構造となっています。
「東京都→区→町」
表にすると下図みたいな感じです。
またデータを渡す際は、表のようにソートされているデータにする必要があります。

|都道府県|区|町|
|:-------|:--|:--|
|東京都|null|null|
|東京都|千代田区|null|
|東京都|千代田区|A町|
|東京都|千代田区|B町|
|東京都|千代田区|C町|
|東京都|千代田区|D町|
|東京都|千代田区|E町|
|東京都|中央区|null|
|東京都|中央区|F町|
|東京都|中央区|G町|
|東京都|港区|null|
|東京都|港区|H町|
|東京都|港区|I町|


### 設定値

|必須|項目|設定値|初期値|説明|
|:---|:----|:-----|:-----|:---|
||isOpen|Boolean|true|表示時、展開するかどうか。|
||isTreeStatusKeep|Boolean|false|画面遷移しても展開状態を維持するか。(※1)|
||headerLabels|配列|なし|設定した場合、ヘッダ要素を作成。|
|必須|columns|配列|なし|表示する列要素。表示順は要素の順番。
|必須|levelColumns|配列|なし|階層を識別するための列要素。|
||levelPadding|数値|20|階層毎の間隔。インデント。|
||onDrawCellValue|function(value,cell,rowData){}|なし|セルに値を書き込む際のコールバックを設定。(※2)|
||onDrawRow|function (row,rowData){}|なし|行を描画する際のコールバックを設定。|
||iconStyle|Json|下記で説明|開閉アイコンの設定|
||iconStyle.openText|文字列|+|ツリーを展開している際に表示するテキスト。|
||iconStyle.openCss|文字列|tree-icon-open|ツリーを展開している際に適用するcss。|
||iconStyle.closeText|文字列|-|ツリーを閉じている際に表示するテキスト。|
||iconStyle.closeCss|文字列|tree-icon-close|ツリーを閉じている際に適用するcss。|

上記設定値を用いたTreeTableの作成は公開しているソース内に含んでいます。

#### isTreeStatusKeep ※1

[Web Storage API](https://developer.mozilla.org/ja/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API)を使用しています。
ブラウザや環境等により使えない場合は、設定値をtrueで渡しても必ずfalseになります。
私の環境はEdge、IE11が使えず、Chromeは使えました。

#### onDrawCellValue ※2

指定した列の値を書き込む際のコールバックを設定します。
引数に設定する値は下記の3つです。

|引数|内容|
|:---|:---|
|value|セルに書き込む値|
|cell|tdのDOM要素|
|rowData|1レコード分のデータ|

指定する列は「columns」と対応する必要があります。

```js
	columns: ['text','value'],
	onDrawCellValue: {
		'text': function (value, cell, rowData) {
			// 任意の処理
		},
		'value': function (value, cell, rowData) {
			// 任意の処理
		},
	}
```

## 参考にしたサイト

・[Web Storage APIの使用](https://developer.mozilla.org/ja/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API)


## 最後に

様々なフレームワーク、ライブラリがオープンソース化されることで、それに依存してしまうことがあると思います。
依存してしまった結果、これがないとできないといった事象が起きてしまうことが少なからずあり、個人の技量が問われるようになったと感じています。
オープンソースを活用して工数を減らすことはもちろん大事ですが、仕様を理解せず振り回されないよう、技術を磨いていきたいものです。