Excelで支給された元データを極力加工せず、JSONとjQueryを用いて、いかにして早くHTMLを作るか？

HTML、JavaScriptを扱う人を対象に投稿しています。

# 要件

```
<ul>
	<li>カテゴリA
		<ul>
			<li><a href="DUMMY">カテゴリAの商品1</a></li>
			<li><a href="DUMMY">カテゴリAの商品2</a></li>
			<li><a href="DUMMY">カテゴリAの商品3</a></li>
		</ul>
	</li>
	<li>カテゴリB
		<ul>
			<li><a href="DUMMY">カテゴリBの商品1</a></li>
			<li><a href="DUMMY">カテゴリBの商品2</a></li>
			<li><a href="DUMMY">カテゴリBの商品3</a></li>
		</ul>
	</li>
</ul>
```

といった、ネストされた```<ul><li>```のHTMLを作るという要件で、カテゴリ、URL、商品名が、みっちりぎっしり大量に記載されたExcelファイルを渡されたとします。
そのExcelファイルが下記の書式（1行につき1商品）だったら、どうしますか？

![1.png](https://qiita-image-store.s3.amazonaws.com/0/47173/8fb96ed8-8e0b-5147-4000-ca95dfa8a6a5.png)
：
![6.png](https://qiita-image-store.s3.amazonaws.com/0/47173/55b07b48-bd98-1475-f4c6-116442264188.png)


# 実装方法

まさか手作業でHTML化するわけにはいきません。
ExcelのデータをJSONに書き出して、JavaScriptでHTMLを組み立てる方法を検討しました。
（他の言語を用いれば、もっと早くできるかもしれませんが、HTML、jQueryを扱う人向けの提案です）

問題はネストの部分。
このExcelの書式では、下記のような入れ子のJSONを作ることが難しそうです。

```
[
	{
		"category":"カテゴリA",
		"product":[
			{"name":"カテゴリAの商品1", "url":"http://XXX/a_01.html"},
			{"name":"カテゴリAの商品2", "url":"http://XXX/a_02.html"},
			{"name":"カテゴリAの商品3", "url":"http://XXX/a_03.html"}
		]
	},
	{
		"category":"カテゴリB",
		"product":[
			{"name":"カテゴリBの商品1", "url":"http://XXX/b_01.html"},
			{"name":"カテゴリBの商品2", "url":"http://XXX/b_02.html"},
			{"name":"カテゴリBの商品3", "url":"http://XXX/b_03.html"}
		]
	},
	{
		"category":"カテゴリC",
		"product":[
			{"name":"カテゴリCの商品1", "url":"http://XXX/c_01.html"},
			{"name":"カテゴリCの商品2", "url":"http://XXX/c_02.html"},
			{"name":"カテゴリCの商品3", "url":"http://XXX/c_03.html"}
		]
	}
]
```

そこで入れ子のJSONを作らずに、Excel1行につき、1オブジェクトとしたJSONからHTMLを出力させます。

## 作業1. ExcelデータのJSON化

ExcelデータをJSON化するには様々な手段がありますが、以下の方法を用いました。

1. 元Excelのラベル（「カテゴリ名」「商品名」「URL」）をキーに変更（「category」「name」「url」）
![2.png](https://qiita-image-store.s3.amazonaws.com/0/47173/c6a9cf93-1498-4e52-c1bc-3b882413fcae.png)
2. 元ExcelをCSVデータとして保存
3. CSV to JSON - CSVJSON http://www.csvjson.com/csv2json でコンバート
![3.png](https://qiita-image-store.s3.amazonaws.com/0/47173/ec600ba4-43ae-b946-43d4-22b51052d67e.png)

下記のJSONファイルを素早く準備できました。

### sample.json

```
[
  {
    "category": "カテゴリA",
    "name": "カテゴリAの商品1",
    "url": "http://XXX/a_01.html"
  },
  {
    "category": "カテゴリA",
    "name": "カテゴリAの商品2",
    "url": "http://XXX/a_02.html"
  },
  {
    "category": "カテゴリA",
    "name": "カテゴリAの商品3",
    "url": "http://XXX/a_03.html"
  },
  {
    "category": "カテゴリB",
    "name": "カテゴリBの商品1",
    "url": "http://XXX/b_01.html"
  },
  {
    "category": "カテゴリB",
    "name": "カテゴリBの商品2",
    "url": "http://XXX/b_02.html"
  },
  {
    "category": "カテゴリB",
    "name": "カテゴリBの商品3",
    "url": "http://XXX/b_03.html"
  },
  {
    "category": "カテゴリC",
    "name": "カテゴリCの商品1",
    "url": "http://XXX/c_01.html"
  },
  {
    "category": "カテゴリC",
    "name": "カテゴリCの商品2",
    "url": "http://XXX/c_02.html"
  },
  {
    "category": "カテゴリC",
    "name": "カテゴリCの商品3",
    "url": "http://XXX/c_03.html"
  }
]
```

## 作業2. HTML、jQueryコードの作成

sample.jsonからHTMLを組み立てるコードを書きます。

### HTML、jQuery

```
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title></title>
<script
  src="https://code.jquery.com/jquery-3.1.1.js"
  integrity="sha256-16cdPddA6VdVInumRGo6IbivbERE8p7CQR3HzTBuELA="
  crossorigin="anonymous"></script>
</head>
<script>
$.getJSON('sample.json', {}, function(json){

	// 変数の宣言
	var htmlString = '';
	var htmlStringWrap = '';
	var prevCategory = '';
	var category = '';

	$.each(json, function(i, obj){

		// 直前のループにおける変数「category」を「prevCategory」に代入（ループ初回時の値は初期値の「''」）
		prevCategory = category;

		// キー「category」の値を変数「category」に代入
		category = obj.category;

		// ループのたびに変数「htmlStringWrap」を初期化
		htmlStringWrap = '';

		// 【第1階層の<li>を作成】「prevCategory」と「category」の値が異なる場合に、<ul>を内包する<li>を、変数「htmlStringWrap」にセット
		// ループ初回の「\n</ul>\n</li>」はinvalidなHTMLのため、無視される
		if (category !== prevCategory) { 
			htmlStringWrap = '\n</ul>\n</li>\n<li>' + category + '\n<ul>';
		}

		// 【第2階層の<li>を作成】ループを回し、obj.url と obj.name に基づいたリンクを内包する<li>を、変数「htmlStringWrap」と連結して、変数「htmlString」に順繰りに追加
		htmlString += htmlStringWrap + '\n<li><a href="' + obj.url + '">' + obj.name + '</a></li>';

	});

	// JSONを元に形成したHTML（変数「htmlString」）を<ul id="link">～</ul>内に追加
	$('#link').html(htmlString);

});
</script>
</head>
<body>
<ul id="link">
</ul>
</body>
</html>

```

### 出力結果

```
<ul id="link">


<li>カテゴリA
<ul>
<li><a href="http://XXX/a_01.html">カテゴリAの商品1</a></li>
<li><a href="http://XXX/a_02.html">カテゴリAの商品2</a></li>
<li><a href="http://XXX/a_03.html">カテゴリAの商品3</a></li>
</ul>
</li>
<li>カテゴリB
<ul>
<li><a href="http://XXX/b_01.html">カテゴリBの商品1</a></li>
<li><a href="http://XXX/b_02.html">カテゴリBの商品2</a></li>
<li><a href="http://XXX/b_03.html">カテゴリBの商品3</a></li>
</ul>
</li>
<li>カテゴリC
<ul>
<li><a href="http://XXX/c_01.html">カテゴリCの商品1</a></li>
<li><a href="http://XXX/c_02.html">カテゴリCの商品2</a></li>
<li><a href="http://XXX/c_03.html">カテゴリCの商品3</a></li></ul></li></ul>
```

![4.png](https://qiita-image-store.s3.amazonaws.com/0/47173/0b9b7061-b1c8-30c7-a519-1764bb086cda.png)


## 補足

### ループ初回の「```</ul></li>```」はinvalidなHTMLのため、無視されます。

```
<ul id="link">
</ul>
</li>
<li>カテゴリA
<ul>
<li><a href="http://XXX/a_01.html">カテゴリAの商品1</a></li>
<li><a href="http://XXX/a_02.html">カテゴリAの商品2</a></li>
<li><a href="http://XXX/a_03.html">カテゴリAの商品3</a></li>
</ul>
</li>
【中略】
<li>カテゴリC
<ul>
<li><a href="http://XXX/c_01.html">カテゴリCの商品1</a></li>
<li><a href="http://XXX/c_02.html">カテゴリCの商品2</a></li>
<li><a href="http://XXX/c_03.html">カテゴリCの商品3</a></li></ul></li></ul>
```
にはなりません。

##### 参照: innerHTML や jQuery.html() は HTMLをそのまま取得できるわけではない

http://jmblog.jp/archives/876

### 静的HTMLとして保存したい場合

2つの方法をとることができます。

* ブラウザの「名前を付けて保存」で、ファイルの種類を「ウェブページ、完全」とすると、HTMLが出力された状態のファイルを取得できます。
* 開発ツールでDOM要素を右クリック「Copy > Copy outerHTML」し、必要な範囲をコピー。
![5.png](https://qiita-image-store.s3.amazonaws.com/0/47173/8e866725-7555-fe46-a63a-a01eeaf64726.png)


支給されたデータを、あれやこれや加工してしまうと、データに齟齬が生じてしまうのではないかと考えました。
支給データはなるべくいじらず、省エネモードで、さくさくとHTMLを作成する、そんなコーダーでありたいと思います。