title: Scroll with arrow keys on dropdown - vuejs
body: <p>I'm building an autocomplete component in vuejs.</p>
  
  <p>And at the moment I got stuck in the scroll animation.</p>
  
  <p>The purpose is to click on the arrow keys to scroll as the direction of the key, but the scroll is only executed when the option is not visible.</p>
  
  <p>I wanted something of this kind, but in vue / javascript. - <a href="http://jsfiddle.net/kMzR9/3/" rel="noreferrer">http://jsfiddle.net/kMzR9/3/</a></p>
  
  <p>If you can not see the problem in the example I left here since the screen is small, here is the jsfiddle - <a href="https://jsfiddle.net/v7yd94r5/" rel="noreferrer">https://jsfiddle.net/v7yd94r5/</a></p>
  
  <p>Here is an example of what I have.</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>const Autocomplete = {
    name: "autocomplete",
    template: "#autocomplete",
    props: {
      items: {
        type: Array,
        required: false,
        default: () =&gt; []
      },
      isAsync: {
        type: Boolean,
        required: false,
        default: false
      }
    },
  
    data() {
      return {
        isOpen: false,
        results: [],
        search: "",
        isLoading: false,
        arrowCounter: 0
      };
    },
  
    methods: {
      onChange() {
        // Let's warn the parent that a change was made
        this.$emit("input", this.search);
  
        // Is the data given by an outside ajax request?
        if (this.isAsync) {
          this.isLoading = true;
        } else {
          // Let's search our flat array
          this.filterResults();
          this.isOpen = true;
        }
      },
  
      filterResults() {
        // first uncapitalize all the things
        this.results = this.items.filter(item =&gt; {
          return item.toLowerCase().indexOf(this.search.toLowerCase()) &gt; -1;
        });
      },
      setResult(result) {
        this.search = result;
        this.isOpen = false;
      },
      onArrowDown(evt) {
        if (this.arrowCounter &lt; this.results.length) {
          this.arrowCounter = this.arrowCounter + 1;
        }
      },
      onArrowUp() {
        if (this.arrowCounter &gt; 0) {
          this.arrowCounter = this.arrowCounter - 1;
        }
      },
      onEnter() {
        this.search = this.results[this.arrowCounter];
        this.isOpen = false;
        this.arrowCounter = -1;
      },
      showAll() {
        this.isOpen = !this.isOpen;
     (this.isOpen) ? this.results = this.items : this.results = [];
      },
      handleClickOutside(evt) {
        if (!this.$el.contains(evt.target)) {
          this.isOpen = false;
          this.arrowCounter = -1;
        }
      }
    },
    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
    }
  });</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>#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;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"&gt;&lt;/script&gt;
  &lt;div id="app"&gt;
    &lt;autocomplete :items="[ 'Apple', 'Banana', 'Orange', 'Mango', 'Pear', 'Peach', 'Grape', 'Tangerine', 'Pineapple']" /&gt;
  
  &lt;/div&gt;
  
  &lt;script type="text/x-template" id="autocomplete"&gt;
    &lt;div class="autocomplete"&gt;
      &lt;input type="text" @input="onChange" v-model="search" @keyup.down="onArrowDown" @keyup.up="onArrowUp" @keyup.enter="onEnter" @click="showAll" /&gt;
      &lt;ul id="autocomplete-results" v-show="isOpen" class="autocomplete-results"&gt;
        &lt;li class="loading" v-if="isLoading"&gt;
          Loading results...
        &lt;/li&gt;
        &lt;li v-else v-for="(result, i) in results" :key="i" @click="setResult(result)" class="autocomplete-result" :class="{ 'is-active': i === arrowCounter }"&gt;
          {{ result }}
        &lt;/li&gt;
      &lt;/ul&gt;
  
    &lt;/div&gt;
  &lt;/script&gt;</code></pre>
  </div>
  </div>
  </p>