The <select>
element gives a menu of options for a user and is implemented by the DOM HTMLSelectElement
Interface. You can add the <multiple>
attributes to the select
element and add multiple options. The examples in this section use HTML, CSS, and JavasScript to customize <select>
styles.
The following example creates a very simple dropdown menu, the second option of which is selected by default.
<!-- The second value will be selected initially --> <select name="choice"> <option value="first">First Value</option> <option value="second" selected>Second Value</option> <option value="third">Third Value</option> </select>
The follow example is more complex, showing off more features you can use on a <select>
element:
<label> Please choose one or more pets: <select name="pets" multiple size="4"> <optgroup label="4-legged pets"> <option value="dog">Dog</option> <option value="cat">Cat</option> <option value="hamster" disabled>Hamster</option> </optgroup> <optgroup label="Flying pets"> <option value="parrot">Parrot</option> <option value="macaw">Macaw</option> <option value="albatross">Albatross</option> </optgroup> </select> </label>
You'll see that:
multiple
attribute.size
attribute causes only 4 lines to display at a time; you can scroll to view all the options.<optgroup>
elements to divide the options up into different groups. This is a
purely visual grouping, its visualization generally consists of the
group name being bolded, and the options being indented.disabled
attribute and therefore can't be selected at all.This example shows how you could use some CSS and JavaScript to provide extensive custom styling for a <select>
box.
This example basically:
<select>
's context (the <option>
elements) in a parent wrapper and reimplements the standard expected
behavior using additional HTML elements and JavaScript. This includes
basic tab behavior to provide keyboard accessibility.attributes
to data-attributes
of the new elements in order to manage state and CSS.Note: Not all native features are supported, it's a Proof of Concept. IT starts from standard HTML but the same results can be achieved starting from JSON data, custom HTML, or other solutions.
<form> <fieldset> <legend>Standard controls</legend> <select name="1A" id="select" autocomplete="off" required> <option>Carrots</option> <option>Peas</option> <option>Beans</option> <option>Pneumonoultramicroscopicsilicovolcanoconiosis</option> </select> </fieldset> <fieldset id="custom"> <legend>Custom controls</legend> <select name="2A" id="select" autocomplete="off" required> <option>Carrots</option> <option>Peas</option> <option>Beans</option> <option>Pneumonoultramicroscopicsilicovolcanoconiosis</option> </select> </fieldset> </form>
body { font-family: Cambria, Cochin, Georgia, Times, "Times New Roman", serif; } .select:focus { border-color: blue; } html body form fieldset#custom div.select[data-multiple] div.header { display: none; } html body form fieldset#custom div.select div.header { content: "↓"; display: flex; flex: 1; align-items: center; padding: 0; position: relative; width: auto; box-sizing: border-box; border-width: 1px; border-style: inherit; border-color: inherit; border-radius: inherit; } html body form fieldset#custom div.select div.header::after { content: "↓"; align-self: stretch; display: flex; align-content: center; justify-content: center; justify-items: center; align-items: center; padding: 0.5em; } html body form fieldset#custom div.select div.header:hover::after { background-color: blue; } .select .header select { appearance: none; font-family: inherit; font-size: inherit; padding: 0; border-width: 0; width: 100%; flex: 1; display: none; } .select .header select optgroup { display: none; } .select select div.option { display: none; } html body form fieldset#custom div.select { user-select: none; box-sizing: border-box; position: relative; border-radius: 4px; border-style: solid; border-width: 0; border-color: gray; width: auto; display: inline-block; } html body form fieldset#custom div.select:focus, html body form fieldset#custom div.select:hover { border-color: blue; } html body form fieldset#custom div.select[data-open] { border-bottom-left-radius: 0; border-bottom-right-radius: 0; } html body form fieldset#custom div.select[data-open] datalist { display: initial; } html body form fieldset#custom div.select datalist { appearance: none; position: absolute; border-style: solid; border-width: 1px; border-color: gray; left: 0; display: none; width: 100%; box-sizing: border-box; z-index: 2; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; } html body form fieldset#custom div.select datalist div.option { background-color: white; margin-bottom: 1px; cursor: pointer; padding: 0.5em; border-width: 0; } html body form fieldset#custom div.select datalist div.option:hover, html body form fieldset#custom div.select datalist div.option:focus, html body form fieldset#custom div.select datalist div.option:checked { background-color: blue; color: white; } html body form fieldset#custom div.select div.optgroup div.option[data-disabled] { color: gray; } html body form fieldset#custom div.select div.optgroup div.option[data-checked] { background-color: blue; color: white; } html body form fieldset#custom div.select div.optgroup div.label { font-weight: bold; } html body form fieldset#custom div.select div.optgroup div.option div.label { font-weight: normal; padding: 0.25em; } html body form fieldset#custom div.select div.header span { flex: 1; padding: 0.5em; }
const selects = custom.querySelectorAll("select"); for (const select of selects) { const div = document.createElement("div"); const header = document.createElement("div"); const datalist = document.createElement("datalist"); const optgroups = select.querySelectorAll("optgroup"); const span = document.createElement("span"); const options = select.options; const parent = select.parentElement; const multiple = select.hasAttribute("multiple"); function onclick(e) { const disabled = this.hasAttribute("data-disabled"); select.value = this.dataset.value; span.innerText = this.dataset.label; if (disabled) return; if (multiple) { if (e.shiftKey) { const checked = this.hasAttribute("data-checked"); if (checked) { this.removeAttribute("data-checked"); } else { this.setAttribute("data-checked", ""); } } else { const options = div.querySelectorAll(".option"); for (let i = 0; i < options.length; i++) { const option = options[i]; option.removeAttribute("data-checked"); } this.setAttribute("data-checked", ""); } } } function onkeyup(e) { e.preventDefault(); e.stopPropagation(); if (e.keyCode === 13) { this.click(); } } div.classList.add("select"); header.classList.add("header"); div.tabIndex = 1; select.tabIndex = -1; span.innerText = select.label; header.appendChild(span); for (const attribute of select.attributes) { div.dataset[attribute.name] = attribute.value; } for (let i = 0; i < options.length; i++) { const option = document.createElement("div"); const label = document.createElement("div"); const o = options[i]; for (const attribute of o.attributes) { option.dataset[attribute.name] = attribute.value; } option.classList.add("option"); label.classList.add("label"); label.innerText = o.label; option.dataset.value = o.value; option.dataset.label = o.label; option.onclick = onclick; option.onkeyup = onkeyup; option.tabIndex = i + 1; option.appendChild(label); datalist.appendChild(option); } div.appendChild(header); for (const o of optgroups) { const optgroup = document.createElement("div"); const label = document.createElement("div"); const options = o.querySelectorAll("option"); Object.assign(optgroup, o); optgroup.classList.add("optgroup"); label.classList.add("label"); label.innerText = o.label; optgroup.appendChild(label); div.appendChild(optgroup); for (const o of options) { const option = document.createElement("div"); const label = document.createElement("div"); for (const attribute of o.attributes) { option.dataset[attribute.name] = attribute.value; } option.classList.add("option"); label.classList.add("label"); label.innerText = o.label; option.tabIndex = i + 1; option.dataset.value = o.value; option.dataset.label = o.label; option.onclick = onclick; option.onkeyup = onkeyup; option.tabIndex = i + 1; option.appendChild(label); optgroup.appendChild(option); } } div.onclick = (e) => { e.preventDefault(); }; parent.insertBefore(div, select); header.appendChild(select); div.appendChild(datalist); datalist.style.top = `${header.offsetTop + header.offsetHeight}px`; div.onclick = (e) => { if (!multiple) { const open = div.hasAttribute("data-open"); e.stopPropagation(); if (open) { div.removeAttribute("data-open"); } else { div.setAttribute("data-open", ""); } } }; div.onkeyup = (event) => { event.preventDefault(); if (event.keyCode === 13) { div.click(); } }; document.addEventListener("click", (e) => { if (div.hasAttribute("data-open")) { div.removeAttribute("data-open"); } }); const width = Math.max( ...Array.from(options).map((e) => { span.innerText = e.label; return div.offsetWidth; }), ); console.log(width); div.style.width = `${width}px`; } document.forms[0].onsubmit = (e) => { const data = new FormData(this); e.preventDefault(); submit.innerText = JSON.stringify([...data.entries()]); };