JavaScript Development Space

Create a Custom Multi-Select Dropdown with Vanilla JavaScript

Add to your RSS feed24 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(item => item.value !== value);
56 e.target.classList.remove('selected');
57 } else {
58 // Select
59 this.selectedValues.push({ value, label });
60 e.target.classList.add('selected');
61 }
62
63 this.updateSelectedTags();
64 }
65 });
66
67 // Remove tag
68 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 });
76
77 // Close dropdown when clicking outside
78 document.addEventListener('click', (e) => {
79 if (!this.container.contains(e.target)) {
80 list.classList.remove('show');
81 }
82 });
83 }
84
85 updateSelectedTags() {
86 const selectedTags = this.container.querySelector('.selected-tags');
87 const header = this.container.querySelector('.dropdown-header');
88
89 // Clear existing tags
90 selectedTags.innerHTML = '';
91
92 // Create new tags
93 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 });
103
104 // Update header text
105 header.textContent = this.selectedValues.length
106 ? `${this.selectedValues.length} selected`
107 : 'Select options';
108 }
109
110 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 }
121
122 // Optional method to get selected values
123 getSelectedValues() {
124 return this.selectedValues.map(item => item.value);
125 }
126 }
127
128 // Example usage
129 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 ];
137
138 const multiSelect = new MultiSelect(
139 document.getElementById('multiSelect'),
140 options
141 );

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

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