Create a Custom Multi-Select Dropdown with Vanilla JavaScript
Add to your RSS feed24 January 20258 min readTable of Contents
Creating a custom multi-select dropdown enhances user experience and allows more flexibility than native <select>
elements. Here's how you can build one from scratch with JavaScript, CSS, and HTML:
Step 1: Analyze Requirements
- Allow users to select multiple options from a dropdown.
- Display selected options in an input field.
- Persist selections across page refreshes using localStorage.
Step 2: HTML Structure
Define the basic structure for the dropdown:
1 <div id="multiSelect" class="multi-select-dropdown"></div>
Step 3: CSS Styling
Style the dropdown for a clean, modern UI:
1 .multi-select-dropdown {2 position: relative;3 width: 300px;4 }5 .dropdown-header {6 border: 1px solid #ccc;7 padding: 10px;8 cursor: pointer;9 background: white;10 }11 .dropdown-list {12 display: none;13 position: absolute;14 width: 100%;15 max-height: 200px;16 overflow-y: auto;17 border: 1px solid #ccc;18 border-top: none;19 background: white;20 }21 .dropdown-list.show {22 display: block;23 }24 .dropdown-item {25 padding: 8px;26 cursor: pointer;27 }28 .dropdown-item:hover {29 background-color: #f0f0f0;30 }31 .dropdown-item.selected {32 background-color: #e0e0e0;33 }34 .selected-tags {35 display: flex;36 flex-wrap: wrap;37 gap: 5px;38 margin-bottom: 5px;39 }40 .tag {41 background-color: #f0f0f0;42 padding: 2px 5px;43 border-radius: 3px;44 display: flex;45 align-items: center;46 }47 .tag-remove {48 margin-left: 5px;49 cursor: pointer;50 }
Step 4: JavaScript Logic
Implement the dynamic functionality:
1 class MultiSelect {2 constructor(container, options) {3 this.container = container;4 this.options = options;5 this.selectedValues = [];6 this.render();7 this.setupEventListeners();8 }910 render() {11 // Create dropdown header12 const header = document.createElement('div');13 header.classList.add('dropdown-header');14 header.textContent = 'Select options';1516 // Create selected tags container17 const selectedTags = document.createElement('div');18 selectedTags.classList.add('selected-tags');1920 // Create dropdown list21 const list = document.createElement('div');22 list.classList.add('dropdown-list');2324 this.options.forEach(option => {25 const item = document.createElement('div');26 item.classList.add('dropdown-item');27 item.textContent = option.label;28 item.dataset.value = option.value;29 list.appendChild(item);30 });3132 this.container.appendChild(selectedTags);33 this.container.appendChild(header);34 this.container.appendChild(list);35 }3637 setupEventListeners() {38 const header = this.container.querySelector('.dropdown-header');39 const list = this.container.querySelector('.dropdown-list');40 const selectedTags = this.container.querySelector('.selected-tags');4142 // Toggle dropdown43 header.addEventListener('click', () => {44 list.classList.toggle('show');45 });4647 // Select/deselect items48 list.addEventListener('click', (e) => {49 if (e.target.classList.contains('dropdown-item')) {50 const value = e.target.dataset.value;51 const label = e.target.textContent;5253 if (this.selectedValues.some(item => item.value === value)) {54 // Deselect55 this.selectedValues = this.selectedValues.filter(item => item.value !== value);56 e.target.classList.remove('selected');57 } else {58 // Select59 this.selectedValues.push({ value, label });60 e.target.classList.add('selected');61 }6263 this.updateSelectedTags();64 }65 });6667 // Remove tag68 selectedTags.addEventListener('click', (e) => {69 if (e.target.classList.contains('tag-remove')) {70 const valueToRemove = e.target.closest('.tag').dataset.value;71 this.selectedValues = this.selectedValues.filter(item => item.value !== valueToRemove);72 this.updateSelectedTags();73 this.updateListSelection();74 }75 });7677 // Close dropdown when clicking outside78 document.addEventListener('click', (e) => {79 if (!this.container.contains(e.target)) {80 list.classList.remove('show');81 }82 });83 }8485 updateSelectedTags() {86 const selectedTags = this.container.querySelector('.selected-tags');87 const header = this.container.querySelector('.dropdown-header');8889 // Clear existing tags90 selectedTags.innerHTML = '';9192 // Create new tags93 this.selectedValues.forEach(item => {94 const tag = document.createElement('div');95 tag.classList.add('tag');96 tag.dataset.value = item.value;97 tag.innerHTML = `98 ${item.label}99 <span class="tag-remove">×</span>100 `;101 selectedTags.appendChild(tag);102 });103104 // Update header text105 header.textContent = this.selectedValues.length106 ? `${this.selectedValues.length} selected`107 : 'Select options';108 }109110 updateListSelection() {111 const list = this.container.querySelector('.dropdown-list');112 list.querySelectorAll('.dropdown-item').forEach(item => {113 const value = item.dataset.value;114 if (this.selectedValues.some(selected => selected.value === value)) {115 item.classList.add('selected');116 } else {117 item.classList.remove('selected');118 }119 });120 }121122 // Optional method to get selected values123 getSelectedValues() {124 return this.selectedValues.map(item => item.value);125 }126 }127128 // Example usage129 const options = [130 { value: "1", label: "JavaScript" },131 { value: "2", label: "TypeScript" },132 { value: "3", label: "React" },133 { value: "4", label: "Angular" },134 { value: "5", label: "Svelve" },135 { value: "6", label: "Vue" },136 ];137138 const multiSelect = new MultiSelect(139 document.getElementById('multiSelect'),140 options141 );
This code defines a class named MultiSelect
that creates and manages a custom multi-select dropdown using vanilla JavaScript. It allows users to select multiple options from a dropdown and displays their selections with tags. Here's a breakdown of the key functionality:
Class Constructor
1 constructor(container, options) {2 this.container = container;3 this.options = options;4 this.selectedValues = [];5 this.render();6 this.setupEventListeners();7 }
container
: A DOM element where the dropdown will be rendered.options
: An array of objects, each representing a dropdown option (value and label).selectedValues
: Tracks the currently selected items.
Calls:
render()
: Renders the dropdown structure.setupEventListeners()
: Adds interactivity to the dropdown.
render
Method
This method creates the visual structure of the dropdown:
- Header: Displays the dropdown title or number of selected items.
- Selected Tags Container: Displays tags for selected items.
- Dropdown List: Lists all the available options dynamically from the
options
array.
Each option is rendered as a div
with a data-value
attribute containing the option's unique value.
setupEventListeners
Method
Adds event listeners to enable dropdown functionality:
1. Toggle Dropdown:
1 header.addEventListener("click", () => {2 list.classList.toggle("show");3 });
Clicking the header toggles the visibility of the dropdown list.
2. Select/Deselect Items:
1 list.addEventListener("click", (e) => {2 if (e.target.classList.contains("dropdown-item")) {3 ...4 }5 });
Clicking an option:
- Adds or removes it from
selectedValues
. - Toggles the
selected
class on the item. - Updates the tags using
updateSelectedTags()
.
3. Remove Tags:
1 selectedTags.addEventListener("click", (e) => {2 if (e.target.classList.contains("tag-remove")) {3 ...4 }5 });
Clicking the remove icon (×
) on a tag:
- Removes the corresponding option from
selectedValues
. - Updates the tags and list selection.
4. Close Dropdown on Outside Click:
1 document.addEventListener("click", (e) => {2 if (!this.container.contains(e.target)) {3 list.classList.remove("show");4 }5 });
updateSelectedTags
Method
Updates the selected tags displayed below the dropdown header:
- Clears existing tags.
- Adds a new tag for each selected item with a remove button.
- Updates the header text to reflect the number of selected items or reset to "Select options."
updateListSelection Method
Synchronizes the dropdown list items with the current selections:
- Adds the
selected
class to items inselectedValues
. - Removes it from unselected items.
Optional getSelectedValues
Method
Returns an array of the value properties of the currently selected options.
Example Usage
1 const options = [2 { value: "1", label: "JavaScript" },3 { value: "2", label: "TypeScript" },4 { value: "3", label: "React" },5 { value: "4", label: "Angular" },6 { value: "5", label: "Svelte" },7 { value: "6", label: "Vue" },8 ];910 const multiSelect = new MultiSelect(11 document.getElementById("multiSelect"),12 options13 );
- options: An array of programming languages/frameworks.
- The dropdown is initialized on the container with the ID multiSelect.
Summary of Features
- Allows multiple selections with tags to display selected options.
- Dynamic rendering of dropdown options.
- Toggleable dropdown list.
- Click-to-select and deselect functionality for items.
- Option to remove tags via a close (×) button.
- Syncs UI with current selections.
This is a clean, reusable component that enhances usability by providing a multi-select dropdown built with pure JavaScript.
Full code
1 <!doctype html>2 <html lang="en">3 <head>4 <meta charset="UTF-8" />5 <title>Custom Multi-Select Dropdown</title>6 <style>7 .multi-select-dropdown {8 position: relative;9 width: 300px;10 margin: 10rem auto;11 }12 .dropdown-header {13 border: 1px solid #ccc;14 padding: 10px;15 cursor: pointer;16 background: white;17 }18 .dropdown-list {19 display: none;20 position: absolute;21 width: 100%;22 max-height: 200px;23 overflow-y: auto;24 border: 1px solid #ccc;25 border-top: none;26 background: white;27 }28 .dropdown-list.show {29 display: block;30 }31 .dropdown-item {32 padding: 8px;33 cursor: pointer;34 }35 .dropdown-item:hover {36 background-color: #f0f0f0;37 }38 .dropdown-item.selected {39 background-color: #e0e0e0;40 }41 .selected-tags {42 display: flex;43 flex-wrap: wrap;44 gap: 5px;45 margin-bottom: 5px;46 }47 .tag {48 background-color: #f0f0f0;49 padding: 2px 5px;50 border-radius: 3px;51 display: flex;52 align-items: center;53 }54 .tag-remove {55 margin-left: 5px;56 cursor: pointer;57 }58 </style>59 </head>60 <body>61 <div id="multiSelect" class="multi-select-dropdown"></div>62 <script>63 class MultiSelect {64 constructor(container, options) {65 this.container = container;66 this.options = options;67 this.selectedValues = [];68 this.render();69 this.setupEventListeners();70 }7172 render() {73 // Create dropdown header74 const header = document.createElement("div");75 header.classList.add("dropdown-header");76 header.textContent = "Select options";7778 // Create selected tags container79 const selectedTags = document.createElement("div");80 selectedTags.classList.add("selected-tags");8182 // Create dropdown list83 const list = document.createElement("div");84 list.classList.add("dropdown-list");8586 this.options.forEach((option) => {87 const item = document.createElement("div");88 item.classList.add("dropdown-item");89 item.textContent = option.label;90 item.dataset.value = option.value;91 list.appendChild(item);92 });9394 this.container.appendChild(selectedTags);95 this.container.appendChild(header);96 this.container.appendChild(list);97 }9899 setupEventListeners() {100 const header = this.container.querySelector(".dropdown-header");101 const list = this.container.querySelector(".dropdown-list");102 const selectedTags = this.container.querySelector(".selected-tags");103104 // Toggle dropdown105 header.addEventListener("click", () => {106 list.classList.toggle("show");107 });108109 // Select/deselect items110 list.addEventListener("click", (e) => {111 if (e.target.classList.contains("dropdown-item")) {112 const value = e.target.dataset.value;113 const label = e.target.textContent;114115 if (this.selectedValues.some((item) => item.value === value)) {116 // Deselect117 this.selectedValues = this.selectedValues.filter(118 (item) => item.value !== value119 );120 e.target.classList.remove("selected");121 } else {122 // Select123 this.selectedValues.push({ value, label });124 e.target.classList.add("selected");125 }126127 this.updateSelectedTags();128 }129 });130131 // Remove tag132 selectedTags.addEventListener("click", (e) => {133 if (e.target.classList.contains("tag-remove")) {134 const valueToRemove = e.target.closest(".tag").dataset.value;135 this.selectedValues = this.selectedValues.filter(136 (item) => item.value !== valueToRemove137 );138 this.updateSelectedTags();139 this.updateListSelection();140 }141 });142143 // Close dropdown when clicking outside144 document.addEventListener("click", (e) => {145 if (!this.container.contains(e.target)) {146 list.classList.remove("show");147 }148 });149 }150151 updateSelectedTags() {152 const selectedTags = this.container.querySelector(".selected-tags");153 const header = this.container.querySelector(".dropdown-header");154155 // Clear existing tags156 selectedTags.innerHTML = "";157158 // Create new tags159 this.selectedValues.forEach((item) => {160 const tag = document.createElement("div");161 tag.classList.add("tag");162 tag.dataset.value = item.value;163 tag.innerHTML = `164165166 `;167 selectedTags.appendChild(tag);168 });169170 // Update header text171 header.textContent = this.selectedValues.length172 ? `${this.selectedValues.length} selected`173 : "Select options";174 }175176 updateListSelection() {177 const list = this.container.querySelector(".dropdown-list");178 list.querySelectorAll(".dropdown-item").forEach((item) => {179 const value = item.dataset.value;180 if (181 this.selectedValues.some((selected) => selected.value === value)182 ) {183 item.classList.add("selected");184 } else {185 item.classList.remove("selected");186 }187 });188 }189190 // Optional method to get selected values191 getSelectedValues() {192 return this.selectedValues.map((item) => item.value);193 }194 }195196 // Example usage197 const options = [198 { value: "1", label: "JavaScript" },199 { value: "2", label: "TypeScript" },200 { value: "3", label: "React" },201 { value: "4", label: "Angular" },202 { value: "5", label: "Svelve" },203 { value: "6", label: "Vue" },204 ];205206 const multiSelect = new MultiSelect(207 document.getElementById("multiSelect"),208 options209 );210 </script>211 </body>212 </html>
Conclusion
Creating a custom multi-select dropdown with vanilla JavaScript provides a powerful and flexible solution for enhancing user interfaces without relying on external libraries. By dynamically rendering elements, managing user interactions, and updating the UI in real time, this approach ensures a seamless and interactive experience. It also gives you full control over customization and functionality, making it adaptable to various use cases. Whether you're building a simple form or a complex application, mastering this technique is a valuable skill that showcases the versatility of JavaScript.