輸入框變身搜尋下拉選單
最近常常遇到 PM 要求「下拉選單同時要有搜尋」的功能,之前在 Vue 裡面使用 V-select 套件來做,如果在原生的 JS 要寫這樣的功能,也找到 chosen.js 或 select2.js 這樣的套件可以做出這樣的功能。
剛開始我都以為「下拉選單同時要有搜尋」是使用<select>
表單元素去改的,但是在找資料的過程中,慢慢認知到可能不是用 select
,而是用 input
去改出這樣的效果。
HTML
<div class="input-wrapper">
<div class="input-div">
<!-- 箭頭 要做旋轉的互動 -->
<i class="uil uil-angle-down"></i>
<!-- input 輸入搜尋 -->
<input type="text" placeholder="search" id="input-search">
<!-- option 要放下拉選單的選項 -->
<div class="option">
</div>
</div>
</div>
CSS
CSS 部分儘量不做太多裝飾,不要混淆我們學習做這個功能的單純性。在這裡我會取消 input
的框線,用 input-div
包住它及放選項的 option
,偽裝成下拉選單。
當 user 點擊 input
的時候,option
加上 .active
樣式,露出下拉選單的選項。
.input-wrapper{
width: 200px;
margin: 50px auto;
padding: 0;
}
.input-div {
position: relative;
border: 1px solid blue;
width: 240px;
max-height: 250px;
overflow: hidden;
}
.input-div input {
width: 196px;
border: none;
outline: none;
}
.input-div input:focus {
border:none;
}
.input-div i {
position: absolute;
right: 6px;
font-size: 16px;
transition: transform 0.3s linear;
}
.arrowTransform {
transform: rotate(-180deg);
}
.option.active i{
transform: rotate(-180deg);
}
.option {
display: none;
height: 100px;
overflow-y: auto;
}
.option li {
list-style: none;
}
.option::-webkit-scrollbar{
width: 7px;
}
.option::-webkit-scrollbar-track{
background: #f1f1f1;
border-radius: 24px;
}
.option::-webkit-scrollbar-thumb{
background: #ccc;
border-radius: 24px;
}
.active {
display:block;
/* border: 1px red solid; */
}
改造的思路如下:
- 監聽
<input>
輸入框的點擊事件,下方要如同下拉選單一般呈現所有的選項。 - 監聽
<input>
輸入事件,篩選下拉選單中符合輸入「字串」的選項。 - 選取下拉選單中的選項,同時呈現在輸入框之中。
在 JS 中,我們先取得這幾個 DOM 元素及設定下拉選單的資料陣列:
// inputSearch 取得 input 輸入框的 DOM 元素
const inputSearch = document.getElementById('input-search');
// option 取得 要置放下拉選項的 DOM 元素
const option = document.querySelector('.option');
// arrow 取得 input 輸入框的向下箭頭
const arrow = document.querySelector('.uil-angle-down');
// 下拉選單的選項
let iParks = ["鋼鐵人","蜘蛛人","美國隊長","黑寡婦","雷神"];
監聽 <input>
輸入框的點擊事件
// 監聽 輸入框,點擊時 option 加上 .active 樣式
inputSearch.addEventListener('click',()=>{
// 添加 active 樣式,讓下拉選單顯現出來
option.classList.toggle('active');
console.log(i);
if (option.classList.contains("active")) {
i.classList.add("arrowTransform");
} else {
i.classList.remove("arrowTransform");
}
// arrow.classList.toggle('arrowTransform');
addIpark();
})
監聽 <input>
輸入事件
// 監聽輸入框
inputSearch.addEventListener("keyup",()=>{
let arr = [];
// 輸入的字串
let searchedVal = inputSearch.value;
arr = iParks.filter(data => {
return data.toLowerCase().startsWith(searchedVal);
}).map(data => `<li onclick="updateName(this)">${data}</li>`).join("");
// 組字串到 option 中
option.innerHTML = arr ? arr : `<p>沒有符合您輸入條件的選項</p>`;
})
選取下拉選單中的選項,同時呈現在輸入框之中
這個 function
要在 option
的選項組版時,放入 <li>
中。
`<li onclick="updateName(this)">${data}</li>`
function updateName(selectedLi){
console.log(selectedLi.innerText);
inputSearch.value ="";
addIpark();
option.classList.remove("active");
if (option.classList.contains("active")) {
i.classList.add("arrowTransform");
} else {
i.classList.remove("arrowTransform");
}
inputSearch.value = selectedLi.innerText;
}
完整 JS
// inputSearch 取得 input 輸入框的 DOM 元素
const inputSearch = document.getElementById('input-search');
// option 取得 要置放下拉選項的 DOM 元素
const option = document.querySelector('.option');
// arrow 取得 input 輸入框的向下箭頭
const arrow = document.querySelector('.uil-angle-down')
const i = document.querySelector('.input-div i')
// 下拉選單的選項
let iParks = ["鋼鐵人","蜘蛛人","美國隊長","黑寡婦","雷神"];
// 監聽 輸入框,點擊時 option 加上 .active 樣式
inputSearch.addEventListener('click',()=>{
// 添加 active 樣式,讓下拉選單顯現出來
option.classList.toggle('active');
console.log(i);
if (option.classList.contains("active")) {
i.classList.add("arrowTransform");
} else {
i.classList.remove("arrowTransform");
}
// arrow.classList.toggle('arrowTransform');
addIpark();
})
// 把選項渲染到下拉選單中
function addIpark(){
option.innerHTML = "";
iParks.forEach(iPark =>{
console.log(iPark);
let li = `<li onclick="updateName(this)">${iPark}</li>`;
option.insertAdjacentHTML("beforeend",li);
})
}
// 下拉選單 選取選項之後 更新到 input
function updateName(selectedLi){
console.log(selectedLi.innerText);
inputSearch.value ="";
addIpark();
option.classList.remove("active");
if (option.classList.contains("active")) {
i.classList.add("arrowTransform");
} else {
i.classList.remove("arrowTransform");
}
inputSearch.value = selectedLi.innerText;
// selectBtn.firstElementChild.innerText = selectedLi.innerText;
}
// 監聽輸入框
inputSearch.addEventListener("keyup",()=>{
console.log(inputSearch.value);
let arr = [];
let searchedVal = inputSearch.value;
arr = iParks.filter(data => {
return data.toLowerCase().startsWith(searchedVal);
}).map(data => `<li onclick="updateName(this)">${data}</li>`).join("");
option.innerHTML = arr ? arr : `<p>沒有符合您輸入條件的選項</p>`;
console.log(arr);
})
參考資料:
輸入框變身搜尋下拉選單
https://popeye-ux.github.io/2023/05/25/js-input-select/