Skip to content

Instantly share code, notes, and snippets.

@TahaSh
Created December 9, 2016 12:45
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save TahaSh/04fae389aaa0250bc04a37da6d691385 to your computer and use it in GitHub Desktop.
Save TahaSh/04fae389aaa0250bc04a37da6d691385 to your computer and use it in GitHub Desktop.
Reusable Autocomplete-Input Component in Vue 2.1
<title>Vue Awesome Autocomplete</title>
<link rel="stylesheet" href="https://unpkg.com/bulma/css/bulma.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="style.css">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<autocomplete-input
:options="options"
@select="onOptionSelect"
>
<template slot="item" scope="option">
<article class="media">
<figure class="media-left">
<p class="image is-64x64">
<img :src="option.thumbnail">
</p>
</figure>
<p>
<strong>{{ option.title }}</strong>
<br>
{{ option.description }}
</p>
</article>
</template>
</autocomplete-input>
</div>
<script id="autocomplete-input-template" type="text/x-template">
<div class="autocomplete-input">
<p class="control has-icon has-icon-right">
<input
v-model="keyword"
class="input is-large"
placeholder="Search..."
@input="onInput($event.target.value)"
@keyup.esc="isOpen = false"
@blur="isOpen = false"
@keydown.down="moveDown"
@keydown.up="moveUp"
@keydown.enter="select"
>
<i class="fa fa-angle-down"></i>
</p>
<ul v-show="isOpen"
class="options-list"
>
<li v-for="(option, index) in fOptions"
:class="{
'highlighted': index === highlightedPosition
}"
@mouseenter="highlightedPosition = index"
@mousedown="select"
>
<slot name="item"
:title="option.title"
:description="option.description"
:thumbnail="option.thumbnail"
>
</li>
</ul>
</div>
</script>
<script>
Vue.component('autocomplete-input', {
template: '#autocomplete-input-template',
props: {
options: {
type: Array,
required: true
}
},
data () {
return {
isOpen: false,
highlightedPosition: 0,
keyword: ''
}
},
computed: {
fOptions () {
const re = new RegExp(this.keyword, 'i')
return this.options.filter(o => o.title.match(re))
}
},
methods: {
onInput (value) {
this.highlightedPosition = 0
this.isOpen = !!value
},
moveDown () {
if (!this.isOpen) {
return
}
this.highlightedPosition =
(this.highlightedPosition + 1) % this.fOptions.length
},
moveUp () {
if (!this.isOpen) {
return
}
this.highlightedPosition = this.highlightedPosition - 1 < 0
? this.fOptions.length - 1
: this.highlightedPosition - 1
},
select () {
const selectedOption = this.fOptions[this.highlightedPosition]
this.$emit('select', selectedOption)
this.isOpen = false
this.keyword = selectedOption.title
}
}
})
new Vue({
el: '#app',
data: {
options: [
{
title: 'First Scene',
description: 'lorem ipsum dolor amet.',
thumbnail: 'http://lorempicsum.com/nemo/200/200/1'
},
{
title: 'Second Scene',
description: 'lorem ipsum dolor amet.',
thumbnail: 'http://lorempicsum.com/nemo/200/200/2'
},
{
title: 'Third Scene',
description: 'lorem ipsum dolor amet.',
thumbnail: 'http://lorempicsum.com/nemo/200/200/3'
},
{
title: 'Fourth Scene',
description: 'lorem ipsum dolor amet.',
thumbnail: 'http://lorempicsum.com/nemo/200/200/4'
}
]
},
methods: {
onOptionSelect (option) {
console.log('Selected option:', option)
}
}
})
</script>
html, body {
height: 100%;
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
display: flex;
justify-content: center;
padding-top: 100px;
height: 100%;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
input {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
}
.autocomplete-input {
position: relative;
height: 300px;
}
ul.options-list {
display: flex;
flex-direction: column;
margin-top: -12px;
border: 1px solid #dbdbdb;
border-radius: 0 0 3px 3px;
position: absolute;
width: 100%;
overflow: hidden;
}
ul.options-list li {
width: 100%;
flex-wrap: wrap;
background: white;
margin: 0;
border-bottom: 1px solid #eee;
color: #363636;
padding: 7px;
cursor: pointer;
}
ul.options-list li.highlighted {
background: #f8f8f8
}
@erhansaydam
Copy link

Hi Taha, thank you for your work.

I have faced with is a scrolling problem on mouse movement. Let's change the css like this:

.autocomplete-input {
position: relative;
height: 200px;
overflow-y: auto;
overflow-x: hidden;
}

Then, search for word "scene". The results causes to overflowing. When you move down by keyboard, the block does not scroll down. How can it be solved?

Best regards...

@acrolink
Copy link

acrolink commented Mar 1, 2018

Thank you very much for sharing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment