輸入框變身搜尋下拉選單

最近常常遇到 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; */
}

改造的思路如下:

  1. 監聽 <input> 輸入框的點擊事件,下方要如同下拉選單一般呈現所有的選項。
  2. 監聽 <input> 輸入事件,篩選下拉選單中符合輸入「字串」的選項。
  3. 選取下拉選單中的選項,同時呈現在輸入框之中。

在 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/
作者
POPEYE
發布於
2023年5月25日
許可協議