
本文介绍如何在用户输入国家名称前缀时,动态高亮首个匹配项并平滑滚动至该选项,提升下拉选择体验。核心是结合正则匹配、CSS 类控制与 scrollIntoView() 实现精准定位。
本文介绍如何在用户输入国家名称前缀时,动态高亮首个匹配项并平滑滚动至该选项,提升下拉选择体验。核心是结合正则匹配、css 类控制与 `scrollintoview()` 实现精准定位。
在构建带搜索功能的下拉菜单(如国家选择器)时,仅高亮匹配项还不够——用户仍需手动滚动查找,尤其当列表较长时体验较差。理想的交互应是:用户每输入一个字符,菜单自动滚动到第一个匹配项,并保持其可见。下面提供一套轻量、可靠、无依赖的原生 JavaScript 解决方案。
✅ 核心实现逻辑
-
监听输入事件:使用
onkeyup触发filtrar()函数(注意:更健壮的做法是用addEventListener('input'),避免遗漏粘贴等操作); -
正则前缀匹配:构造不区分大小写的
^${input}正则,确保只匹配以输入内容开头的国家(如输au→ 匹配Australia,不匹配United Kingdom); -
单次高亮 + 滚动:遍历
.dropdown-item,对首个匹配项添加selected-country类,并立即调用item.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); -
清除冗余监听:原代码中每次
filtrar()都为每个 item 重复绑定click事件,易导致多次触发。应移出循环,一次性初始化点击逻辑。
✅ 优化后的完整代码
<!-- HTML 结构(精简版) --> <input type="text" class="form-control bg-white dropdown-toggle" id="country" data-bs-toggle="dropdown" aria-expanded="false" placeholder="Select a country" oninput="filtrar()" <!-- 推荐改用 oninput,支持粘贴、删除等 --> /> <ul class="dropdown-menu" id="country_list" style="max-height: 300px; overflow-y: auto; cursor: pointer;" aria-labelledby="country"> <li><a class="dropdown-item" data-code="AT">Austria</a></li> <li><a class="dropdown-item" data-code="AU">Australia</a></li> <li><a class="dropdown-item" data-code="AD">Andorra</a></li> <li><a class="dropdown-item" data-code="BE">Belgium</a></li> <li><a class="dropdown-item" data-code="BG">Bulgaria</a></li> <!-- 更多选项... --> </ul>
/* CSS 样式 */
.selected-country {
background-color: #007bff !important;
color: #fff !important;
font-weight: 500;
}// JavaScript 逻辑
const countries = document.querySelectorAll('.dropdown-item');
const countryField = document.getElementById('country');
const countryList = document.getElementById('country_list');
// ✅ 一次性绑定点击事件(避免重复监听)
countries.forEach(item => {
item.addEventListener('click', function (e) {
e.preventDefault();
countryField.value = this.textContent.trim();
// 可选:关闭下拉菜单(Bootstrap 5+)
const bsDropdown = bootstrap.Dropdown.getInstance(countryField);
if (bsDropdown) bsDropdown.hide();
});
});
function filtrar() {
const input = countryField.value.trim().toLowerCase();
const regex = new RegExp(`^${input}`, 'i');
let found = false;
countries.forEach(item => {
const itemText = item.textContent.toLowerCase();
if (input.length === 0 || found) {
item.classList.remove('selected-country');
return;
}
if (regex.test(itemText)) {
item.classList.add('selected-country');
// ✅ 平滑滚动到首个匹配项(仅在首次匹配时执行)
item.scrollIntoView({
behavior: 'smooth',
block: 'nearest', // 避免过度跳动,优先显示在视口中部或边缘
inline: 'nearest'
});
found = true;
} else {
item.classList.remove('selected-country');
}
});
}
// ✅ 初始化:清空时也触发一次,确保状态一致
countryField.addEventListener('blur', () => {
if (!countryField.value.trim()) filtrar();
});⚠️ 注意事项与最佳实践
-
性能提示:若国家列表超 200 项,建议配合虚拟滚动或防抖(
debounce),避免高频scrollIntoView影响渲染; -
Accessibility:
scrollIntoView在无障碍阅读器中可能不触发通知,可补充aria-live="polite"区域提示“已定位到匹配项”; -
Bootstrap 兼容性:确保使用 Bootstrap 5+(
data-bs-toggle);若用 v4,请改用data-toggle并引入 Popper.js; -
样式冲突:
.selected-country的!important仅用于覆盖 Bootstrap 默认背景,生产环境建议通过更高优先级 CSS 选择器替代; -
空输入处理:
input.length === 0分支确保清空输入时清除所有高亮,避免残留状态。
通过以上实现,用户在输入 au 时,菜单将自动滚动至 Australia 并高亮,点击即可填充输入框——简洁、直观、符合直觉。

















