Auto suggest uses extra spaces in the html, because of the reason the next elements like H1, H2 and paragraph are going down when I start typing in the suggestion box
App.vue
<template>
<div id="app">
<Autocomplete
:items="[
'Apple',
'Banana',
'Orange',
'Mango',
'Pear',
'Peach',
'Grape',
'Tangerine',
'Pineapple',
]"
/>
<p>p1</p>
<h1>h1</h1>
<h1>h2</h1>
</div>
</template>
<script>
import Autocomplete from "./components/AutoComplete";
import AutoCompleteSearch from "./directives/auto-complete-search.js";
import axios from "axios";
export default {
name: "App",
directives: {
AutoCompleteSearch,
},
components: {
Autocomplete,
},
data() {
return {
data: [],
};
},
methods: {
async getUser() {
await axios
.get("https://jsonplaceholder.typicode.com/users")
.then((res) => (this.data = res.data))
.catch((err) => console.log(err));
},
},
mounted() {
this.data = [{ name: "Elie" }, { name: "John" }];
},
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
AutocompletSearch.js -- directive
import Vue from "vue";
/**
* Add the list attribute to the bound input field.
*
* @param element
* @param binding
* @returns {HTMLElement}
*/
const addAttribute = (element, binding) => {
const { id } = binding.value;
element.setAttribute("list", id);
return element;
};
/**
* The search data for auto-completions
*
* @param binding
* @returns {Object|Array}
*/
const searchData = (binding) => {
const { data } = binding.value;
return data;
};
/**
* Construct the datalist HTMLElement
*
* @param element
* @param binding
* @returns {HTMLDataListElement}
*/
const constructDataList = (element, binding) => {
const datalist = document.createElement("datalist");
datalist.id = binding.value.id;
const data = searchData(binding);
data.forEach((item) => {
const { autoCompleteValue } = binding.value;
const option = document.createElement("option");
option.value = item[autoCompleteValue];
datalist.appendChild(option);
});
return datalist;
};
/**
* @param el the element where to bind the directive
* @param binding {id & autoCompleteValue & data}
*/
const init = (el, binding) => {
const element = addAttribute(el, binding);
const wrapper = element.parentNode;
const datalist = constructDataList(element, binding);
wrapper.appendChild(datalist);
};
export default Vue.directive("auto-complete-search", {
update(el, binding, vnode) {
const vm = vnode.context;
const bindingData = binding.value;
const componentName = vm.$options.name;
const customCallbackName = bindingData.callbackName;
console.log("component updated");
// call the function to fetch auto complete dat
vm.$options.methods.getUser();
if (!vm.$options.methods.getUser && customCallbackName === undefined)
return console.error(`the getUser() is neither defined or overriden in ${componentName} component, on
the auto-complete-search directive`);
if (binding.value.apply) {
init(el, binding);
}
if (!binding.value.apply) {
el.removeAttribute("list");
}
}
});
AutoComplete.vue
<template>
<div >
<input
type="text"
@input="onChange"
v-model="search"
@keydown.down="onArrowDown"
@keydown.up="onArrowUp"
@keydown.enter="onEnter"
/>
<ul id="autocomplete-results" v-show="isOpen" >
<li v-if="isLoading">Loading results...</li>
<li
v-else
v-for="(result, i) in results"
:key="i"
@click="setResult(result)"
:
>
{{ result }}
</li>
</ul>
</div>
</template>
<script>
export default {
name: "AutoComplete",
props: {
items: {
type: Array,
required: false,
default: () => [],
},
isAsync: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
isOpen: false,
results: [],
search: "",
isLoading: false,
arrowCounter: -1,
};
},
watch: {
items: function (value, oldValue) {
if (value.length !== oldValue.length) {
this.results = value;
this.isLoading = false;
}
},
},
mounted() {
document.addEventListener("click", this.handleClickOutside);
},
destroyed() {
document.removeEventListener("click", this.handleClickOutside);
},
methods: {
setResult(result) {
this.search = result;
this.isOpen = false;
},
filterResults() {
this.results = this.items.filter((item) => {
return item.toLowerCase().indexOf(this.search.toLowerCase()) > -1;
});
},
onChange() {
this.$emit("input", this.search);
if (this.isAsync) {
this.isLoading = true;
} else {
this.filterResults();
this.isOpen = true;
}
},
handleClickOutside(event) {
if (!this.$el.contains(event.target)) {
this.isOpen = false;
this.arrowCounter = -1;
}
},
onArrowDown() {
if (this.arrowCounter < this.results.length) {
this.arrowCounter = this.arrowCounter 1;
}
},
onArrowUp() {
if (this.arrowCounter > 0) {
this.arrowCounter = this.arrowCounter - 1;
}
},
onEnter() {
this.search = this.results[this.arrowCounter];
this.isOpen = false;
this.arrowCounter = -1;
},
},
};
</script>
<style>
.autocomplete {
position: relative;
}
.autocomplete-results {
padding: 0;
margin: 0;
border: 1px solid #eeeeee;
height: 120px;
overflow: auto;
}
.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;
}
</style>
Please help me out this, auto suggestion can show like popup-modal instead of using extra spaces in the html. like
Here is my code link - https://codesandbox.io/s/vuejs-autocomplete-input-forked-fh31tr
CodePudding user response:
After checking out your codesandbox just add these 3 css properties to your autocomplete
class:
position: absolute;
width: 100%;
background: white;