【Vue.js】フォーム入力中に候補を表示させる

2020年8月5日

何か検索するときにテキストボックスにテキストを入力していたら、候補がしたに出てくることありませんか?

あれがどうしても実装したくて、素のjavascript(jQuery)でゴリゴリ頑張って描いたこともありましたが、今回はJSのフレームワーク(ライブラリ)のVue.jsを使って入力候補を表示できる機能を実装していこうと思います。

webアプリとかで、カテゴリ・タグを入力する時、すでにあるものを候補にぽんっと出してくれたらめちゃめちゃ助かります。(このブログを投稿するときにも思います笑)

今回の環境と目標

Vue.js:2.0
jQuery:あり
html, css, js

検索ボックスと候補欄のデザイン
検索ボックスに入力すると同時に候補をリストアップする
候補はVueオブジェクトのdataプロパティの中にオブジェクトの配列として用意。(外部から用意できる様にする)
[{name: "},{name: "},{name: "}]
リストを選択したらボックスに挿入させる
フォーカスイン、フォーカスアウトでリストを表示・非常時

それではやっていきます。

フォームとリストを作る

まずはざっとデザインします。

<!-- html -->
<div id="form1">
  <input>
  <div id="kouhos">
    <ul>
      <li>りんご</li>
      <li>ゴリラ</li>
      <li>ラスベガス</li>
    </ul>
  </div>
</div>
<!-- css -->
#form1{
      width: 50%;
      margin: 10px;
      position: relative;
    }
    #form1 input{
      width: 100%; /*親要素いっぱい広げる*/
      padding: 10px 15px; /*ボックスを大きくする*/
      font-size: 16px;
      border-radius: 3px; /*ボックス角の丸み*/
      border: 2px solid #ddd; /*枠線*/
      /* box-sizing: border-box; */
    }
    #form1 input:focus {
      box-shadow: 0 0 5px 0 rgba(255,153,0,1);
      border: 2px solid #FFF;
      outline: 0;
      position: relative;
    }
    #kouhos{
      width: 100%;
      background-color: white;
      position: absolute;
    }
    #kouhos ul{
      margin: 0;
      border: 1px solid #EEE;
      padding-left: 0;
    }
    #kouhos li{
      border: 1px solid white;
      list-style-type: none;
      padding: 0.8rem 5%;;
    }
    #kouhos li:hover{
      background-color: lightskyblue;
      color: white;
      text-decoration: underline;
    }
  • りんご
  • ゴリラ
  • ラスベガス

こんな感じです。

このリストが、フォーカスしたときに出てきて、フォーカスが外れたら消えます。

Vueオブジェクトを生成する

それでは組み込んでいきます。

// js
var form1 = new Vue({
        el: '#form1',
        data: {
          searchText: '',
          tags: []
        },
        computed: {
          filteredTag: function () {
            var f_tags = [];
            this.tags.forEach((tag, i) => {
              if (tag.name.includes(this.searchText)) {
                f_tags.push({name: tag.name});
              }
            });
            return f_tags;
          }
        }
      });
<!-- html(修正) -->
<div id="form1">
  <input v-model="searchText">
  <div id="kouhos">
    <ul>
      <li v-for="tag in filteredTag">{{tag.name}}</li>
    </ul>
  </div>
  <p>メッセージ:{{searchText}}</p>
</div>

流れは次のようになります。

  1. v-modelでinput要素のvalとVueのsearchTextプロパティの値をバインド
  2. tagsプロパティには後からデータをいれる。
  3. tagsプロパティの各値の文字列にsearchTextが入っていればそれらをv-forでリストアップ!

Vueオブジェクトのざっと概要

詳細は公式マニュアルがわかりやすいので割愛しますが、Vueのテンプレとしては決まっていて、次のようになります。

elプロパティで要素取得(コンテナとなる)
dataプロパティに入ったものが(Vueオブジェクト).(プロパティ名)として取り出せる。
computedプロパティで関数を定義

となります。

computedプロパティに定義したfilteredTagを見てみます。

filteredTag: function () {
  var f_tags = [];
  this.tags.forEach((tag, i) => {
    if (tag.name.includes(this.searchText)) {
      f_tags.push({name: tag.name});
    }
  });
  return f_tags;
}

最終的にreturnする(リストアップする)のがf_tags配列です。

thisはこのVueオブジェクトを指します。

tagsには、[{name: 'りんご'},{name: 'ウサギ'},{name: 'かめ'}]のようにtagがあるので、tag.name.includes(this.searchText)searchTextに一致するものを配列としてまとめ、返します。

データは適当に入れておきます。jsonファイルや、phpを通してデータベースを取得したりすれば本格的に使えそうです。

var arr = [
  {name: 'りんご'},
  {name: 'リンカーン'},
  {name: '林道'},
  {name: 'りんちゃん'}
];
form1.tags = arr;

これで、すでにv-modelでバインディングしてv-forでリストアップが実現できました!

表示・非表示、リスト選択スクリプト

次にリストの表示・非表示、タグを選択したらテキストボックスに挿入するスクリプトを作成します。これはお好みでどうぞ。

// リスト現れる
$('#form1 input').focusin(function(){
  $('#kouhos').show();
});

// リスト消える(ちょっと遅延)
$('#form1 input').focusout(function(){
  setTimeout(function(){
    $('#kouhos').hide();
  },100);
});

// テキストボックスに挿入
$('#kouhos li').click(function(){
  $('#form1 input').val($(this).text());
});

これはざっと、単純に見たまんまって感じです。説明は省略(笑)

ただし、1点沼に入ったところがfocusoutイベントとclickイベントの実行順序の関係です。

どうしてもfocusoutの方が時系列が先なのでうまく処理ができず($('#kouhos').hide();するとDOM操作ができなくなり、テキストボックスに挿入するイベントが発火しなくなってしもた)setTimeoutでさくっと遅延を入れています。

最後にリストを非表示にすれば完成です!

まとめ

次のようになります。

「j」と入力すると「j」が入っているものだけが残ります。