Selecting an option with a mouse click in VueJS autocomplete

Some time back, I developed an autocomplete feature in Vue for a project I'm working on.

However, today I noticed a small bug.

When I click on the desired option with my mouse, the selection does not get transmitted as expected. This issue is evident in the console.log() example where the previously selected option is displayed if I click on a different one subsequently.

I tried using setTimeout(() => {}, 200) and it seemed to resolve the detection and emission of the option, but I believe this may not be the optimal solution for this scenario.

Any suggestions?

Example

...

Answer №1

When using the onblur event, it may not work as expected because it fires when you click outside and before the onclick's item listener, resulting in the value not being updated.

To ensure that data is captured when a user types anything in the input field, use the onchange event and call the onChange() method inside setResult().

const Autocomplete = {
  name: "autocomplete",
  template: "#autocomplete",
  props: {
    items: {
      type: Array,
      required: false,
      default: () => Array(150).fill().map((_, i) => `Fruit ${i+1}`)
    },
    isAsync: {
      type: Boolean,
      required: false,
      default: false
    }
  },

  data() {
    return {
      isOpen: false,
      results: [],
      search: "",
      isLoading: false,
      arrowCounter: 0
    };
  },

  methods: {
    onChange() {
      console.log( this.search)
      // Let's warn the parent that a change was made
      this.$emit("input", this.search);
    },
    setResult(result, i) {
      this.arrowCounter = i;
      this.search = result;
      this.isOpen = false;
      // Fire onChange, because it won't do it on blur
      this.onChange();
    },
    showAll() {
      this.isOpen = !this.isOpen;
(this.isOpen) ? this.results = this.items : this.results = [];
    },
  },
  computed: {
      filterResults() {
      // first uncapitalize all the things
      this.results = this.items.filter(item => {
        return item.toLowerCase().indexOf(this.search.toLowerCase()) > -1;
      });
      
     
      return this.results;
    },
  },
  watch: {
    items: function(val, oldValue) {
      // actually compare them
      if (val.length !== oldValue.length) {
        this.results = val;
        this.isLoading = false;
      }
    }
  },
  mounted() {
    document.addEventListener("click", this.handleClickOutside);
  },
  destroyed() {
    document.removeEventListener("click", this.handleClickOutside);
  }
};

new Vue({
  el: "#app",
  name: "app",
  components: {
    autocomplete: Autocomplete
  }
});
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}

.autocomplete {
  position: relative;
  width: 130px;
}

.autocomplete-results {
  padding: 0;
  margin: 0;
  border: 1px solid #eeeeee;
  height: 120px;
  overflow: auto;
  width: 100%;
}

.autocomplete-result {
  list-style: none;
  text-align: left;
  padding: 4px 2px;
  cursor: pointer;
}

.autocomplete-result.is-active,
.autocomplete-result:hover {
  background-color: #4aae9b;
  color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
  <autocomplete />

</div>

<script type="text/x-template" id="autocomplete">
  <div class="autocomplete">
    <input type="text" @change="onChange" v-model="search"  @click="showAll" />
    <ul id="autocomplete-results" v-show="isOpen" ref="scrollContainer" class="autocomplete-results">
      <li class="loading" v-if="isLoading">
        Loading results...
      </li>
      <li ref="options" v-else v-for="(result, i) in filterResults" :key="i" @click="setResult(result, i)" class="autocomplete-result" :class="{ 'is-active': i === arrowCounter }">
        {{ result }}
      </li>
    </ul>

  </div>
</script>

Answer №2

focus would be more suitable in this scenario, and I believe you are overengineering the solution. Simply trigger your emit function within the setResult method:

setResult(result, i) {
      this.arrowCounter = i;
      this.search = result;
      this.isOpen = false;     
      this.$emit("input", this.search);
    },

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I'm having trouble finding the solution to setting a token in my request header

I have been following a tutorial in a book to build the authentication for my app. However, I am facing an issue where after logging in correctly, I am unable to set the token back into the request. The error message that I receive is: Failed to execute ...

Combining numerical values within an array using JavaScript

Below are different buttons ranging from 1 to 9 that I have: <button type="button" onclick="calculatorNumber(1)"> When clicked, the following function is triggered: function calculatorNumber(i) { myNumbers.push(i); var x = document.getElem ...

Error encountered: Unable to locate module 'net' while attempting to import Twilio in a Next.js application

Issue with Twilio Import in Next.js I recently started learning Next.js and encountered a problem when trying to import Twilio. The console displayed errors stating "fs not found, net not found". ./node_modules/https-proxy-agent/dist/agent.js:15:0 Module ...

Obtaining UTC dates with JavaScript: A guide

Can someone help me figure out how to retrieve the current UTC date using Javascript? I see there are methods available for getting the time in UTC, such as: date.getUTCHours(); But how can I specifically obtain the date? ...

Warning message in React about missing floating prop in components

application.js (Webpack Entry Point) import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.jsx'; document.addEventListener('DOMContentLoaded', () => { ReactDOM.render(<App /> ...

The formValidation, previously known as BootstrapValidator, is causing issues with my form submission via Ajax, despite my efforts to update the code to work with

I recently upgraded the old BootstrapValidator to the new 0.6.0 release known as formValidation. Despite reading the documentation multiple times, I have been unsuccessful in finding the issue and need some assistance. Below are the CSS styles and scripts ...

The computed function functions smoothly in one component but encounters issues in a different one

After adding multiple components to a page, I noticed that updates made to a parent object are reflected in one component but not the other. The main page, PatientEditor.vue, includes the following components: <notes-editor v-model="pt" /> <char ...

Issue with AngularJS UI Router where nested partial views are not being displayed

I am currently working on an AngularJs app with index.html as the start-up page. By default, the projects view is displayed, and at the top of the page, there is an icon to show todo items for logged-in users using Bootstrap's data-toggle dropdown. Ho ...

Utilizing the foreach loop to pinpoint the accurate index within an array

I've been struggling with this issue for a while now. I am using forEach to iterate over an array, and my goal is to render the correct page with the corresponding index when clicking on the component. Currently, my problem is that even though I loop ...

Utilizing AngularJS to implement a Currency Filter on the output of a personalized filter

I'm currently working on a custom filter that transforms zero values into ' - ' for display purposes. The goal is to display currency formatting for non-zero values. However, I encountered an unexpected token error while trying to implement ...

Node.js is being utilized to send an abundance of requests to the server

Here is my setup: var tempServer=require("./myHttp"); tempServer.startServer(8008,{'/fun1':fun1 , '/fun2': fun2 , '/fun3':fun3 , '/fun4':fun4},"www"); This code creates a server on localhost:8008. If I enter th ...

Utilizing a live input from an HTML form in App-Script

I am completely new to the world of HTML and App-Script. My goal is to create a dynamic list of options from a Google Sheet that will populate a listbox in my HTML code. When a user selects an option from the listbox and clicks submit, I want that value to ...

The <nuxt-img/> component does not seem to function properly when using v-bind:src to display images retrieved from

The documentation states that using <nuxt-img/> is similar to the HTML's <img> tag, but in my case, it's not functioning as expected. To illustrate this issue, I've created an example where the <img> tag works fine, while ...

The shared service is experiencing difficulties in transferring data to the following component

I have developed two components along with a shared service. My goal is to transfer data from one component to another, but I am encountering an issue where the object appears empty. Below is the code for the first component: import { Component, OnInit } ...

Inject some content into the page with the power of jQuery

Currently working with jQuery, I have a div containing H2 tags. <div id="preload"><h2></h2></div> I'm looking to understand how to use jQuery to insert text inside the <h2> tags The expected outcome should be: <div ...

Utilize Devextreme's dxDataGrid component to transmit the selected RowData to a function upon clicking a button within that particular row

I am utilizing the dxDataGrid user interface widget from the Devextreme product. My goal is to transform one of its columns into a clickable button. Here is my progress so far: Field Configuration { dataField: 'LetterNumber', caption: ' ...

Send an Ajax request when an option is selected from a dropdown

I've been attempting to implement an AJAX call using axios in order to change user roles without the need for a button click. However, I can't seem to get it functioning correctly. Here's my dropdown menu: <select> <option v-on:cl ...

When attempting to decrypt with a password using CryptoJS, AES decryption returns an empty result

Example The code snippet below is what I am currently using: <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script> <div id="decrypted">Please wait...</div> Insert new note:<input type="te ...

Unable to find component: "wrestler-choice-box". If this is a built-in custom element, please ensure it is not included in component resolution

In my attempt to achieve a specific goal, I have an array of data that I want to incorporate into my HTML document along with a Vue component containing a template. My intention is to utilize list rendering so that the names and images from this array bind ...

Exploring the world of Laravel Vue with Firebase Push notifications

I'm currently working on setting up push notifications for my Laravel/Vue application. After installing the firebase package in my node_modules, which includes firebase/messaging, I've imported the necessary files like this: var firebase = requi ...