Je ne me contente pas de solutions HTML / CSS uniquement, j'ai donc décidé de créer une personnalisation en select
utilisant JavaScript.
C'est quelque chose que j'ai écrit au cours des 30 dernières minutes, il peut donc être encore amélioré.
Tout ce que vous avez à faire est de créer une liste simple avec peu d'attributs de données. Le code transforme automatiquement la liste en une liste déroulante sélectionnable. Il ajoute également un caché input
pour contenir la valeur sélectionnée, de sorte qu'il peut être utilisé dans un formulaire.
Contribution:
<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>
Production:
<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>
Le texte de l'élément censé être un espace réservé est grisé. L'espace réservé est sélectionnable, au cas où l'utilisateur souhaite revenir sur son choix. En utilisant également CSS, tous les inconvénients de select
peuvent être surmontés (par exemple, l'incapacité de styliser les options).
// 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>
Mise à jour : j'ai amélioré cela (sélection en utilisant les touches haut / bas / entrée), j'ai rangé un peu la sortie et je l'ai transformée en objet. Sortie courant:
<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>
Initialisation:
new Liselect(document.getElementsByTagName("ul")[0]);
Pour un examen plus approfondi: JSFiddle , GitHub (renommé).
Mise à jour: je l'ai réécrit. Au lieu d'utiliser une liste, nous pouvons simplement utiliser une sélection. De cette façon, cela fonctionnera même sans JavaScript (au cas où il serait désactivé).
Contribution:
<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]);
Production:
<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 .