Nie jestem zadowolony z rozwiązań opartych tylko na HTML / CSS, więc zdecydowałem się utworzyć niestandardowy select
za pomocą JavaScript.
Jest to coś, co napisałem w ciągu ostatnich 30 minut, dzięki czemu można go jeszcze ulepszyć.
Wszystko, co musisz zrobić, to stworzyć prostą listę z kilkoma atrybutami danych. Kod automatycznie zamienia listę w rozwijalne menu do wyboru. Dodaje również ukryty input
do przechowywania wybranej wartości, dzięki czemu można go użyć w formularzu.
Wejście:
<ul class="select" data-placeholder="Role" data-name="role">
<li data-value="admin">Administrator</li>
<li data-value="mod">Moderator</li>
<li data-value="user">User</li>
</ul>
Wynik:
<div class="ul-select-container">
<input type="hidden" name="role" class="hidden">
<div class="selected placeholder">
<span class="text">Role</span>
<span class="icon">▼</span>
</div>
<ul class="select" data-placeholder="Role" data-name="role">
<li class="placeholder">Role</li>
<li data-value="admin">Administrator</li>
<li data-value="mod">Moderator</li>
<li data-value="user">User</li>
</ul>
</div>
Tekst elementu, który ma być symbolem zastępczym, jest wyszarzony. Symbol zastępczy można wybrać na wypadek, gdyby użytkownik chciał cofnąć swój wybór. Również za pomocą CSS select
można przezwyciężyć wszystkie wady (np. Niemożność stylizacji opcji).
// Helper function to create elements faster/easier
// https://github.com/akinuri/js-lib/blob/master/element.js
var elem = function(tagName, attributes, children, isHTML) {
let parent;
if (typeof tagName == "string") {
parent = document.createElement(tagName);
} else if (tagName instanceof HTMLElement) {
parent = tagName;
}
if (attributes) {
for (let attribute in attributes) {
parent.setAttribute(attribute, attributes[attribute]);
}
}
var isHTML = isHTML || null;
if (children || children == 0) {
elem.append(parent, children, isHTML);
}
return parent;
};
elem.append = function(parent, children, isHTML) {
if (parent instanceof HTMLTextAreaElement || parent instanceof HTMLInputElement) {
if (children instanceof Text || typeof children == "string" || typeof children == "number") {
parent.value = children;
} else if (children instanceof Array) {
children.forEach(function(child) {
elem.append(parent, child);
});
} else if (typeof children == "function") {
elem.append(parent, children());
}
} else {
if (children instanceof HTMLElement || children instanceof Text) {
parent.appendChild(children);
} else if (typeof children == "string" || typeof children == "number") {
if (isHTML) {
parent.innerHTML += children;
} else {
parent.appendChild(document.createTextNode(children));
}
} else if (children instanceof Array) {
children.forEach(function(child) {
elem.append(parent, child);
});
} else if (typeof children == "function") {
elem.append(parent, children());
}
}
};
// Initialize all selects on the page
$("ul.select").each(function() {
var parent = this.parentElement;
var refElem = this.nextElementSibling;
var container = elem("div", {"class": "ul-select-container"});
var hidden = elem("input", {"type": "hidden", "name": this.dataset.name, "class": "hidden"});
var selected = elem("div", {"class": "selected placeholder"}, [
elem("span", {"class": "text"}, this.dataset.placeholder),
elem("span", {"class": "icon"}, "▼", true),
]);
var placeholder = elem("li", {"class": "placeholder"}, this.dataset.placeholder);
this.insertBefore(placeholder, this.children[0]);
container.appendChild(hidden);
container.appendChild(selected);
container.appendChild(this);
parent.insertBefore(container, refElem);
});
// Update necessary elements with the selected option
$(".ul-select-container ul li").on("click", function() {
var text = this.innerText;
var value = this.dataset.value || "";
var selected = this.parentElement.previousElementSibling;
var hidden = selected.previousElementSibling;
hidden.value = selected.dataset.value = value;
selected.children[0].innerText = text;
if (this.classList.contains("placeholder")) {
selected.classList.add("placeholder");
} else {
selected.classList.remove("placeholder");
}
selected.parentElement.classList.remove("visible");
});
// Open select dropdown
$(".ul-select-container .selected").on("click", function() {
if (this.parentElement.classList.contains("visible")) {
this.parentElement.classList.remove("visible");
} else {
this.parentElement.classList.add("visible");
}
});
// Close select when focus is lost
$(document).on("click", function(e) {
var container = $(e.target).closest(".ul-select-container");
if (container.length == 0) {
$(".ul-select-container.visible").removeClass("visible");
}
});
.ul-select-container {
width: 200px;
display: table;
position: relative;
margin: 1em 0;
}
.ul-select-container.visible ul {
display: block;
padding: 0;
list-style: none;
margin: 0;
}
.ul-select-container ul {
background-color: white;
border: 1px solid hsla(0, 0%, 60%);
border-top: none;
-webkit-user-select: none;
display: none;
position: absolute;
width: 100%;
z-index: 999;
}
.ul-select-container ul li {
padding: 2px 5px;
}
.ul-select-container ul li.placeholder {
opacity: 0.5;
}
.ul-select-container ul li:hover {
background-color: dodgerblue;
color: white;
}
.ul-select-container ul li.placeholder:hover {
background-color: rgba(0, 0, 0, .1);
color: initial;
}
.ul-select-container .selected {
background-color: white;
padding: 3px 10px 4px;
padding: 2px 5px;
border: 1px solid hsla(0, 0%, 60%);
-webkit-user-select: none;
}
.ul-select-container .selected {
display: flex;
justify-content: space-between;
}
.ul-select-container .selected.placeholder .text {
color: rgba(0, 0, 0, .5);
}
.ul-select-container .selected .icon {
font-size: .7em;
display: flex;
align-items: center;
opacity: 0.8;
}
.ul-select-container:hover .selected {
border: 1px solid hsla(0, 0%, 30%);
}
.ul-select-container:hover .selected .icon {
opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="select" data-placeholder="Role" data-name="role">
<li data-value="admin">Administrator</li>
<li data-value="mod">Moderator</li>
<li data-value="user">User</li>
</ul>
<ul class="select" data-placeholder="Sex" data-name="sex">
<li data-value="male">Male</li>
<li data-value="female">Female</li>
</ul>
Aktualizacja : Ulepszyłem to (wybór za pomocą klawiszy góra / dół / enter), trochę posprzątałem wynik i zamieniłem go w obiekt. Wyjście prądowe:
<div class="li-select-container">
<input type="text" readonly="" placeholder="Role" title="Role">
<span class="arrow">▼</span>
<ul class="select">
<li class="placeholder">Role</li>
<li data-value="admin">Administrator</li>
<li data-value="mod">Moderator</li>
<li data-value="user">User</li>
</ul>
</div>
Inicjalizacja:
new Liselect(document.getElementsByTagName("ul")[0]);
Do dalszego badania: JSFiddle , GitHub (przemianowany).
Aktualizacja: przepisałem to ponownie. Zamiast korzystać z listy, możemy po prostu użyć opcji select. W ten sposób będzie działać nawet bez JavaScript (w przypadku, gdy jest wyłączony).
Wejście:
<select name="role" data-placeholder="Role" required title="Role">
<option value="admin">Administrator</option>
<option value="mod">Moderator</option>
<option>User</option>
</select>
new Advancelect(document.getElementsByTagName("select")[0]);
Wynik:
<div class="advanced-select">
<input type="text" readonly="" placeholder="Role" title="Role" required="" name="role">
<span class="arrow">▼</span>
<ul>
<li class="placeholder">Role</li>
<li data-value="admin">Administrator</li>
<li data-value="mod">Moderator</li>
<li>User</li>
</ul>
</div>
JSFiddle , GitHub .