帶有多選和突出顯示關鍵字的自定義下拉選擇框(靜態)

熊仔其人發表於2024-12-02

帶有多選和突出顯示關鍵字的自定義下拉選擇框:
Custom Dropdown Select Box with Multiple Selection and Highlighting Keywords:
不使用select元素,用div、ul、li、span元素實現帶有多選和突出顯示關鍵字的自定義下拉選擇框。
完整控制元件包含三部分:

  1. 第一步用div模擬select選擇框,ul+li模擬option選項,並用span標記關鍵詞;
  2. 第二步CSS渲染展示效果;
  3. 第三步用原生JavaScript指令碼實現點選選擇,多選,取消選擇,獲取選中項結果(陣列);

1,HTML模擬選擇框

核心程式碼如下:

<!-- 自定義下拉選擇框 html 開始 -->
<div class="custom-select">
    <div class="selected-options">
        <span class="placeholder">請選擇...</span>
    </div>
    <ul class="options-list">
        <li class="option" data-value="option1">選項1 <span class="highlight">關鍵詞</span></li>
        <li class="option" data-value="option2">選項2</li>
        <li class="option" data-value="option3">這個選項3 也有<span class="highlight">關鍵詞</span></li>
        <li class="option" data-value="option4">選項4</li>
    </ul>
</div>
<!-- 自定義下拉選擇框 html 結束 -->

搭配上CSS樣式渲染就可以看到靜態的選擇框。接下來還學透過JavaScript指令碼實現功能邏輯。

2,JavaScript指令碼實現功能邏輯

我們知道JavaScript是瀏覽器前端指令碼語言,要實現網頁動態效果是很合適的。根據物件導向程式設計思想,首先我們得拿到要處理的物件,即HTML標籤(DOM元素),例如:

var customSelect = document.querySelector('.custom-select');
var optionsList = document.querySelector('.options-list');
var selectedOptions = document.querySelector('.selected-options');
var options = document.querySelectorAll('.option');

接著我們對標籤元素繫結事件驅動,例如:

監聽DOMContentLoaded觸發事件

DOMContentLoaded 是一個在網頁的文件結構(DOM)載入完成後觸發的事件,而不需要等待所有的外部資源(如圖片、樣式表、指令碼等)載入完成。這個事件對於執行依賴於DOM解析完成後的JavaScript程式碼非常有用,因為它允許你儘早地開始執行程式碼,而不必等待整個頁面完全載入。
在JavaScript中,你可以使用 addEventListener 方法來監聽 DOMContentLoaded 事件。以下是一個簡單的示例:

// 網頁的文件結構(DOM)載入完成後觸發的事件
document.addEventListener('DOMContentLoaded', function (event) {
    event.stopPropagation(); // 阻止事件冒泡到外層元素
    console.log('DOM已載入完成!');  
    // 在這裡執行你的程式碼 
}

在這個示例中,當 DOMContentLoaded 事件觸發時,會執行一個匿名函式,該函式在控制檯輸出一條訊息。你可以在這個函式內部執行任何需要在DOM載入完成後執行的JavaScript程式碼。
需要注意的是,DOMContentLoaded 事件只會在文件的初始載入時觸發一次。如果你希望在頁面載入完成後執行某些操作,並且這些操作需要在每次頁面載入時都執行,你可能需要考慮使用其他方法,如 load 事件或 jQuery 的 $(document).ready() 方法(如果你在使用jQuery)。
另外,如果你正在編寫一個庫或框架,並且需要確保在DOM載入完成之前不執行任何操作,那麼監聽 DOMContentLoaded 事件可能是一個很好的選擇。這樣可以確保你的程式碼在DOM結構可用時立即執行,而不必等待其他資源載入完成。

監聽click觸發事件

在JavaScript中,click事件是一個常用的滑鼠事件,它在使用者點選某個元素(例如,按鈕、連結、圖片等)時觸發。你可以使用addEventListener方法來監聽click事件,並在事件觸發時執行相應的程式碼。
透過監聽click事件,可以實現各種互動效果,例如按鈕點選、連結跳轉、圖片放大等。可以透過addEventListener方法將click事件繫結到指定的元素上,並在事件觸發時執行相應的操作。
以下是一個三種常見的簡單示例程式碼,演示瞭如何使用click事件:

// 選項標籤元素繫結click觸發事件
document.querySelectorAll('.option').addEventListener('click', function (event) {
    event.stopPropagation(); // 阻止事件冒泡到外層元素
	console.log('選項標籤元素被點選了!');
});

// 實時建立的標籤元素繫結click觸發事件
var span = document.createElement('span');
span.addEventListener('click', function (event) {
    event.stopPropagation(); // 阻止事件冒泡到外層元素
	console.log('實時建立的標籤元素被點選了!');
});

// 點選選項以外的地方觸發事件
document.addEventListener('click', function (e) {
    e.stopPropagation(); // 阻止事件冒泡到外層元素
    console.log('選項以外的地方被點選了!');
});

需要注意的是,同一個元素可以新增多個同型別的事件監聽器,它們會按照新增的順序依次執行。此外,你可以使用removeEventListener方法來移除之前新增的事件監聽器。
這部分更詳細可以參考:JavaScript操作addEventListener監聽觸發事件

陣列的遍歷、插入、刪除操作

在JavaScript中,陣列是一種常見的資料結構,可以包含多個元素,並且可以進行遍歷、插入和刪除等操作。

這部分更詳細可以參考:JavaScript運算元組

調整標籤元素展示樣式

在JavaScript中,classList.toggle 是一個DOM(Document Object Model)元素的方法,用於切換元素的類名。如果元素已經包含指定的類名,則該類名會被移除;如果元素不包含指定的類名,則該類名會被新增。這個方法在動態修改元素的樣式時非常有用。

這部分更詳細可以參考:JavaScript操作DOM元素的classList

完整程式碼

完整程式碼(DropdownSelectBox.html 單個網頁檔案):

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="MobileOptimized" content="240">
    <meta name="applicable-device" content="mobile">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no,shrink-to-fit=no">
    <meta name="format-detection" content="telephone=no,email=no,adress=no">

    <meta name="theme-color" content="#3c80d4" media="(prefers-color-scheme: light)">
    <meta name="theme-color" content="#9cc8ff" media="(prefers-color-scheme: dark)">

    <title>帶有多選和突出顯示關鍵字的自定義下拉選擇框</title>
    <meta name="Description" content="帶有多選和突出顯示關鍵字的自定義下拉選擇框:Custom Dropdown Select Box with Multiple Selection and Highlighting Keywords">
    <meta name="author" content="熊仔其人">

    <!-- CSS樣式 -->
    <style type="text/css">
        /* 自定義下拉選擇框 CSS 開始 */
        .custom-select {
            position: relative;
            width: 200px;
            border: 1px solid #ccc;
            border-radius: 5px;
            padding: 1px;
        }

        .selected-options {
            padding: 1px;
            cursor: pointer;
            background-color: #fff;
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
        }

        .selected-options .selectedOptions-icon-cross {
            background-color: #f8f9fa;
            border: 1px solid #ccc;
            border-radius: 5px;
            padding: 1px;
        }

        .selected-options .selectedOptions-icon-cross::after {
            /* content: '\2717'; */
            content: '\2715';
        }

        .placeholder {
            color: #999;
        }

        .options-list {
            position: absolute;
            top: 100%;
            left: 0;
            right: 0;
            border: 1px solid #ccc;
            border-top: none;
            border-radius: 0 0 5px 5px;
            background-color: #fff;
            list-style: none;
            padding: 0;
            margin: 0;
            display: none;
            /* 預設隱藏選項列表 */
        }

        .option {
            padding: 5px;
            cursor: pointer;
        }

        .option:hover {
            background-color: #f2f2f2;
        }

        .option.selected {
            background-color: #e0e0e0;
        }

        .option.selected::after {
            /* content: '\221A'; */
            content: '\2713';
            color: #007bff;
            margin-left: 5px;
        }

        .highlight {
            color: red;
            /* 關鍵詞標紅 */
        }

        /* 顯示選項列表 */
        .custom-select.active .options-list {
            display: block;
        }
        /* 自定義下拉選擇框 CSS 結束 */
    </style>

</head>

<body>
    帶有多選和突出顯示關鍵字的自定義下拉選擇框:
    Custom Dropdown Select Box with Multiple Selection and Highlighting Keywords:
    <hr />
    
    <!-- 自定義下拉選擇框 html 開始 -->
    <div class="custom-select">
        <div class="selected-options">
            <span class="placeholder">請選擇...</span>
        </div>
        <ul class="options-list">
            <li class="option" data-value="option1">選項1 <span class="highlight">關鍵詞</span></li>
            <li class="option" data-value="option2">選項2</li>
            <li class="option" data-value="option3">這個選項3 也有<span class="highlight">關鍵詞</span></li>
            <li class="option" data-value="option4">選項4</li>
        </ul>
    </div>
    <!-- 自定義下拉選擇框 html 結束 -->

    <!-- 自定義下拉選擇框 JS指令碼 開始 -->
    <script>
        document.addEventListener('DOMContentLoaded', function (event) {
            event.stopPropagation(); // 阻止事件冒泡到外層元素
            var customSelect = document.querySelector('.custom-select');
            var optionsList = document.querySelector('.options-list');
            var selectedOptions = document.querySelector('.selected-options');
            var options = document.querySelectorAll('.option');

            // 顯示/隱藏選項列表  
            selectedOptions.addEventListener('click', function (event) {
                event.stopPropagation(); // 阻止事件冒泡到外層元素
                customSelect.classList.toggle('active');
            });

            // 選項點選事件  
            options.forEach(function (option) {
                option.addEventListener('click', function (event) {
                    event.stopPropagation(); // 阻止事件冒泡到外層元素
                    var isSelected = this.classList.contains('selected');
                    this.classList.toggle('selected', !isSelected);

                    // 更新已選項顯示  
                    updateSelectedDisplay();
                });
            });

            // 點選選項以外的地方關閉選項列表  
            document.addEventListener('click', function (e) {
                e.stopPropagation(); // 阻止事件冒泡到外層元素
                if (!customSelect.contains(e.target)) {
                    customSelect.classList.remove('active');
                }
            });

            // 更新已選項的顯示  
            function updateSelectedDisplay() {
                var selectedItems = [];
                options.forEach(function (option) {
                    if (option.classList.contains('selected')) {
                        selectedItems.push({ value: option.getAttribute('data-value'), text: option.textContent.trim() });
                    }
                });

                // 清空並重新填充已選項  
                selectedOptions.innerHTML = '';
                if (selectedItems.length === 0) {
                    selectedOptions.appendChild(document.createTextNode('請選擇...'));
                } else {
                    selectedItems.forEach(function (item) {
                        var span = document.createElement('span');
                        span.setAttribute('class', 'selectedOptions-icon-cross');
                        span.setAttribute('data-value', item.value);
                        span.textContent = item.text + ' ';
                        span.addEventListener('click', function (event) {
                            event.stopPropagation(); // 阻止事件冒泡到外層元素
                            var itemValue = this.getAttribute('data-value');
                            options.forEach(function (option) {
                                if (option.getAttribute('data-value') == itemValue) {
                                    option.classList.toggle('selected', false);
                                }
                            });

                            // 更新已選項顯示  
                            updateSelectedDisplay();
                        });
                        selectedOptions.appendChild(span);
                    });
                }
            }
        });

        // 獲取選中項
        function getSelectedItems() {
            var selectedItems = [];
            var selectedOptions = document.querySelectorAll('.custom-select .selected-options .selectedOptions-icon-cross');
            selectedOptions.forEach(function (option) {
                selectedItems.push({ value: option.getAttribute('data-value'), text: option.textContent.trim() });
            });

            return selectedItems;
        }

    </script>
    <!-- 自定義下拉選擇框 JS指令碼 結束 -->

</body>

</html>

效果

效果展示:

我這裡是將可選擇項寫固定的,在實際專案中可以改成輸入關鍵詞,非同步方法去後臺查詢可供選項的列表,繫結到前端該控制元件,然後就是正常的在專案中動態地使用了。

相關文章