今回はAutoCompleteを自作する第2回です。第1回の記事の続きとなります。まだ見てない方はこちらから。
WEBシステムで入力フォームを作っている際に必ずと言ってもいいほど必要になるの「AutoComplete(Suggest)」の機能を自作していきます。
Vue.jsの学習をしながら段階的にAutoCompleteの機能を作っていきます。
第2回の完成ページ
前回はAjax経由で取得したデータをサジェストリストとして表示するところまでを作成しました。
今回はサジェストリストのフォーカス表示などの主に見た目の部分を改良していきます。
第2回が終わった時に出来上がるページについては以下のようになります。ポイントは以下です。
- 上下のキーボード押下に合わせて選択されたアイテムをフォーカス表示(Action=True)
- マウスオーバーしたアイテムもフォーカス表示(Action=True)

動画も掲載しておきます。上下のキーボードのクリック時の動きは少し分かりづらいかもしれませんが、キーのクリックに合わせて選択アイテムのフォーカスが変わっていることが分かります。また、マウスオーバーに合わせてアイテムのフォーカスも変わっていることが分かります。
サジェストリストのフォーカス表示機能の追加
AutoCompleteコンポーネントの修正
今回はVueコンポーネントの修正だけとなります。最終的なコードは以下となります。
resources/js/components/AutoCompleteComponent.vue
<template>
    <div>
        <input
            type="text"
            placeholder="what are you looking for?"
            v-model="query"
            class="form-control"
            autocomplete="off"
            name="email"
            @input="getSuggestionList"
            @change="change"
            @focus="open = true"
            @keydown.down="down"
            @keydown.up="up"
            @keydown.enter="enter"
        />
        <div class="panel-footer" v-if="results.length && open">
            <ul class="list-group">
                <li
                    v-for="(result, index) in results"
                    v-bind:key="result.index"
                    v-bind:class="{ active: index === current }"
                    class="list-group-item list-group-item-action"
                    @click="suggestionClick(index)"
                    @mouseover="mouseOver(index)"
                    v-text="result.email"
                ></li>
            </ul>
        </div>
    </div>
</template>
<script>
export default {
    data: function() {
        return {
            query: "",
            results: [],
            open: false,
            current: 0
        };
    },
    methods: {
        getSuggestionList() {
            this.results = [];
            if (this.query.length >= 1) {
                axios
                    .get("/search/email", {
                        params: { query: this.query }
                    })
                    .then(Response => {
                        this.results = Response.data;
                        if (this.results) {
                            this.open = true;
                        }
                        console.log(this.results);
                    })
                    .catch(error => {
                        console.log(error);
                    });
            }
        },
        mouseOver: function(index) {
            this.current = index;
        },
        up() {
            if (this.current > 0) this.current--;
        },
        down() {
            if (this.current < this.results.length - 1) this.current++;
        },
        change() {
            if (this.open == false) {
                this.open = true;
                this.current = 0;
            }
        },
        enter() {
            if (this.results[this.current]) {
                this.query = this.results[this.current].email;
                this.results = this.results[this.current];
                this.open = false;
                this.current = 0;
            }
        },
        suggestionClick(index) {
            this.query = this.results[index].email;
            this.open = false;
            this.current = 0;
            this.results = this.results[index];
        }
    }
};
</script>
動作確認
動作を確認してみましょう。npm run devを実施してからアクセスして下さい。
解説
それでは解説していきます。
今回はマウスオーバやキーボードが押された時のイベントに対応する部分がメインとなります。コード量がかなり増えてますが、イベントに対応したメソッドを都度呼び出しているだけですので整理してひとつずつ見ていきます。
マウスオーバ時の動作
まずはマウスオーバ時の動作になります。
少し説明が難しいですががんばって説明してみます。(分かりづらかったらすみません)
ポイントは変数のcurrentです。current=Trueの時だけactiveがtrueとなってフォーカスされるということを頭に入れておいてください。
マウスオーバー時はmouseOverメソッドへindexを渡しています。渡されるindexはresultsの配列番号です。下の画像の場合でいうと3番目のリストにマウスオーバした際には配列番号である2が渡されcurrent=2になります・
activeがtrueになる条件はindex=currentです。そのためindex=2の時のループがcurrent=2でActiveオンとなりフォーカスされます。

該当する部分:
v-for="(result, index) in results"
v-bind:key="result.index"
v-bind:class="{ active: index === current }"
@mouseover="mouseOver(index)"
mouseOver: function(index) {
        this.current = index;
},上下キーボードが押された時の動作
次に上下のキーボードが押された時の動作について説明します。
基本的には先ほどのマウスオーバと同様にcurrentでの制御となります。上が押されたらフォーカスを1つ上に移動したいので、currentの値を-1しています。ただし、currentは0より下にはならないように条件つけて制御しています。
逆に下を押したときも同様にresultsの数より上にならないように制御しています。
該当する部分:
@keydown.down="down"
@keydown.up="up"
//When up pressed while suggestions are open
up() {
    if (this.current > 0) this.current--;
},
//When up pressed while suggestions are open
down() {
    if (this.current < this.results.length - 1) this.current++;
},Enterキーが押された時の動作
最後にEnterキーが押された時の動作です。
フォーカスしていた値をqueryにセットしています。どの値をセットするか判断するためにcurrentを使ってます。
また、サジェストリスト(results)に選択した値だけをセットしなおしてます。これは値を設定した後に動きがあった時に前のサジェストを表示されるのを防止するためです。
あわせて、open=falseとしてサジェストリストを非表示にすると共にcurrentを0にします。currentを0にするのは、再度サジェストをした時に前のフォーカスの場所にあたってしまうずれ防止するためです。
実際にresultsのセットをしない場合の動作や、 open=falseやcurrent=0を無効化した場合の動作を確認してみてください。実際の動きが理解できると思います。
該当する部分:
@keydown.enter="enter"
//When the press enter 
 enter() {
    if (this.results[this.current]) {
        this.query = this.results[this.current].email;
        this.results = this.results[this.current];
        this.open = false;
        this.current = 0;
    }
},第2回まとめ
今回はキーボードが押されたり、マウスオーバーした時の見た目のフォーカス表示の制御をおこないました。
はじめはかなり難しいと感じると思いますが、仕組みさえわかってしまえば意外と簡単に理解できるのではないでしょうか。
次回はもう少し見栄えをよくするためにサジェストにマッチした文字の強調表示の機能を追加していきます。
今回は以上となります。
 
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
  
  
  
  


コメント