JavaScript Development Space

Build a Custom Multi-Select Dropdown

24 January 20258 min read
Create a Custom Multi-Select Dropdown with Vanilla JavaScript

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:

html
1 <div id="multiSelect" class="multi-select-dropdown"></div>

Step 3: CSS Styling

Style the dropdown for a clean, modern UI:

css
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:

js
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 }
9
10 render() {
11 // Create dropdown header
12 const header = document.createElement("div");
13 header.classList.add("dropdown-header");
14 header.textContent = "Select options";
15
16 // Create selected tags container
17 const selectedTags = document.createElement("div");
18 selectedTags.classList.add("selected-tags");
19
20 // Create dropdown list
21 const list = document.createElement("div");
22 list.classList.add("dropdown-list");
23
24 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 });
31
32 this.container.appendChild(selectedTags);
33 this.container.appendChild(header);
34 this.container.appendChild(list);
35 }
36
37 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");
41
42 // Toggle dropdown
43 header.addEventListener("click", () => {
44 list.classList.toggle("show");
45 });
46
47 // Select/deselect items
48 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;
52
53 if (this.selectedValues.some((item) => item.value === value)) {
54 // Deselect
55 this.selectedValues = this.selectedValues.filter(
56 (item) => item.value !== value
57 );
58 e.target.classList.remove("selected");
59 } else {
60 // Select
61 this.selectedValues.push({ value, label });
62 e.target.classList.add("selected");
63 }
64
65 this.updateSelectedTags();
66 }
67 });
68
69 // Remove tag
70 selectedTags.addEventListener("click", (e) => {
71 if (e.target.classList.contains("tag-remove")) {
72 const valueToRemove = e.target.closest(".tag").dataset.value;
73 this.selectedValues = this.selectedValues.filter(
74 (item) => item.value !== valueToRemove
75 );
76 this.updateSelectedTags();
77 this.updateListSelection();
78 }
79 });
80
81 // Close dropdown when clicking outside
82 document.addEventListener("click", (e) => {
83 if (!this.container.contains(e.target)) {
84 list.classList.remove("show");
85 }
86 });
87 }
88
89 updateSelectedTags() {
90 const selectedTags = this.container.querySelector(".selected-tags");
91 const header = this.container.querySelector(".dropdown-header");
92
93 // Clear existing tags
94 selectedTags.innerHTML = "";
95
96 // Create new tags
97 this.selectedValues.forEach((item) => {
98 const tag = document.createElement("div");
99 tag.classList.add("tag");
100 tag.dataset.value = item.value;
101 tag.innerHTML = `
102 ${item.label}
103 <span class="tag-remove">×</span>
104 `;
105 selectedTags.appendChild(tag);
106 });
107
108 // Update header text
109 header.textContent = this.selectedValues.length
110 ? `${this.selectedValues.length} selected`
111 : "Select options";
112 }
113
114 updateListSelection() {
115 const list = this.container.querySelector(".dropdown-list");
116 list.querySelectorAll(".dropdown-item").forEach((item) => {
117 const value = item.dataset.value;
118 if (this.selectedValues.some((selected) => selected.value === value)) {
119 item.classList.add("selected");
120 } else {
121 item.classList.remove("selected");
122 }
123 });
124 }
125
126 // Optional method to get selected values
127 getSelectedValues() {
128 return this.selectedValues.map((item) => item.value);
129 }
130 }
131
132 // Example usage
133 const options = [
134 { value: "1", label: "JavaScript" },
135 { value: "2", label: "TypeScript" },
136 { value: "3", label: "React" },
137 { value: "4", label: "Angular" },
138 { value: "5", label: "Svelve" },
139 { value: "6", label: "Vue" },
140 ];
141
142 const multiSelect = new MultiSelect(
143 document.getElementById("multiSelect"),
144 options
145 );

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

js
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:

js
1 header.addEventListener("click", () => {
2 list.classList.toggle("show");
3 });

Clicking the header toggles the visibility of the dropdown list.

2. Select/Deselect Items:

js
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:

js
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:

js
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 in selectedValues.
  • Removes it from unselected items.

Optional getSelectedValues Method

Returns an array of the value properties of the currently selected options.

Example Usage

js
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 ];
9
10 const multiSelect = new MultiSelect(
11 document.getElementById("multiSelect"),
12 options
13 );
  • 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

html
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 }
71
72 render() {
73 // Create dropdown header
74 const header = document.createElement("div");
75 header.classList.add("dropdown-header");
76 header.textContent = "Select options";
77
78 // Create selected tags container
79 const selectedTags = document.createElement("div");
80 selectedTags.classList.add("selected-tags");
81
82 // Create dropdown list
83 const list = document.createElement("div");
84 list.classList.add("dropdown-list");
85
86 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 });
93
94 this.container.appendChild(selectedTags);
95 this.container.appendChild(header);
96 this.container.appendChild(list);
97 }
98
99 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");
103
104 // Toggle dropdown
105 header.addEventListener("click", () => {
106 list.classList.toggle("show");
107 });
108
109 // Select/deselect items
110 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;
114
115 if (this.selectedValues.some((item) => item.value === value)) {
116 // Deselect
117 this.selectedValues = this.selectedValues.filter(
118 (item) => item.value !== value
119 );
120 e.target.classList.remove("selected");
121 } else {
122 // Select
123 this.selectedValues.push({ value, label });
124 e.target.classList.add("selected");
125 }
126
127 this.updateSelectedTags();
128 }
129 });
130
131 // Remove tag
132 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 !== valueToRemove
137 );
138 this.updateSelectedTags();
139 this.updateListSelection();
140 }
141 });
142
143 // Close dropdown when clicking outside
144 document.addEventListener("click", (e) => {
145 if (!this.container.contains(e.target)) {
146 list.classList.remove("show");
147 }
148 });
149 }
150
151 updateSelectedTags() {
152 const selectedTags = this.container.querySelector(".selected-tags");
153 const header = this.container.querySelector(".dropdown-header");
154
155 // Clear existing tags
156 selectedTags.innerHTML = "";
157
158 // Create new tags
159 this.selectedValues.forEach((item) => {
160 const tag = document.createElement("div");
161 tag.classList.add("tag");
162 tag.dataset.value = item.value;
163 tag.innerHTML = `
164 ${item.label}
165 <span class="tag-remove">×</span>
166 `;
167 selectedTags.appendChild(tag);
168 });
169
170 // Update header text
171 header.textContent = this.selectedValues.length
172 ? `${this.selectedValues.length} selected`
173 : "Select options";
174 }
175
176 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 }
189
190 // Optional method to get selected values
191 getSelectedValues() {
192 return this.selectedValues.map((item) => item.value);
193 }
194 }
195
196 // Example usage
197 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 ];
205
206 const multiSelect = new MultiSelect(
207 document.getElementById("multiSelect"),
208 options
209 );
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.

JavaScript Development Space

JSDev Space – Your go-to hub for JavaScript development. Explore expert guides, best practices, and the latest trends in web development, React, Node.js, and more. Stay ahead with cutting-edge tutorials, tools, and insights for modern JS developers. 🚀

Join our growing community of developers! Follow us on social media for updates, coding tips, and exclusive content. Stay connected and level up your JavaScript skills with us! 🔥

© 2025 JavaScript Development Space - Master JS and NodeJS. All rights reserved.