【发布时间】:2018-09-13 21:38:36
【问题描述】:
我目前正在用 Javascript 编写一个自动完成组件,它根据邮政编码完成两个输入字段 plz(邮政编码)和 ort(城市)。输入 3 位数字后,自动完成功能会立即触发。
现在很遗憾,德国的许多邮政编码都不能用作标识符 - 几个小城市都可以使用相同的邮政编码。
要试用我的代码,请输入
562
在标记为 PLZ 的输入中。
将打开一个自动建议列表,其中最多可同时显示 10 个项目。使用 ↓ 向下移动列表。
当您使用 ↓ 或 ↑导航时,当前活动 项不会导致列表滚动,我需要一个解决方案>.
如果有人可以在这里为我指明正确的方向,我会非常乐意自己实现它。
let plz = [{"plz":"56244","ort":"Rückeroth","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Ötzingen","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Niedersayn","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Vielbach","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Hartenfels","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Ewighausen","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Leuterod","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Kuhnhöfen","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Goddert","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Freirachdorf","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Maxsain","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Freilingen","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Weidenhahn","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Helferskirchen","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Arnshöfen","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Hahn am See","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Sessenhausen","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Wölferlingen","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Steinen","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Schenkelberg","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Krümmel","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"},{"plz":"56244","ort":"Ettinghausen","bundesland":"Rheinland-Pfalz","kreis":"Westerwaldkreis"}]
let plzAutoCompleteConfig = {
minCharactersToRun: 3,
maxResults: 100,
allowedKeyCodes: [8, 9, 13, 37, 38, 39, 40, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105]
}
let plzOrtAutoComplete = function plzOrtAutoComplete() {
if (!document.querySelector('[data-has-plz-ort-autocomplete]')) return;
// find all plz autocompletes
let autocompletes = Array.from(document.querySelectorAll('[data-has-plz-ort-autocomplete]'))
for (let autocomplete of autocompletes) {
let plzInput = document.getElementById(autocomplete.getAttribute('data-plz'))
let ortInput = document.getElementById(autocomplete.getAttribute('data-ort'))
let suggestList = document.createElement('ul')
suggestList.flush = function() {
this.innerHTML = ''
}
suggestList.className = 'autocomplete-suggest list-unstyled'
plzInput.parentNode.appendChild(suggestList)
for (let eventName of ['input', 'focus']) {
plzInput.addEventListener(eventName, function(evt) {
const checkInput = () => {
let matches = plz.filter(x => {
return x.plz.startsWith(this.value)
})
switch (true) {
case (matches.length > plzAutoCompleteConfig.maxResults):
suggestList.flush()
break
case ((matches.length <= plzAutoCompleteConfig.maxResults && matches.length > 1) || (matches.length === 1 && this.value.length < 5)):
suggestList.flush()
for (let match of matches) {
let li = document.createElement('li')
li.textContent = `${match.plz} ${match.ort}`
li.title = `${match.plz} ${match.ort} in ${match.bundesland}, ${match.kreis} übernehmen durch Enter oder Klick`
li.addEventListener('click', () => {
plzInput.value = match.plz
ortInput.value = match.ort
ortInput.focus()
suggestList.flush()
})
li.addEventListener('mouseenter', function() {
this.classList.add('active')
})
li.addEventListener('mouseleave', function() {
this.classList.remove('active')
})
suggestList.appendChild(li)
}
this.parentNode.appendChild(suggestList)
break
case (matches.length === 1 && this.value.length === 5):
if (event.type !== 'focus' && ['deleteContentBackward', 'deleteContentForward'].indexOf(evt.inputType) === -1) {
suggestList.flush()
plzInput.value = matches[0].plz
ortInput.value = matches[0].ort
ortInput.focus()
}
break
default:
{
suggestList.flush()
break
}
}
}
if (isNaN(Number(this.value))) {
this.value = ''
return
}
if (this.value.length >= plzAutoCompleteConfig.minCharactersToRun) {
if (['deleteContentBackward', 'deleteContentForward'].indexOf(evt.inputType) > -1) {
console.log(this.value)
}
checkInput()
}
})
}
plzInput.addEventListener('keydown', function(evt) {
let keyCode = evt.keyCode || evt.which;
let activeLi = suggestList.querySelector('li.active')
if (keyCode) {
if (!plzAutoCompleteConfig.allowedKeyCodes.includes(keyCode)) {
evt.preventDefault()
} else {
switch (keyCode) {
case 8: // backspace
suggestList.flush()
break
case 13: // Enter
evt.preventDefault()
if (!suggestList.hasChildNodes()) {
return
}
if (!activeLi) {
return
} else {
plzInput.value = activeLi.textContent.substr(0, 5)
ortInput.value = activeLi.textContent.substr(6)
suggestList.flush()
ortInput.focus()
}
break
case 37:
break
case 38: // cursor up
if (!suggestList.hasChildNodes()) {
return
}
if (activeLi) {
activeLi.classList.remove('active')
let prevLi = activeLi.previousSibling
if (prevLi) {
prevLi.classList.add('active')
} else {
suggestList.querySelector('li:last-of-type').classList.add('active')
}
} else {
suggestList.querySelector('li:last-of-type').classList.add('active')
}
break
case 39:
break
case 40:
if (!suggestList.hasChildNodes()) {
return
}
if (activeLi) {
activeLi.classList.remove('active')
let nextLi = activeLi.nextSibling
if (nextLi) {
nextLi.classList.add('active')
} else {
suggestList.querySelector('li:first-of-type').classList.add('active')
}
} else {
suggestList.querySelector('li:first-of-type').classList.add('active')
}
break
case 46: // delete
suggestList.flush()
break
default:
break
}
}
}
})
plzInput.addEventListener('blur', function(evt) {
setTimeout(function() {
suggestList.flush()
}, 250)
})
ortInput.addEventListener('input', function(evt) {
console.log(this.value)
})
}
}
plzOrtAutoComplete();
.autocomplete-suggest {
background-color: #fff;
border: 1px solid #ddd;
box-shadow: 3px 3px 5px #ccc;
max-height: 6em;
left: 5px;
opacity: 1;
overflow-y: auto;
pointer-events: all;
position: absolute;
z-index: 999;
transition-duration: .2s;
}
.autocomplete-suggest:empty {
max-height: 0;
opacity: 0;
pointer-events: none;
}
.autocomplete-suggest li {
line-height: 1.5em;
margin: 0 auto;
padding: .3em 1.2em .2em .6em;
transition: all .2s ease-in-out;
white-space: nowrap;
}
.autocomplete-suggest li:nth-child(even) {
background-color: rgba(0, 0, 0, 0.05);
}
.autocomplete-suggest li:hover, .autocomplete-suggest li.active {
cursor: pointer;
background-color: #a00;
color: #fff;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<div class="form-row" data-has-plz-ort-autocomplete data-plz="eazVersicherterPLZ" data-ort="eazVersicherterOrt">
<div class="form-group col-sm-4">
<label for="eazVersicherterPLZ">PLZ</label>
<input class="form-control" type="text" id="eazVersicherterPLZ" name="eazVersicherterPLZ" data-plz="eazVersicherterOrt" maxlength=5 />
</div>
<div class="form-group col-sm-8">
<label for="eazVersicherterOrt">Ort</label>
<input class="form-control" type="text" id="eazVersicherterOrt" name="eazVersicherterOrt">
</div>
</div>
这是由 Javascript 生成的自动提示 HTML:
<ul class="autocomplete-suggest list-unstyled">
<li>56244 Rückeroth</li>
<li>56244 Ötzingen</li>
<li>56244 Niedersayn</li>
<li>56244 Vielbach</li>
<li>56244 Hartenfels</li>
<li>56244 Ewighausen</li>
<li>56244 Leuterod</li>
<li>56244 Kuhnhöfen</li>
<li>56244 Goddert</li>
<li>56244 Freirachdorf</li>
<li>56244 Maxsain</li>
<li>56244 Freilingen</li>
<li>56244 Weidenhahn</li>
<li>56244 Helferskirchen</li>
<li>56244 Arnshöfen</li>
<li>56244 Hahn am See</li>
<li>56244 Sessenhausen</li>
<li>56244 Wölferlingen</li>
<li>56244 Steinen</li>
<li>56244 Schenkelberg</li>
<li>56244 Krümmel</li>
<li>56244 Ettinghausen</li>
</ul>
【问题讨论】:
-
您可以检查这是否是最后一个可见元素,然后向上滚动一个位置。可能的解决方案小提琴链接jsfiddle.net/kMzR9/3
标签: javascript html css autocomplete typeahead