LoginSignup
3
5

More than 5 years have passed since last update.

インクリメンタルサーチ

Last updated at Posted at 2017-02-08

概要

  • 他の方のコードを書き直した。
  • 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 の場合に実行

外部リンク

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5