[JavaScript]jQueryプラグイン、select2を始める！そして、select2の改善事項

##**1%足りないエレメント、`select`**
HTML5ではselectエレメントを利用してComboBoxコントロールを実装することができます。しかし、**基本エレメントだけでは期待値が高い使用者の厳しい要求を受け入れることに機能があまりにも足りないです。**
このようなselectエレメントの足りない機能を強化するアドオン性格のライブラリが結構ありますが、今回の文章では**多く使用されるjQueryプラグイン、Select2の説明と改善事項**に関して説明させていただきたいお思っております。

##**Select2の特徴**
本格的に使用法を整理するのは**[🔗ドキュメント](https://select2.github.io/)**に本当に優しく説明しておりますので、ご覧ください。Select2ライブラリーの基本的な**特徴**は以下の通りであります。
**自動完成(AutoComplete)とAJAXを利用した遠隔データ読み込みが可能**であります。
自動完成の場合は日本語は文字単位から可能です。また、**jQuery基盤のプラグインでjQueryが必須**です。**Bootstrapテーマを支援して違和感なく使用が可能**です。
GPL v2ライセンスです。商業的な目的の修正がない以上、**無料**使用が可能です。

##**概要**
皆様、**[🔗select2](https://select2.github.io/)**というjQueryプラグインをよく使っていますね。
利用ケースは**[🔗こちら](https://select2.github.io/examples.html)**にある例たちをご覧ください。
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/322d5719-c163-fa93-8fb0-8a03dc30df10.png)

前回のプロジェクトでこのプラグインを利用しましたが、
**改善事項がいくつか必要になりましたので、同じ状況の方に共有したい**と思っています。

##**なぜ、改善が必要か**
前回のプロジェクトで求めている`<select>`要素は以下のとおりでした。
**・Pulldownリストで選択**
**・マルチ選択**
**・選択した項目はタグとして配置**
**・チェックボックスがあるPulldownリスト**

**[🔗ドキュメントにある例]()**を見て、「以外と簡単に出来るじゃん〜:-)」と思いました。
しかし、 **問題がDone!!!ＹＡ・ＢＡ・Ｉ ！**
一応以下の内容で動作しておりますが、**いくつかの改善事項が必要**でした。

**✪求めている<select>の通り動作するシンプルな設定方法**

```js:select2_example.js
$(".user_list").select2({
    closeOnSelect : false
});
```
結果のご参考は**[🔗こちら](http://codepen.io/stephenbe/pen/zBrzYP)**

**✪改善事項**
① マルチ選択する場合、Pulldown ListエリアのScrollTopがリセットされる。
② マルチ選択する場合、Pulldown Listエリアが固定され、入力タグが積み重ねるエリアを隠してしまう。
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/07197d2d-1a93-b512-f049-8e65ddbedfc3.png)
③ マルチ選択する場合、PulldownList以外のエリアをクリックしてもPulldownListエリアが閉められない。
④ マルチ選択する場合、PulldownListにチェックボックスがあるフォマットを提供していないので改善が必要
⑤ 選択のエリアとタグが重ねるエリアを分けるようなフォマットを提供していないので改善が必要

##**改善内容**
**① マルチ選択する場合、Pulldown ListエリアのScrollTopが維持されるように対応**
以下のコードをご覧するとすぐご存知だと思いますが、選択したり選択したものを外する場合、クリックした要素(Object)のoffsetTopを利用しております。

```js:select2u.js
const SELECT2_PULLDOWNLIST_OPTIONS_AREA_SELECT_NAME = '.select2-dropdown > .select2-results > .select2-results__options';
const SELECT2_PULLDOWNLIST_OPTIONS_SELECT_NAME = '.select2-results__option';
const SELECT2_PULLDOWNLIST_OPTION_MOUSE_OVER_CLASS_NAME = 'select2-results__option--highlighted';

var select2u = {

	//このObjectの全判に扱う要素を定義する
	$select2: null,

	//メインのjQueryプラグインSelect2の要素に設定する
	setInit: function(targetClassName) {
		select2u.$select2 = $("."+targetClassName).select2({ /*options*/ });
	}

	setSelect2SelectEventHandler: function() {
		select2u.$select2.on("select2:select", function (e) {
			select2u.getEventCtl(e);
		});
	},

	setSelect2UnSelectEventHandler: function() {
		select2u.$select2.on("select2:unselect", function (e) {  
			select2u.getEventCtl(e);
		});
	}, 

	//selectとunselectの場合の処理が一緒で、違うパラメーターだけ渡す
	getEventCtl: function(e) {
		var targetElem = e.params.data.element.offsetTop;

	    //PulldownListAreaのスクロールを維持する
	    $(SELECT2_PULLDOWNLIST_OPTIONS_AREA_SELECT_NAME).scrollTop(targetElem*1.4);
	    //PulldownListAreaのOptionたちのmouseOverの表示（背景）をリセットする
	    $(SELECT2_PULLDOWNLIST_OPTIONS_SELECT_NAME).removeClass(SELECT2_PULLDOWNLIST_OPTION_MOUSE_OVER_CLASS_NAME);
	    //PulldownListAreaのOptionの正しいターゲットにmouseOverの表示（背景）ををセット
	    $(targetElemId).addClass(SELECT2_PULLDOWNLIST_OPTION_MOUSE_OVER_CLASS_NAME);
	}
};
```

**② マルチ選択する場合、Pulldown Listエリアが固定され、入力タグが積み重ねるエリアを隠してしまう現状解決**
こちらは、上端で紹介した、`closeOnSelect : false`の設定をやめて、実装しました。
対応はものすごく簡単です。

```js:select2u.js
var select2u = {

	//このObjectの全判に扱う要素を定義する
	$select2: null,

	//メインのjQueryプラグインSelect2の要素に設定する
	setInit: function(targetClassName) {
		select2u.$select2 = $("."+targetClassName).select2({ /*options*/ });
	}

	setSelect2SelectEventHandler: function() {
		select2u.$select2.on("select2:select", function (e) {
			select2u.getEventCtl(e);
		});
	},

	setSelect2UnSelectEventHandler: function() {
		select2u.$select2.on("select2:unselect", function (e) {  
			select2u.getEventCtl(e);
		});
	}, 

	//selectとunselectの場合の処理が一緒で、違うパラメーターだけ渡す
	getEventCtl: function(e) {
		select2u.$select2.select2("open");
	}
};
```
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/161be8d2-c9cd-edd6-1ef8-efc645400edd.png)

**④ マルチ選択する場合、PulldownListにチェックボックスがあるフォマットを提供していないので改善**
プラグインのオブジェクトから提供しているtemplateオブジェクト設定で実装を入れます。
`templateResult`のオプションで内部項目に関して調整とその意外の処理をしています。

```js:select2u.js
const SELECT2_PULLDOWNLIST_CHECKED_OPTIONS_SELECT_NAME = '.select2-results__option[aria-selected = true] > input';

var select2u = {

	//このObjectの全判に扱う要素を定義する
	$select2: null,

	//メインのjQueryプラグインSelect2の要素に設定する
	setInit: function(targetClassName) {
		select2u.$select2 = $("."+targetClassName).select2({
			//要素(options)を操作するテンプレートメソッド
			templateResult: function (data) {
				if (data.id === '') {
					return '選択する項目がありません';
				}
				//要素(options)を確認して内容にcheckboxを追加してReturnする
				return "<input type='checkbox' class='select2-pulldown-checkbox'><label for='checkboxId'>"+data.text+"</label>";
			},
			//マークアップに設定する(checkboxのため)
			escapeMarkup: function (markup) { return markup; },
			//その以外の設定(項目以外入れる ＆ 全体Cancelボタンの設定を一応コメントアウトする)
			// allowClear: true,
			// tags: true
		});
		
	}

	setSelect2SelectEventHandler: function() {
		select2u.$select2.on("select2:select", function (e) {
			select2u.getEventCtl(e);
		});
	},

	setSelect2UnSelectEventHandler: function() {
		select2u.$select2.on("select2:unselect", function (e) {  
			select2u.getEventCtl(e);
		});
	}, 

	//selectとunselectの場合の処理が一緒で、違うパラメーターだけ渡す
	getEventCtl: function(e) {
		$(SELECT2_PULLDOWNLIST_CHECKED_OPTIONS_SELECT_NAME).prop('checked', true);
	}
};
```
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/dd63253b-9559-ec8e-e77c-c892f068c14e.png)

**⑤ 選択のエリアとタグが重ねるエリアを分けるようなフォマットを提供していないので改善**
`templateResult: function (data) {}`と`select2u.$select2OnlyDisplay.val(select2u.$select2.val()).trigger("change");`の部分が重要ポイントです。
選択のみの`<select>`要素に選択してもタグが生成されない設定をしておいて、
タグのみの要素には、Pulldown Listが出ないように設定しております。
そして、選択のみの予想から選択した場合、タグ要素の値をトリガーします。

```js:select2u.js
var select2u = {

	//このObjectの全判に扱う要素を定義する
	$select2: null,
	$select2OnlyDisplay: null,

	//メインのjQueryプラグインSelect2の要素に設定する
	setInit: function(targetClassName) {
		select2u.$select2 = $("."+targetClassName).select2({ /* options */ });
	},

	//タグ表示のみの要素の設定する
	setOnlyDisplayInit: function(targetClassNameToOnlyDisplay) {
		return new Promise((resolve, reject) => {
			select2u.$select2OnlyDisplay = $("."+targetClassNameToOnlyDisplay).select2({
				// allowClear: true,
				// tags: true,
				//PulldownListが表示されないように何もしない
				templateResult: function (data) {}
			});
		});
	},

	setSelect2SelectEventHandler: function() {
		select2u.$select2.on("select2:select", function (e) {
			select2u.getEventCtl(e);
		});
	},

	setSelect2UnSelectEventHandler: function() {
		select2u.$select2.on("select2:unselect", function (e) {  
			select2u.getEventCtl(e);
		});
	}, 

	//selectとunselectの場合の処理が一緒で、違うパラメーターだけ渡す
	getEventCtl: function(e) {
		select2u.$select2OnlyDisplay.val(select2u.$select2.val()).trigger("change"); 
	}
};
```

##**まとめ**
👏👏👏👏👏👏👏