自动完成组件是前端交互里非常实用的功能,用户在输入框中输入部分字符,组件实时给出可能的匹配项供选择。使用原生JavaScript实现这样一个组件,核心在于输入监听、数据筛选、列表渲染以及键盘交互几个部分。下面我们一步步拆解,并用代码构建一个可运行的简单版本。

一、基础HTML结构
首先需要一个文本输入框和一个用于展示候选结果的容器。候选容器默认隐藏,当匹配到数据时再显示。结构尽量简单,方便后续用JS控制。
我们将输入框和列表放在一个相对定位的包裹层中,列表使用绝对定位覆盖在输入框下方。这样可以避免影响其他页面布局。
<div class="autocomplete-wrapper" style="position:relative;width:300px;"> <input type="text" id="ac-input" placeholder="输入关键字" /> <ul id="ac-list" style="display:none;position:absolute;left:0;top:100%;width:100%;border:1px solid #ccc;list-style:none;padding:0;margin:0;background:#fff;"></ul> </div>
二、输入事件与防抖处理
如果每次键盘弹起都立即筛选并渲染,会造成大量无用计算。尤其当候选数据来自远程接口时,频繁请求会让服务端压力剧增。因此必须引入防抖机制,延迟执行逻辑。
防抖的原理是:在事件触发后等待一段时间,若期间没有再次触发,才真正执行函数。下面我们写一个通用的防抖函数,并绑定到输入框的input事件上。
function debounce(fn, delay) {
let timer = null;
return function() {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(context, args);
}, delay);
};
}
const inputEl = document.getElementById('ac-input');
const listEl = document.getElementById('ac-list');
inputEl.addEventListener('input', debounce(function(e) {
const keyword = e.target.value.trim();
if (!keyword) {
listEl.style.display = 'none';
return;
}
const result = filterData(keyword);
renderList(result);
}, 300));
三、数据筛选与渲染
筛选逻辑取决于你的数据源。本地数组可以用字符串的includes方法做模糊匹配;若是远程数据,则把关键字传给接口即可。这里以本地数组为例。
渲染时动态创建li元素,并给每个项绑定点击事件,选中后把值填入输入框并隐藏列表。同时要注意特殊字符的转义,避免输入内容破坏HTML结构。
const dataSource = ['apple', 'banana', 'orange', 'peach', 'pineapple', 'grape'];
function filterData(keyword) {
return dataSource.filter(function(item) {
return item.includes(keyword);
});
}
function renderList(data) {
if (!data.length) {
listEl.style.display = 'none';
return;
}
listEl.innerHTML = '';
data.forEach(function(item) {
const li = document.createElement('li');
li.textContent = item;
li.style.padding = '8px';
li.style.cursor = 'pointer';
li.addEventListener('click', function() {
inputEl.value = item;
listEl.style.display = 'none';
});
listEl.appendChild(li);
});
listEl.style.display = 'block';
}
四、键盘导航与回车选择
只支持鼠标点击还不够友好,用户习惯用上下键移动高亮项,回车确认。我们需要维护一个当前高亮索引,并在keydown事件中更新样式。
注意在列表中阻止默认回车行为以免表单意外提交,同时在输入变化时重置索引。以下代码补充了键盘交互逻辑。
let currentIndex = -1;
inputEl.addEventListener('keydown', function(e) {
const items = listEl.querySelectorAll('li');
if (!items.length) return;
if (e.key === 'ArrowDown') {
currentIndex = (currentIndex + 1) % items.length;
updateActive(items);
} else if (e.key === 'ArrowUp') {
currentIndex = (currentIndex - 1 + items.length) % items.length;
updateActive(items);
} else if (e.key === 'Enter') {
e.preventDefault();
if (currentIndex > -1) {
inputEl.value = items[currentIndex].textContent;
listEl.style.display = 'none';
}
}
});
function updateActive(items) {
items.forEach(function(li, idx) {
li.style.background = idx === currentIndex ? '#eee' : '#fff';
});
}
五、点击外部关闭列表
用户点击页面其他地方时,候选列表应当隐藏。如果遗漏这一步,列表会一直挡住内容。通过监听document的点击,判断目标是否在组件内即可。
这种方式也适用于多个实例共存的情况,只要各自判断归属。下面给出基础实现,你可以将其封装为独立函数。
document.addEventListener('click', function(e) {
const wrapper = document.querySelector('.autocomplete-wrapper');
if (!wrapper.contains(e.target)) {
listEl.style.display = 'none';
}
});
六、封装为可复用组件
上面都是零散逻辑,实际项目中建议封装成一个构造函数或类,接收输入框与数据源参数,隐藏内部细节。这样在多个页面调用时只需几行代码。
封装后还能扩展功能,比如远程搜索、渲染模板自定义、最大显示条数限制等。以下示例展示一个极简的类结构。
class AutoComplete {
constructor(input, data) {
this.input = input;
this.data = data;
this.list = document.createElement('ul');
this.list.style.cssText = 'display:none;position:absolute;left:0;top:100%;width:100%;border:1px solid #ccc;list-style:none;padding:0;margin:0;background:#fff;';
input.parentNode.style.position = 'relative';
input.parentNode.appendChild(this.list);
this.bind();
}
bind() {
const self = this;
this.input.addEventListener('input', debounce(function(e) {
const kw = e.target.value.trim();
const res = self.data.filter(function(d) { return d.includes(kw); });
self.render(res);
}, 300));
}
render(data) {
this.list.innerHTML = '';
data.forEach(function(d) {
const li = document.createElement('li');
li.textContent = d;
li.style.padding = '8px';
li.addEventListener('click', () => {
this.input.value = d;
this.list.style.display = 'none';
});
this.list.appendChild(li);
});
this.list.style.display = data.length ? 'block' : 'none';
}
}
new AutoComplete(document.getElementById('ac-input'), dataSource);
到此,一个不依赖任何框架的自动完成组件就完成了。你可以基于这个骨架继续优化样式、增加异步加载,或适配移动端触摸事件,让它更贴合你的业务场景。
JavaScript自动完成组件UI组件修改时间:2026-08-07 16:45:25