我有一个JavaScript应用程序,用户可以在其中搜索地图上的位置。输入文本提供了自动完整的建议:作为用户类型,一些建议出现在文本输入本身的底部。
我正在使用第三方JavaScript自动完成库,该库收取每个用户请求的费用。不幸的是,每次击键都将其视为一个单个请求,因为库从输入元素接收onInput
回调时不会实现任何辩论。因此,这些建议看起来很活泼,但要花很多用户要求。
我想做的是重新定义输入元素内的输入回调以实现bebounconcing(例如500ms(。
由于第三方库接受JavaScript元素本身,因此我不能使用外部辩论机制:(可能库检测到输入元素发送的onInput
消息本身(
var placesAutocomplete = places({
appId: 'xxxxxxxxxxx',
apiKey: 'kkkkkkkkkk',
container: document.querySelector('#inputelement'), // <- the library accepts the element itself, not an "on input" listener (which could be easily debounced by an external function)
language: G_lang
});
placesAutocomplete.on('change', function(res){
// user leaves the input text, set lat lon on my map (code not shown here on SO)
var lat = res.suggestion.latlng.lat;
var lon = res.suggestion.latlng.lng;
finish(lat, lon);
});
必须由JavaScript元素本身提供审问。因此,基本上,该元素应发射由辩论机构过滤的onInput
回调。
只能使用Vanilla JavaScript?
可以这样做吗?编辑
看来有人试图在GitHub项目页面上提出扣除辩论功能的请求,但被拒绝了:https://github.com/algolia/places/issues/281
和其他人分配了图书馆,并自行叉 -> https://github.com/acuityscheduling/places/tree/feature/debouncous
使用官方Codepen,我做了这个hackish bebouned版本:
var client = algoliasearch("latency", "6be0576ff61c053d5f9a3225e2a90f76")
var index = client.initIndex('movies');
var myAutocomplete = autocomplete('#search-input', {
hint: false
}, [{
source: autocomplete.sources.hits(index, {
hitsPerPage: 5
}),
displayKey: 'title',
templates: {
suggestion: function(suggestion) {
var sugTemplate = "<img src='" + suggestion.image + "'/><span>" + suggestion._highlightResult.title.value + "</span>"
return sugTemplate;
}
}
}]).on('autocomplete:selected', function(event, suggestion, dataset) {
console.log(suggestion, dataset);
});
document.querySelector(".searchbox [type='reset']").addEventListener("click", function() {
document.querySelector(".aa-input").focus();
this.classList.add("hide");
myAutocomplete.autocomplete.setVal("");
});
document.querySelector("#debouncer").addEventListener("input", function() {
var s = document.querySelector("#search-input");
s.value = this.value;
clearTimeout(this.tick);
this.tick = setTimeout(() => s.dispatchEvent(new KeyboardEvent('input')), 500);
});
document.querySelector("#search-input").addEventListener("input", function() {
var searchbox = document.querySelector(".aa-input");
var reset = document.querySelector(".searchbox [type='reset']");
if (searchbox.value.length === 0) {
reset.classList.add("hide");
} else {
reset.classList.remove('hide');
}
});
body {
padding: 60px;
}
.searchbox {
display: inline-block;
position: relative;
width: 200px;
height: 37px;
white-space: nowrap;
box-sizing: border-box;
font-size: 13px;
font-family: arial, sans-serif;
}
#debouncer {
position: absolute;
z-index: 1;
box-sizing:border-box;
padding: 0 30px 0 37px;
width: 100%;
height: 100%;
vertical-align: middle;
white-space: normal;
font-size: inherit;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: none;
border:0;
}
.algolia-autocomplete {
display: block;
height: 100%;
}
.searchbox__wrapper {
width: 100%;
height: 100%;
}
.searchbox__input {
display: inline-block;
-webkit-transition: box-shadow .4s ease, background .4s ease;
transition: box-shadow .4s ease, background .4s ease;
border: 0;
border-radius: 19px;
box-shadow: inset 0 0 0 1px #D9D9D9;
color:transparent;
background: #FFFFFF;
padding: 0;
padding-right: 30px;
padding-left: 37px;
width: 100%;
height: 100%;
vertical-align: middle;
white-space: normal;
font-size: inherit;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.searchbox__input::-webkit-search-decoration,
.searchbox__input::-webkit-search-cancel-button,
.searchbox__input::-webkit-search-results-button,
.searchbox__input::-webkit-search-results-decoration {
display: none;
}
#debouncer:hover ~ * .searchbox__input {
box-shadow: inset 0 0 0 1px silver;
}
#debouncer:focus ~ * .searchbox__input,
#debouncer:active ~ * .searchbox__input {
outline: 0;
box-shadow: inset 0 0 0 1px #4098CE;
background: #FFFFFF;
}
#debouncer::-webkit-input-placeholder {
color: #AAAAAA;
}
#debouncer::-moz-placeholder {
color: #AAAAAA;
}
#debouncer:-ms-input-placeholder {
color: #AAAAAA;
}
#debouncer::placeholder {
color: #AAAAAA;
}
.searchbox__submit {
position: absolute; z-index:2;
top: 0;
right: inherit;
left: 0;
margin: 0;
border: 0;
border-radius: 18px 0 0 18px;
background-color: rgba(255, 255, 255, 0);
padding: 0;
width: 37px;
height: 100%;
vertical-align: middle;
text-align: center;
font-size: inherit;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.searchbox__submit::before {
display: inline-block;
margin-right: -4px;
height: 100%;
vertical-align: middle;
content: '';
}
.searchbox__submit:hover,
.searchbox__submit:active {
cursor: pointer;
}
.searchbox__submit:focus {
outline: 0;
}
.searchbox__submit svg {
width: 17px;
height: 17px;
vertical-align: middle;
fill: #666666;
}
.searchbox__reset {
position: absolute; z-index:2;
top: 8px;
right: 8px;
margin: 0;
border: 0;
background: none;
cursor: pointer;
padding: 0;
font-size: inherit;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
fill: rgba(0, 0, 0, 0.5);
}
.searchbox__reset.hide {
display: none;
}
.searchbox__reset:focus {
outline: 0;
}
.searchbox__reset svg {
display: block;
margin: 4px;
width: 13px;
height: 13px;
}
.searchbox__input:valid~.searchbox__reset {
display: block;
-webkit-animation-name: sbx-reset-in;
animation-name: sbx-reset-in;
-webkit-animation-duration: .15s;
animation-duration: .15s;
}
@-webkit-keyframes sbx-reset-in {
0% {
-webkit-transform: translate3d(-20%, 0, 0);
transform: translate3d(-20%, 0, 0);
opacity: 0;
}
100% {
-webkit-transform: none;
transform: none;
opacity: 1;
}
}
@keyframes sbx-reset-in {
0% {
-webkit-transform: translate3d(-20%, 0, 0);
transform: translate3d(-20%, 0, 0);
opacity: 0;
}
100% {
-webkit-transform: none;
transform: none;
opacity: 1;
}
}
.aa-dropdown-menu {
position: relative;
top: -6px;
border-radius: 3px;
margin: 6px 0 0;
padding: 0;
text-align: left;
height: auto;
position: relative;
background: transparent;
border: none;
width: 100%;
left: 0 !important;
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2), 0 2px 3px 0 rgba(0, 0, 0, 0.1);
}
.aa-dropdown-menu:before {
position: absolute;
content: '';
width: 14px;
height: 14px;
background: #fff;
z-index: 0;
top: -7px;
border-top: 1px solid #D9D9D9;
border-right: 1px solid #D9D9D9;
transform: rotate(-45deg);
border-radius: 2px;
z-index: 999;
display: block;
left: 24px;
}
.aa-dropdown-menu .aa-suggestions {
position: relative;
z-index: 1000;
}
.aa-dropdown-menu [class^="aa-dataset-"] {
position: relative;
border: solid 1px #D9D9D9;
border-radius: 3px;
overflow: auto;
padding: 8px 8px 8px;
}
.aa-dropdown-menu * {
box-sizing: border-box;
}
.aa-suggestion {
font-size: 1.1em;
padding: 4px 4px 0;
display: block;
width: 100%;
height: 38px;
clear: both;
}
.aa-suggestion span {
white-space: nowrap!important;
text-overflow: ellipsis;
overflow: hidden;
display: block;
float: left;
line-height: 2em;
width: calc(100% - 30px);
}
.aa-suggestion.aa-cursor {
background: #eee;
}
.aa-suggestion em {
color: #4098CE;
}
.aa-suggestion img {
float: left;
vertical-align: middle;
height: 30px;
width: 20px;
margin-right: 6px;
}
<script src="//cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js"></script>
<script src="//cdn.jsdelivr.net/autocomplete.js/0/autocomplete.min.js"></script>
<form novalidate="novalidate" onsubmit="return false;" class="searchbox">
<div role="search" class="searchbox__wrapper">
<input id="debouncer" type="text" name="search" autocomplete="off" placeholder="Search for a movie">
<input id="search-input" type="search" name="autocomplete" autocomplete="off" required="required" class="searchbox__input">
<button type="submit" title="Submit your search query." class="searchbox__submit">
<svg role="img" aria-label="Search">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-search-13"></use>
</svg>
</button>
<button type="reset" title="Clear the search query." class="searchbox__reset hide">
<svg role="img" aria-label="Reset">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-clear-3"></use>
</svg>
</button>
</div>
</form>
<div class="svg-icons" style="height: 0; width: 0; position: absolute; visibility: hidden">
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="sbx-icon-clear-3" viewBox="0 0 40 40"><path d="M16.228 20L1.886 5.657 0 3.772 3.772 0l1.885 1.886L20 16.228 34.343 1.886 36.228 0 40 3.772l-1.886 1.885L23.772 20l14.342 14.343L40 36.228 36.228 40l-1.885-1.886L20 23.772 5.657 38.114 3.772 40 0 36.228l1.886-1.885L16.228 20z" fill-rule="evenodd"/></symbol>
<symbol id="sbx-icon-search-13" viewBox="0 0 40 40"><path d="M26.806 29.012a16.312 16.312 0 0 1-10.427 3.746C7.332 32.758 0 25.425 0 16.378 0 7.334 7.333 0 16.38 0c9.045 0 16.378 7.333 16.378 16.38 0 3.96-1.406 7.593-3.746 10.426L39.547 37.34c.607.608.61 1.59-.004 2.203a1.56 1.56 0 0 1-2.202.004L26.807 29.012zm-10.427.627c7.322 0 13.26-5.938 13.26-13.26 0-7.324-5.938-13.26-13.26-13.26-7.324 0-13.26 5.936-13.26 13.26 0 7.322 5.936 13.26 13.26 13.26z" fill-rule="evenodd"/></symbol>
</svg>
</div>
这种方法几乎不比这种自动完成的实现本身好。希望它能有所帮助。
我认为我设法找到了一个很好的解决方案。我分配了库并启用了访问功能(已经存在,仅针对自动完成表单禁用(。
最终结果:使用少量自动完成滞后200ms,使用80%,完全可以接受