Afficher les étiquettes de liste de données mais soumettre la valeur réelle

98

Actuellement, l' <datalist>élément HTML5 est pris en charge dans la plupart des principaux navigateurs (à l'exception de Safari) et semble être un moyen intéressant d'ajouter des suggestions à une entrée.

Cependant, il semble y avoir des divergences entre la mise en œuvre de l' valueattribut et le texte interne sur le <option>. Par exemple:

<input list="answers" name="answer">
<datalist id="answers">
  <option value="42">The answer</option>
</datalist>

Ceci est géré différemment par différents navigateurs:

Chrome et Opera:
Datalist dans Chrome / Opera

FireFox et IE 11:
Datalist dans FireFox

Après en avoir sélectionné un, l'entrée est remplie avec la valeur et non avec le texte interne. Je veux seulement que l'utilisateur voit le texte ("La réponse") dans la liste déroulante et dans l'entrée, mais passe la valeur 42lors de la soumission, comme le selectferait.

Comment puis-je faire en sorte que tous les navigateurs aient la liste déroulante pour afficher les étiquettes (texte intérieur) du <option>s, mais envoyer l' valueattribut lorsque le formulaire est soumis?

Stéphan Muller
la source
1
Je pense que Firefox et IE11 ont tort ici; selon les spécifications One et Two , il semble que cela value=""devrait prévaloir sur une chaîne dans les balises, chaque fois qu'il y a une value=""déclaration. La suggestion serait donc de faire de "la réponse" votre attribut de valeur.
TylerH
1
@TylerH Non - Firefox et IE11 sont corrects ici: l'attribut label fournit une étiquette pour l'élément. L'étiquette d'un élément option est la valeur de l'attribut de contenu d'étiquette, s'il y en a un, ou, s'il n'y en a pas, la valeur de l'attribut IDL de texte de l'élément. w3.org/TR/html5/forms.html#attr-option-label
easwee
1
@easwee Cela dit simplement que l'étiquette devrait être ce qui est dans l'étiquette s'il y a une étiquette, et que la valeur devrait être ce qui est dans la valeur s'il y a une valeur. Il ne précise pas lequel a la priorité.
TylerH

Réponses:

113

Notez que ce datalistn'est pas la même chose qu'un select. Il permet aux utilisateurs d'entrer une valeur personnalisée qui ne figure pas dans la liste, et il serait impossible d'extraire une valeur alternative pour une telle entrée sans la définir au préalable.

Les moyens possibles de gérer les entrées utilisateur sont de soumettre la valeur entrée telle quelle, de soumettre une valeur vide ou d'empêcher la soumission. Cette réponse ne gère que les deux premières options.

Si vous souhaitez interdire entièrement la saisie utilisateur, ce selectserait peut -être un meilleur choix.


Pour afficher uniquement la valeur de texte de optiondans la liste déroulante, nous utilisons le texte interne pour cela et omettons l' valueattribut. La valeur réelle que nous voulons envoyer est stockée dans un data-valueattribut personnalisé :

Pour soumettre cela, data-valuenous devons utiliser un fichier <input type="hidden">. Dans ce cas, nous omettons le name="answer"sur l'entrée normale et le déplaçons vers la copie cachée.

<input list="suggestionList" id="answerInput">
<datalist id="suggestionList">
    <option data-value="42">The answer</option>
</datalist>
<input type="hidden" name="answer" id="answerInput-hidden">

De cette façon, lorsque le texte de l'entrée d'origine change, nous pouvons utiliser javascript pour vérifier si le texte est également présent dans le datalistet le récupérer data-value. Cette valeur est insérée dans l'entrée masquée et soumise.

document.querySelector('input[list]').addEventListener('input', function(e) {
    var input = e.target,
        list = input.getAttribute('list'),
        options = document.querySelectorAll('#' + list + ' option'),
        hiddenInput = document.getElementById(input.getAttribute('id') + '-hidden'),
        inputValue = input.value;

    hiddenInput.value = inputValue;

    for(var i = 0; i < options.length; i++) {
        var option = options[i];

        if(option.innerText === inputValue) {
            hiddenInput.value = option.getAttribute('data-value');
            break;
        }
    }
});

L'identifiant answeret answer-hiddenl'entrée normale et cachée sont nécessaires pour que le script sache quelle entrée appartient à quelle version cachée. De cette façon, il est possible d'avoir plusieurs inputs sur la même page avec un ou plusieurs datalists fournissant des suggestions.

Toute entrée utilisateur est soumise telle quelle. Pour soumettre une valeur vide lorsque l'entrée utilisateur n'est pas présente dans la liste de données, passez hiddenInput.value = inputValueàhiddenInput.value = ""


Exemples de travail avec jsFiddle: javascript et jQuery

Stéphan Muller
la source
2
comment géreriez-vous les options avec le même texte interne mais des valeurs de données différentes? par exemple jsfiddle.net/7k3sovht/78
bigbearzhu
1
Pour autant que je sache, il n'y a aucun moyen de différencier les deux options. Puisqu'il n'y a pas d'événement utilisateur à écouter, il est impossible de savoir lequel des deux ils ont réellement sélectionné; tout ce que nous savons, c'est quelle valeur a fini dans le champ de saisie.
Stephan Muller
@StephanMuller excellente réponse pour quelqu'un qui apprend plus de HTML5 comme moi, merci! Comment effaceriez-vous la zone de texte de la démo après la soumission?
Chris22
Pure jquery document.querySelector ('input [liste]'). AddEventListener ('input', function (e) {var value = $ (this) .val (); var list = $ (this) .attr ('list' ); var id = $ (this) .attr ('id'); $ ('#' + list + 'option'). each (function () {if ($ (this) .val () == value) { $ ('#' + id + '- caché'). val ($ (this) .attr ('data-value'));}});});
Piotr Kazuś
@StephanMuller merci pour le partage, très utile, je cours comme vous avez codé, mais j'obtiens la valeur des données juste en utilisant le retour arrière dans l'entrée, pas lors de la soumission ou même de la sélection d'un élément différent, qu'est-ce qui peut ne pas aller?
digitai
47

La solution que j'utilise est la suivante:

<input list="answers" id="answer">
<datalist id="answers">
  <option data-value="42" value="The answer">
</datalist>

Accédez ensuite à la valeur à envoyer au serveur en utilisant JavaScript comme ceci:

var shownVal = document.getElementById("answer").value;
var value2send = document.querySelector("#answers option[value='"+shownVal+"']").dataset.value;


J'espère que cela aide.

Hezbollah Shah
la source
Je reçois une raison indéfinie?
Dejell
1
Dépannage avec console.log à chaque étape. Assurez-vous que vous utilisez la même valeur d'identifiant que celle que vous appelez. Et vérifiez aussi les demi-cylindres.
Hezbullah Shah
1
cette solution est une si bonne idée! - J'ai pu, assez simplement, mettre en œuvre cette solution en quelques minutes. j'ai d'abord mis à jour mon inputélément avec l'attribut data-value="1". Ensuite, j'ai écrit le bloc suivant aux endroits où la valeur était saisie et utilisée:if (typeof $(this).attr('data-value') != 'undefined') { var id = $(this).attr('name'); val = $('#'+id).find('option[value="'+val+'"]').attr('data-value'); }
WEBjuju
1
Mais que faire si vous avez deux légendes égales avec des valeurs de données différentes
Richard Aguirre
1
Merci Hezbullah Shah, vous avez vraiment sauvé tant d'heures
ABODE
5

Je me rends compte que c'est peut-être un peu tard, mais je suis tombé dessus et je me demandais comment gérer des situations avec plusieurs valeurs identiques, mais des clés différentes (selon le commentaire de bigbearzhu).

J'ai donc légèrement modifié la réponse de Stephan Muller:

Une liste de données avec des valeurs non uniques:

<input list="answers" name="answer" id="answerInput">
<datalist id="answers">
  <option value="42">The answer</option>
  <option value="43">The answer</option>
  <option value="44">Another Answer</option>
</datalist>
<input type="hidden" name="answer" id="answerInput-hidden">

Lorsque l'utilisateur sélectionne une option, le navigateur remplace input.valuepar le valuede l' datalistoption au lieu du innerText.

Le code suivant recherche ensuite un optionavec cela value, le pousse dans le champ masqué et remplace le input.valuepar le innerText.

document.querySelector('#answerInput').addEventListener('input', function(e) {
    var input = e.target,   
        list = input.getAttribute('list'),
        options = document.querySelectorAll('#' + list + ' option[value="'+input.value+'"]'),
        hiddenInput = document.getElementById(input.getAttribute('id') + '-hidden');

    if (options.length > 0) {
      hiddenInput.value = input.value;
      input.value = options[0].innerText;
      }

});

En conséquence, l'utilisateur voit tout ce que dit l'option innerText, mais l'identifiant unique de option.valueest disponible lors de la soumission du formulaire. Démo jsFiddle

cobbystreet
la source
Bel ajout :)
Stephan Muller
1

En cliquant sur le bouton de recherche, vous pouvez le trouver sans boucle.
Ajoutez simplement à l'option un attribut avec la valeur dont vous avez besoin (comme id) et recherchez-le spécifique.

$('#search_wrapper button').on('click', function(){
console.log($('option[value="'+ 
$('#autocomplete_input').val() +'"]').data('value'));
})
Gal Albalak
la source
-11

En utilisant PHP, j'ai trouvé un moyen assez simple de le faire. Les gars, utilisez quelque chose comme ça

<input list="customers" name="customer_id" required class="form-control" placeholder="Customer Name">
            <datalist id="customers">
                <?php 
    $querySnamex = "SELECT * FROM `customer` WHERE fname!='' AND lname!='' order by customer_id ASC";
    $resultSnamex = mysqli_query($con,$querySnamex) or die(mysql_error());

                while ($row_this = mysqli_fetch_array($resultSnamex)) {
                    echo '<option data-value="'.$row_this['customer_id'].'">'.$row_this['fname'].' '.$row_this['lname'].'</option>
                    <input type="hidden" name="customer_id_real" value="'.$row_this['customer_id'].'" id="answerInput-hidden">';
                }

                 ?>
            </datalist>

Le code ci-dessus permet au formulaire de porter l'identifiant de l'option également sélectionnée.

23r0
la source
Cela ressemble à un désordre et sent l'injection SQL. Ne fais pas ça.
Fusseldieb
@ 23r0, quelques commentaires sur les raisons pour lesquelles cela a pu être rejeté; cette question concerne spécifiquement le code frontal. Inclure du code backend (comme PHP) peut être inutile et déroutant. En ce qui concerne le HTML, je pense que vous n'obtiendrez jamais que la dernière valeur pour customer_id_realsoumis, pas la valeur sélectionnée.
John