创建一个自定义的下拉菜单
DEMO
下方是 Demo 展示区域,点击即可输入框触发下拉菜单。
过程
点击输入框,打开下拉菜单
我最初的想法是给每个输入元素添加一个点击事件监听器,但后来我意识到,当文档加载完成时,并不意味着页面不会随着内容的更新而添加新的输入元素。
因此,为了解决这个问题,我们可以给 body 添加一个点击事件监听器。当 Input 发出的点击事件冒泡时,我们就可以做我们需要做的事情——显示下拉菜单。
(伪)代码如下:
1
2
3
4
5
6
7
8
9
10
11
12const showSelect = () =>{
// create a dropdown element and append to document
// ...
}
document.addEventListener("click", (e) => {
const target = e.target;
if (target.tagName === "INPUT" && ["text", "password"].includes(target.type)) {
showSelect();
}
});设置下拉框选项和样式
我们可以使用“getBoundingClientRect()”来获取当前输入元素的偏移位置(相对于其父元素)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27let currentInput = null
const showSelect = ({top, left, width}) =>{
// create a dropdown element and append to document
const dropdownElement = document.createElement("div");
// ...
const inputParentElement = currentInput.parentElement;
if(!["fixed", "absolute"].includes(inputParentElement.position)){
inputParentElement.style.position = 'relative';
const {offsetTop, offsetLeft, offsetHeight} = currentInput
dropdownElement.style.position = 'absolute'
dropdownElement.style.top = offsetTop + offsetHeight + 'px'
dropdownElement.style.left = offsetLeft + 'px'
inputParentElement.appendChild(dropdownElement)
}else{
document.body.appendChild(dropdownElement);
}
}
document.addEventListener("click", (e) => {
const target = e.target;
if (target.tagName === "INPUT" && ["text", "password"].includes(target.type)) {
currentInput = target;
const { x, y, width, height } = currentInput.getBoundingClientRect();
showSelect({ top: y + height, left: x, width });
}
});收起下拉菜单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22const hideSelect = () => {
const selectwrapElement = document.querySelector("#selectwrapElement");
if (selectwrapElement) selectwrapElement.parentElement.removeChild(selectwrapElement);
};
// hide on clicking an option in dropdown menu
option.onclick = () => {
currentInput.value = value;
hideSelect();
};
// hide on press `Escape` or input anything in current input
document.addEventListener("keyup", (e) => {
if (document.querySelector("#selectwrapElement")) {
if (
e.code === "Escape" ||
document.activeElement.tagName === "INPUT"
) {
hideSelect();
}
}
});
为什么不
使用 <select>
元素?
其实我一开始确实用 Select 元素来实现我的需求,因为它的属性简单清晰。
但是因为它的属性太简单(只有 label
和 value
),当我需要显示标签内容时,它们无处可去。
所以我用自定义 <div>
和样式重写了下拉元素。
完整代码
1 | (function(){ |