概要
- 他の方のコードを書き直した。
- class構文を使い、大幅にリファクタリングした。
サンプルコード
<div class="form-group">
<input type='text' id="keyword" class="form-control" placeholder="好きなフルーツを入力してください">
<button type="button" id="submit" class="btn">検索</button>
</div>
<ul id="list"></ul>
/* twitter-bootstrap使用 */
#keyword {
display: inline-block;
width: 50%;
}
#submit {
display: inline-block;
background-color: #6495ed;
color: #fff;
padding: 5.5px 20px;
vertical-align: top;
}
#list {
padding: 0;
margin: 0 auto;
width: 50%;
list-style: none;
}
li {
border-radius: 5px;
box-shadow: 0.5px 0.5px 1px 1px rgba(0,0,0,0.1) inset;
background-color: #eee;
margin: 10px;
padding: 10px;
}
.form-group {
width: 75%;
margin: 20px auto;
text-align: center;
}
const fruits = ['apple', 'apricot', 'avocado', 'blueberry', 'cherry', 'coconut', 'cranberry', 'dragonfruit', 'durian', 'grape', 'grapefruit', 'guava', 'kiwi fruit', 'lemon', 'lime', 'lychee', 'mango', 'melon', 'watermelon', 'miracle fruit', 'orange', 'bloodorange', 'clementine', 'mandarine', 'tangerine', 'papaya', 'passionfruit', 'peach', 'pear', 'persimmon', 'physalis', 'plum/prune', 'pineapple', 'pomegranate', 'raspberry', 'rambutan', 'star fruit', 'strawberry'];
class IncrementalSearch {
constructor(option) {
const { input, result, data, notFound } = option;
this.data = data;
this.$input = $(input);
this.$result = $(result);
this.preVal = '';
this.notFound = notFound || 'not found';
this.bindEvent();
}
bindEvent() {
this.$input.on('keyup', () => this.suggestList());
}
makeTopMatchStr(str) {
return '^' + str;
}
makeReg(str){
const regStr = str.split('').map(this.makeTopMatchStr).join('|');
return new RegExp(regStr);
}
filterData(str) {
const reg = this.makeReg(str);
return this.data.filter(v => reg.test(v));
}
appendList(data) {
const $list = data.map(word => $('<li />').addClass('list').text(word));
this.$result.append($list);
}
clearList() {
this.$result.html('');
}
suggestList() {
const val = this.$input.val().trim().replace(/\s/g, '');
if (val === this.preVal) {
return;
}
this.clearList();
this.preVal = val;
if (val.length === 0) {
return;
}
const matchData = this.filterData(val),
result = matchData.length > 0 ? matchData : [this.notFound];
this.appendList(result);
}
};
$(function() {
new IncrementalSearch({
input: '#keyword',
result: '#list',
data: fruits,
notFound: '一致する果物はありませんでした'
});
});
解説
分割代入
- 連想配列から値を効率的に取り出し、キー名と同じ変数にそれぞれ代入。
// 実行コードのオプション設定
new IncrementalSearch({
input: '#keyword',
result: '#list',
data: fruits,
notFound: '一致する果物はありませんでした'
});
// クラス設定
class IncrementalSearch {
constructor(option) {
const { input, result, data, notFound } = option;
空白の削除
- replace + 正規表現で空白指定(
\s
)。 - これで全部消せるのだが、ネイティブメソッドに前後の空白を消す
trim()
があるので、最初にそれをした上で、入力文字列の間に残った空白を消す感じに。
const val = this.$input.val().trim().replace(/\s/g, '');
正規表現文字列の作成
-
ab
という文字列 →/^a|^b/
という正規表現に変換。- 先頭一致(
^a
^b
)を or(|
) で繋いでいく形。
- 先頭一致(
-
^a|^b
という文字列まで作って、 その文字列を引数にnew RegExp()
すると 正規表現オブジェクトの/^a|^b/
になる。
makeTopMatchStr(str) {
return '^' + str;
}
makeReg(str){
const regStr = str.split('').map(this.makeTopMatchStr).join('|');
return new RegExp(regStr);
}
配列から条件に合致する要素を抽出
- filterの基本的な使い方 + 正規表現によるマッチング。
- 正規表現オブジェクトが持つ
test
メソッドを利用すると、結果がtrue, falseで返ってくる。 - filter内で配列内の要素を引数にtestメソッドを使えば、条件を満たすもの(trueを返す要素)だけを抽出できる(
!
を使えば逆も可能)。
filterData(str) {
const reg = this.makeReg(str);
return this.data.filter(v => reg.test(v));
}
アペンド
- 通常の配列を元にDOM配列を作成。
- 出来上がったDOM配列を丸ごとappendする。
appendList(data) {
const $list = data.map(word => $('<li />').addClass('list').text(word));
this.$result.append($list);
}
処理の中断
- if文とreturnを利用して、処理を途中で終わらせる。
- returnの後のコードは、それまでの中断条件を全て満たさなかった場合のみ実行される。
// 無条件で実行する
if (val === this.preVal) {
return;
}
// val !== this.preVal なら実行
if (val.length === 0) {
return;
}
// val !== this.preVal かつ val.length > 0 の場合に実行