jQueryでニコニコみたいなタグ追加機構を作る

結構悩んだ上に笑える落とし穴があったのでメモがてら。

結論から言うと、domを後付けで増やしまくる構造なので、イベント拾うためにちゃんと書かなきゃいけない。
・増やしたボタンは.clickでなく、.on("click",～）で拾う
・増やしたタグの再読み込みもちゃんと考慮すること

使用バージョンは1.11.4。たぶんonが使える1.9以降なら大丈夫。たぶん。
IE11とちょろめで動作確認済み。

html的にこんな感じ。
初期表示分のタグについてはイテレータやらなにやらでちゃんと連番を付けていると想定。

```html:sandbox.html
<script src="./js/sandbox.js"></script>
（略）
<h1>sandbox - タグを編集してみるテスト</h1>
<div id="wrap-tag">
 <div id="tags">
  <span id="1">タグ１</span>
  <span id="2">タグ２</span>
  <span id="3">タグ３</span>
  <span id="4">タグ４</span>
  <span id="5">タグ５</span>
  <span id="6">タグ６</span>
  <span id="7">タグ７</span>
 </div>
</div>

<div class="form-group">
 <button type="button" class="btn btn-default neon" id="edit_btn" value="edit" >編集</button>
</div>

<div id="tagform"></div>

```


見え方
sandbox - タグを編集してみるテスト
タグ１ タグ２ タグ３ タグ４ タグ５ タグ６ タグ７

編集（ボタン）

で、jQuery側。エラー処理とかは結構適当なので適宜どうぞ。

```javascript:sandbox.js

$(document).ready(function(){
	console.log("sandbox page is loaded.");

	// IEは悪い文明！粉砕する！（
	Number.isNaN = Number.isNaN || function(any) {
	  return typeof any === 'number' && isNaN(any);
	};

	// 編集ボタン押下時
	$(".form-group").on("click", "#edit_btn", function(){
		var val = $(this).val();
		console.log('clicked', val);
		if(val == "edit"){
			$('#tags span').each(function(){
				$(this).append("<button type=\"button\" class=\"btn btn-default neon\" id=\"delete_btn\" value=\"" + this.id + "\" >削除</button>");
			});
			$('#edit_btn').text("編集完了");
			$('#edit_btn').val("end_edit");
			var form = $("<form>").attr({"class":"tageditform","action":"#"}),
			input = $("<input>").attr({"class":"tageditinput","type":"text"});
			$(form).append(input).append("<button type=\"button\" class=\"btn btn-default neon\" id=\"insert_btn\" value=\"add\" >追加</button>").appendTo($("#tagform"));

			// 追加ボタン押下時 編集ボタン押下後の単発部分に書かないと多重処理になることに注意
			$(document).on("click", "#insert_btn", function(){
				var id = parseInt($('#tags span:last').attr("id"));
				var addid = Number.isNaN(id) ? 1 : (id + 1) ;
				var addtag = $(".tageditinput").val();
				console.log('clicked add', addid, addtag);
				if(addtag != ""){
					$('#tags').append("<span id=\"" + addid + "\">" + addtag + "<button type=\"button\" class=\"btn btn-default neon\" id=\"delete_btn\" value=\"" + addid + "\" >削除</button></span>");
				}
			});

			// 削除ボタン押下時
			$(document).on("click", "#delete_btn", function(){
				var delval = $(this).val();
				console.log('clicked delete', delval);
			    $(this).parents("#tags span").remove();
			});
		}
		else if(val == "end_edit"){
			// 編集完了処理
			console.log('ＤＢ更新処理とかしたり');
		}
	});
});
```

ajaxでDBと連携するなら追加時適宜でも通信する方がよいかも知れない。とりあえず見た目実装。

引っ掛かったポイント

$(#delete_btn).click(function(){　～（略
よくあるボタンの書き方だけど、これでやると最初に作ってないせいでボタンが有効にならないケースが出たりする。

その他凡ミス：数え切れないほど　エスケープ漏れとか（書き方変えろ説

もっと楽な書き方とかプラグインがあればいいんだけど、とりあえず強引すぎる力技でなんとかした系。たぶん。
（こっちは環境的にプラグイン入れられなけど）。

追記：NaNパターン消し忘れてたのでちょっとだけ修正

追記２：余りにコードが汚過ぎたのできれいにしてみた版。

```javascript:sandbox.js
$(document).ready(function(){

	console.log("sandbox page is loaded.");

	// IE死すべし慈悲はない（どう見ても慈悲
	Number.isNaN = Number.isNaN || function(any) {
	  return typeof any === 'number' && isNaN(any);
	};

	var append_html = function(target, add_html){
		target.append(add_html);
	}

	var add_form = function(target){
		var form = $("<form>").attr({"class":"tageditform","action":"#"}),
		input = $("<input>").attr({"class":"tageditinput","type":"text"}),
		button_html = '<button type="button" class="btn btn-default neon" id="insert_btn" value="add" >追加</button>';
		$(form).append(input).append(button_html).appendTo(target);
	}

	var press_add = function(){
		var id = parseInt($('#tags span:last').attr("id")),
		addid = Number.isNaN(id) ? 1 : (id + 1) ,
		addtag = $(".tageditinput").val();
		console.log('clicked add', addid, addtag);
		if(addtag != ""){
			var add_html = '<span id="' + addid + '">' + addtag + '<button type="button" class="btn btn-default neon" id="delete_btn" value="' + addid + '" >削除</button></span>',
			target = $('#tags');
			append_html(target, add_html);
		}
	}
	var press_del = function(own){
		var delval = own.val();
		console.log('clicked delete', delval);
		own.parents("#tags span").remove();
	}

	var press_edit = function(own){
		var val = own.val();
		console.log('clicked', val);
		if(val == "edit"){
			$('#tags span').each(function(){
				var add_html = '<button type="button" class="btn btn-default neon" id="delete_btn" value="' + this.id + '" >削除</button>',
				target = $(this);
				append_html(target, add_html);
			});
			own.text("編集完了");
			own.val("end_edit");

			// フォーム要素を追加する
			var target = $("#tagform");
			add_form(target);

			// 追加ボタン押下時 編集ボタン押下後の単発部分に書かないと多重処理になることに注意
			$(document).on("click", "#insert_btn", function(){
				press_add();
			});

			// 削除ボタン押下時
			$(document).on("click", "#delete_btn", function(){
				var own = $(this);
				press_del(own);
			});
		}
		else if(val == "end_edit"){
			// 編集完了処理
			console.log('ajaxで実際に更新処理投げたりとか');
		}
	};

	// 編集ボタン押下
	$(function(){
		$(".form-group").on("click", "#edit_btn", function(){
			var own = $(this);
			press_edit(own);
		})
	});
});
```