Written by TSUYOSHI

Vue・Nuxtでサジェストを自作する時はblurとclickイベントタイミングに注意

JavaScript PROGRAMMING Vue

本記事では、Vueでinputフォームにテキスト入力をする際にサジェスト表示をして、そのクリックでサジェストを選択する動作を自作する際のポイントをお伝えします。

今回の記事は完全にVueのプログラムについての解説になります。

この記事を書いている僕は、フロントエンドのプログラマー(フリーランス)として、これまで4年ほどのWeb制作経験があり、Vue,React,Nuxtなどフロントエンドの開発を得意としています。

Vueでサジェストを自作する時はblurとclickイベントタイミングに注意


Vueでinputにサジェストをつける実装を自前で行う場合について、解説します。

GitHubにサンプルを公開

今回のサンプルコードはGitHubにて公開しています。
https://github.com/it-web-life/vue-suggest-input-component

何でもいいならプラグインを使うのが楽

どうやら「vue-simple-suggest」と言うプラグインが有名どころみたいなのですが、細かくデザインしたりカスタマイズする際には自作する方がよかったりします。

今回起こった問題についても、調べて海外サイトなどでも同様に困っている人が多いようで具体的な解決策は見つからずだったのですが、「vue-simple-suggest」プラグインのコードを解析したら参考になる部分があって、対応しました。やはりプラグインの作者は頭がよい人が多いので、本当に参考になりますね。

サジェストの仕組み

サジェストの仕組み
サジェストは、inputタグ等に文字が入力された際にAPIから情報を取得してinputの下に表示する枠を出すというのが一般的な仕様かと思います。

よってinputタグで「focusイベント」が発生したり、「inputイベント」(文字入力あった時)が発生した時に、サジェスト領域を表示しますが、Vueであればv-ifv-showを使ってここの表示を制御します。

そしてサジェストに「Clickイベント」を仕込んで、Clickされたサジェストワードをinputタグの表示に反映させるというのが一般的な作りです。

サジェストを作る上でハマった「Clickイベント」と「Blurイベント」のタイミング


文字の入力をするinputタグが含まれる部分をInputコンポーネント(Input.vue)として作成し、サジェストを表示する部品をSuggestコンポーネント(Suggest.vue)として作成します。

  • Inputコンポーネントには、valueをsyncで渡し、inputタグにてfocusやblurイベントがあれば、親コンポーネントに通知する仕組みにしています。
  • Suggestコンポーネントには、Inputタグにフォーカス中かどうかのフラグとサジェストするべき文字列を渡し、サジェストワードがクリックされたらそのサジェストワードを親コンポーネントに通知する仕組みにしています。

そして、InputコンポーネントとSuggestコンポーネントを制御する親コンポーネントを「SuggestInput.vue」として作成します。「SuggestInput.vue」では「Input.vue」と「Suggest.vue」を読み込んで制御します。

SuggestInput.vue ※呼び出しをしている親コンポーネント

Input.vue ※子コンポーネント

Suggest.vue ※子コンポーネント

サジェスト表示中にサジェストをクリックするとClickイベントが発生しない問題

作りは簡単なのですが、inputタグにフォーカスしてサジェストが表示されている時に、サジェストをクリックしてもClickイベントが発生しないという問題が起きました。

以下のようにblurイベント発生でサジェストを非表示にしていたため、サジェストのClickイベントが正常に作動しないことが原因でした。

inputタグにフォーカスされて、サジェストが表示される
 ↓
サジェストをクリック操作する
 ↓
最初にinputタグのblurイベントが発生
 ↓
blur処理でサジェストをフラグで表示制御(v-showで表示・非表示制御)していたため、フラグが落ちてサジェストが消える
 ↓
サジェストのDOMが非表示になり、clickイベントが発生しない

blurで表示を非表示にしないと、サジェスト以外の箇所をクリックされた時に、サジェストを非表示にできなくなるので、blurイベントでの表示制御は必要で、修正方法に困りました。。。

サジェストのhover中はblurイベントを無視することで解決

解決方法は以下の通りです。

  • サジェストをhover中かどうかのフラグを新たに作成しました。hover中かどうかは、mouseoverイベントとmouseleaveイベントで制御しました。
  • サジェストをhover中に、blurイベントが発生した場合、そのblurイベントは無視するようにしました。hover中にblurが発生するということは、clickされたということが言えるので、このような処理にしました。

そして、この処理で問題の解決ができました。ちなみに、これは「vue-simple-suggest」プラグインの処理を参考にしました。

Reactでは同様の実装でも問題は起こらなかった

以前、Reactで似たようなサジェストの実装をしたことがありましたが、Reactでは問題は発生しませんでした。おそらく、仮想DOMの切り替えをsetStateで行っていたため、タイミング的に問題が起きなかったのだと思います。

Vueだと、v-ifv-showでサジェストの表示・非表示を制御することが多いと思うので、このあたりの違いでイベント発生のタイミングに影響を与えていたのかもしれません。

まとめ

Vueでサジェスト処理を作る場合は、v-if, v-showとその中の要素のClickイベントのタイミングの関係性を考慮する必要があることをお伝えしました。

今回紹介した内容のサンプルコード

https://github.com/it-web-life/vue-suggest-input-component

ご参考になれば幸いです。