Je voudrais créer un champ de texte avec une liste déroulante qui permet à l'utilisateur de choisir des valeurs prédéfinies. L'utilisateur doit également pouvoir saisir une nouvelle valeur ou en sélectionner une prédéfinie dans une liste déroulante. Je sais que je peux utiliser deux widgets pour cela, mais dans mon application, ce serait plus ergonomique s'il était unifié dans un seul widget.
Existe-t-il un widget standard ou dois-je utiliser un javascript tiers?
Et la portabilité du navigateur?
La meilleure façon de procéder est probablement d'utiliser une bibliothèque tierce.
Il y a une implémentation de ce que vous recherchez dans jQuery UI et dans dojo . jQuery est plus populaire, mais dojo vous permet de définir de manière déclarative des widgets en HTML, ce qui ressemble plus à ce que vous recherchez.
Celui que vous utiliserez dépendra de votre style, mais les deux sont développés pour le travail entre navigateurs, et les deux seront mis à jour plus souvent que le copier-coller de code.
Vous pouvez accomplir cela en utilisant le <datalist>
tag en HTML5.
<input type="text" name="product" list="productName"/>
<datalist id="productName">
<option value="Pen">Pen</option>
<option value="Pencil">Pencil</option>
<option value="Paper">Paper</option>
</datalist>
Si vous double-cliquez sur le texte saisi dans le navigateur, une liste avec l'option définie apparaîtra.
Cela peut être réalisé à l'aide de HTML simple, CSS et JQuery. J'ai créé un exemple de page:
$(document).ready(function(){
$(".editableBox").change(function(){
$(".timeTextBox").val($(".editableBox option:selected").html());
});
});
.editableBox {
width: 75px;
height: 30px;
}
.timeTextBox {
width: 54px;
margin-left: -78px;
height: 25px;
border: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<select class="editableBox">
<option value="1">01:00</option>
<option value="2">02:00</option>
<option value="3">03:00</option>
<option value="4">04:00</option>
<option value="5">05:00</option>
<option value="6">06:00</option>
<option value="7">07:00</option>
<option value="8">08:00</option>
<option value="9">09:00</option>
<option value="10">10:00</option>
<option value="11">11:00</option>
<option value="12">12:00</option>
<option value="13">13:00</option>
<option value="14">14:00</option>
<option value="15">15:00</option>
<option value="16">16:00</option>
<option value="17">17:00</option>
<option value="18">18:00</option>
<option value="19">19:00</option>
<option value="20">20:00</option>
<option value="21">21:00</option>
<option value="22">22:00</option>
<option value="23">23:00</option>
<option value="24">24:00</option>
</select>
<input class="timeTextBox" name="timebox" maxlength="5"/>
</div>
Le <select>
tag autorise uniquement l'utilisation d'entrées prédéfinies. La solution typique à votre problème consiste à avoir une entrée intitulée "Autre" et un champ d'édition désactivé (<input type="text"
). Ajoutez du JavaScript pour activer le champ d'édition uniquement lorsque "Autre" est sélectionné.
Il peut être possible de créer en quelque sorte une liste déroulante qui permet une édition directe, mais IMO qui n'en vaut pas la peine. Si c'était le cas, Amazon, Google ou Microsoft le feraient ;-) Faites simplement le travail avec la solution la moins compliquée. C'est plus rapide (votre patron peut aimer ça) et généralement plus facile à entretenir (vous pouvez aimer ça).
Implémentation très simple (uniquement des fonctionnalités de base) basée sur CSS et une ligne de code JS
<div class="dropdown">
<input type="text" />
<select onchange="this.previousElementSibling.value=this.value; this.previousElementSibling.focus()">
<option>This is option 1</option>
<option>Option 2</option>
</select>
</div>
Veuillez noter: il utilise previousElementSibling qui n'est pas pris en charge dans les anciens navigateurs (sous IE9)
.dropdown {
position: relative;
width: 200px;
}
.dropdown select
{
width: 100%;
}
.dropdown > * {
box-sizing: border-box;
height: 1.5em;
}
.dropdown select {
}
.dropdown input {
position: absolute;
width: calc(100% - 20px);
}
Le voici sur JSFiddle
ComboBox avec TextBox (pour les valeurs prédéfinies ainsi que les valeurs définies par l'utilisateur.)
Un peu de CSS et vous avez terminé violon
<div style="position: absolute;top: 32px; left: 430px;" id="outerFilterDiv">
<input name="filterTextField" type="text" id="filterTextField" tabindex="2" style="width: 140px;
position: absolute; top: 1px; left: 1px; z-index: 2;border:none;" />
<div style="position: absolute;" id="filterDropdownDiv">
<select name="filterDropDown" id="filterDropDown" tabindex="1000"
onchange="DropDownTextToBox(this,'filterTextField');" style="position: absolute;
top: 0px; left: 0px; z-index: 1; width: 165px;">
<option value="-1" selected="selected" disabled="disabled">-- Select Column Name --</option>
</select>
Je ne suis pas sûr qu'il existe un moyen de le faire automatiquement sans Javascript.
Ce dont vous avez besoin est quelque chose qui s'exécute du côté du navigateur pour renvoyer votre formulaire au serveur quand l'utilisateur fait une sélection - donc, javascript.
Assurez-vous également que vous disposez d'un autre moyen (c'est-à-dire un bouton d'envoi) pour ceux qui ont désactivé Javascript.
Un bon exemple: Combo-Box Viewer
J'avais même une combo-box plus sophistiquée hier, avec ceci dhtmlxCombo , en utilisant ajax pour récupérer des valeurs pertinentes parmi une grande quantité de données.
Une zone de liste déroulante est malheureusement quelque chose qui a été omis des spécifications HTML.
La seule façon de le gérer, malheureusement, est de rouler le vôtre ou d'en utiliser un pré-construit. Celui-ci semble assez simple. J'utilise celui-ci pour une application open source, mais malheureusement, vous devez payer pour une utilisation commerciale.
HTML n'a pas de liste déroulante modifiable intégrée ou de liste déroulante, mais j'ai implémenté une solution principalement CSS dans n article .
Vous pouvez voir une démo complète ici mais en résumé, écrivez du HTML comme ceci:
<span class="combobox withtextlist">
<input value="Fruit">
<span tabindex="-1" class="downarrow"></span>
<select size="10" class="sticky">
<option>Apple</option>
<option>Banana</option>
<option>Cherry</option>
<option>Dewberry</option>
</select>
</span>
Et utilisez CSS comme celui-ci pour le styliser (il est conçu pour les deux zones de liste déroulante, qui ont un bouton flèche vers le bas ▾, et des menus déroulants qui s'ouvrent lorsque vous cliquez dessus et peuvent être stylés différemment):
/* ------------------------------------------ */
/* ----- combobox / dropdown list styling */
/* ------------------------------------------ */
.combobox {
/* Border slightly darker than Chrome's <select>, slightly lighter than FireFox's */
border: 1px solid #999;
padding-right: 1.25em; /* leave room for ▾ */
}
.dropdown, .combobox {
/* "relative" and "inline-block" (or just "block") are needed
here so that "absolute" works correctly in children */
position: relative;
display: inline-block;
}
.combobox > .downarrow, .dropdown > .downarrow {
/* ▾ Outside normal flow, relative to container */
display: inline-block;
position: absolute;
top: 0;
bottom: 0;
right: 0;
width: 1.25em;
cursor: default;
nav-index: -1; /* nonfunctional in most browsers */
border-width: 0px; /* disable by default */
border-style: inherit; /* copy parent border */
border-color: inherit; /* copy parent border */
}
/* Add a divider before the ▾ down arrow in non-dropdown comboboxes */
.combobox:not(.dropdown) > .downarrow {
border-left-width: 1px;
}
/* Auto-down-arrow if one is not provided */
.downarrow:empty::before {
content: '▾';
}
.downarrow::before, .downarrow > *:only-child {
text-align: center;
/* vertical centering trick */
position: relative;
top: 50%;
display: block; /* transform requires block/inline-block */
transform: translateY(-50%);
}
.combobox > input {
border: 0
}
.dropdown > *:last-child,
.combobox > *:last-child {
/* Using `display:block` here has two desirable effects:
(1) Accessibility: it lets input widgets in the dropdown to
be selected with the tab key when the dropdown is closed.
(2) It lets the opacity transition work.
But it also makes the contents visible, which is undesirable
before the list drops down. To compensate, use `opacity: 0`
and disable mouse pointer events. Another side effect is that
the user can select and copy the contents of the hidden list,
but don't worry, the selected content is invisible. */
display: block;
opacity: 0;
pointer-events: none;
transition: 0.4s; /* fade out */
position: absolute;
left: 0;
top: 100%;
border: 1px solid #888;
background-color: #fff;
box-shadow: 1px 2px 4px 1px #666;
box-shadow: 1px 2px 4px 1px #4448;
z-index: 9999;
min-width: 100%;
box-sizing: border-box;
}
/* List of situations in which to show the dropdown list.
- Focus dropdown or non-last child of it => show last-child
- Focus .downarrow of combobox => show last-child
- Stay open for focus in last child, unless .less-sticky
- .sticky last child stays open on hover
- .less-sticky stays open on hover, ignores focus in last-child */
.dropdown:focus > *:last-child,
.dropdown > *:focus ~ *:last-child,
.combobox > .downarrow:focus ~ *:last-child,
.combobox > .sticky:last-child:hover,
.dropdown > .sticky:last-child:hover,
.combobox > .less-sticky:last-child:hover,
.dropdown > .less-sticky:last-child:hover,
.combobox > *:last-child:focus:not(.less-sticky),
.dropdown > *:last-child:focus:not(.less-sticky) {
display: block;
opacity: 1;
transition: 0.15s;
pointer-events: auto;
}
/* focus-within not supported by Edge/IE. Unsupported selectors cause
the entire block to be ignored, so we must repeat all styles for
focus-within separately. */
.combobox > *:last-child:focus-within:not(.less-sticky),
.dropdown > *:last-child:focus-within:not(.less-sticky) {
display: block;
opacity: 1;
transition: 0.15s;
pointer-events: auto;
}
/* detect Edge/IE and behave if though less-sticky is on for all
dropdowns (otherwise links won't be clickable) */
@supports (-ms-ime-align:auto) {
.dropdown > *:last-child:hover {
display: block;
opacity: 1;
pointer-events: auto;
}
}
/* detect IE and do the same thing. */
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.dropdown > *:last-child:hover {
display: block;
opacity: 1;
pointer-events: auto;
}
}
.dropdown:not(.sticky) > *:not(:last-child):focus,
.downarrow:focus, .dropdown:focus {
pointer-events: none; /* Causes second click to close */
}
.downarrow:focus {
outline: 2px solid #8BF; /* Edge/IE can't do outline transparency */
outline: 2px solid #48F8;
}
/* ---------------------------------------------- */
/* Optional extra styling for combobox / dropdown */
/* ---------------------------------------------- */
*, *:before, *:after {
/* See https://css-tricks.com/international-box-sizing-awareness-day/ */
box-sizing: border-box;
}
.combobox > *:first-child {
display: inline-block;
width: 100%;
box-sizing: border-box; /* so 100% includes border & padding */
}
/* `.combobox:focus-within { outline:...}` doesn't work properly
in Firefox because the focus box is expanded to include the
(possibly hidden) drop list. As a workaround, put focus box on
the focused child. It is barely-visible so that it doesn't look
TOO ugly if the child isn't the same size as the parent. It
may be uglier if the first child is not styled as width:100% */
.combobox > *:not(:last-child):focus {
outline: 2px solid #48F8;
}
.combobox {
margin: 5px;
}
Vous avez également besoin de JavaScript pour synchroniser la liste avec la zone de texte:
function parentComboBox(el) {
for (el = el.parentNode; el != null && Array.prototype.indexOf.call(el.classList, "combobox") <= -1;)
el = el.parentNode;
return el;
}
// Uses jQuery
$(".combobox.withtextlist > select").change(function() {
var textbox = parentComboBox(this).firstElementChild;
textbox.value = this[this.selectedIndex].text;
});
$(".combobox.withtextlist > select").keypress(function(e) {
if (e.keyCode == 13) // Enter pressed
parentComboBox(this).firstElementChild.focus(); // Closes the popup
});