Mostrar etiquetas de lista de datos pero enviar el valor real

98

Actualmente, el <datalist>elemento HTML5 es compatible con la mayoría de los principales navegadores (excepto Safari) y parece una forma interesante de agregar sugerencias a una entrada.

Sin embargo, parece haber algunas discrepancias entre la implementación del valueatributo y el texto interno en <option>. Por ejemplo:

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

Esto se maneja de manera diferente por diferentes navegadores:

Chrome y Opera:
Lista de datos en Chrome / Opera

FireFox e IE 11:
Datalist en FireFox

Después de seleccionar uno, la entrada se llena con el valor y no con el texto interno. Solo quiero que el usuario vea el texto ("La respuesta") en el menú desplegable y en la entrada, pero pase el valor 42al enviar, como lo selectharía.

¿Cómo puedo hacer que todos los navegadores tengan la lista desplegable que muestre las etiquetas (texto interno) de los <option>correos electrónicos, pero envíen el valueatributo cuando se envíe el formulario?

Stephan Muller
fuente
1
Creo que Firefox e IE11 están mal aquí; de acuerdo con las especificaciones Uno y Dos , parece que value=""debería tener prioridad sobre una cadena dentro de las etiquetas, siempre que haya un value=""declarado. Entonces, la sugerencia sería hacer de "la respuesta" su atributo de valor.
TylerH
1
@TylerH No: Firefox e IE11 son correctos aquí: el atributo de etiqueta proporciona una etiqueta para el elemento. La etiqueta de un elemento de opción es el valor del atributo de contenido de la etiqueta, si hay uno, o, si no lo hay, el valor del atributo IDL de texto del elemento. w3.org/TR/html5/forms.html#attr-option-label
easwee
1
@easwee Eso solo dice que la etiqueta debe ser lo que está en la etiqueta si hay una etiqueta, y que el valor debe ser lo que está en el valor si hay un valor. No especifica cuál tiene prioridad.
TylerH

Respuestas:

113

Tenga en cuenta que datalistno es lo mismo que a select. Permite a los usuarios ingresar un valor personalizado que no está en la lista, y sería imposible obtener un valor alternativo para dicha entrada sin definirlo primero.

Las posibles formas de manejar la entrada del usuario son enviar el valor ingresado como está, enviar un valor en blanco o evitar el envío. Esta respuesta maneja solo las dos primeras opciones.

Si desea no permitir la entrada del usuario por completo, tal vez selectsea ​​una mejor opción.


Para mostrar solo el valor de texto de optionen el menú desplegable, usamos el texto interno y omitimos el valueatributo. El valor real que queremos enviar se almacena en un data-valueatributo personalizado :

Para enviar esto data-valuetenemos que usar un <input type="hidden">. En este caso, dejamos fuera la name="answer"entrada normal y la movemos a la copia oculta.

<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 esta manera, cuando el texto en la entrada original cambia, podemos usar javascript para verificar si el texto también está presente en datalisty recuperar su data-value. Ese valor se inserta en la entrada oculta y se envía.

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;
        }
    }
});

La identificación answery answer-hiddenla entrada normal y oculta son necesarios para que el script sepa qué entrada pertenece a qué versión oculta. De esta manera, es posible tener varios correos inputelectrónicos en la misma página con uno o más datalistcorreos electrónicos que brinden sugerencias.

Cualquier entrada de usuario se envía tal cual. Para enviar un valor vacío cuando la entrada del usuario no está presente en la lista de datos, cambie hiddenInput.value = inputValueahiddenInput.value = ""


Ejemplos de trabajo de jsFiddle: JavaScript simple y jQuery

Stephan Muller
fuente
2
¿Cómo manejaría las opciones con el mismo texto interno pero con diferentes valores de datos? por ejemplo, jsfiddle.net/7k3sovht/78
bigbearzhu
1
Por lo que puedo decir, no hay forma de diferenciar entre las dos opciones. Debido a que no hay ningún evento de usuario para escuchar, es imposible saber cuál de los dos seleccionaron realmente; todo lo que sabemos es qué valor terminó en el campo de entrada.
Stephan Muller
@StephanMuller gran respuesta para alguien que está aprendiendo más HTML5 como yo, ¡gracias! ¿Cómo limpiaría el cuadro de texto de la demostración después de enviarla?
Chris22
Pure jquery document.querySelector ('input [list]'). 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 + '- oculto'). val ($ (esto) .attr ('valor-datos'));}});});
Piotr Kazuś
@StephanMuller gracias por compartir, muy útil, estoy corriendo como usted codificó, sin embargo, obtengo el valor de los datos solo cuando uso el retroceso en la entrada, no al enviar o incluso seleccionar un elemento diferente, ¿qué puede estar mal?
digitai
47

La solución que utilizo es la siguiente:

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

Luego acceda al valor que se enviará al servidor usando JavaScript como este:

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


Espero eso ayude.

Hezbullah Shah
fuente
Estoy indefinido por alguna razón?
Dejell
1
Solucione problemas con console.log en cada paso. Asegúrese de estar utilizando el mismo valor de identificación al que está llamando. Y compruebe también los semicolones.
Hezbullah Shah
1
¡esta solución es una gran idea! - Pude, de manera bastante simple, implementar esta solución en minutos. primero actualicé mi inputelemento con atributo data-value="1". Luego escribí el siguiente bloque en los lugares donde se tomó y usó el valor:if (typeof $(this).attr('data-value') != 'undefined') { var id = $(this).attr('name'); val = $('#'+id).find('option[value="'+val+'"]').attr('data-value'); }
WEBjuju
1
Pero, ¿qué pasa si tiene dos subtítulos iguales con algunos valores de datos diferentes
Richard Aguirre
1
Gracias Hezbullah Shah, realmente ahorraste tantas horas
ABODE
5

Me doy cuenta de que esto puede ser un poco tarde, pero me topé con esto y me preguntaba cómo manejar situaciones con múltiples valores idénticos, pero claves diferentes (según el comentario de bigbearzhu).

Así que modifiqué ligeramente la respuesta de Stephan Muller:

Un datalist con valores no únicos:

<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">

Cuando el usuario selecciona una opción, el navegador reemplaza input.valuecon el valuede la datalistopción en lugar delinnerText .

El siguiente código luego busca un optioncon eso value, lo empuja al campo oculto y lo reemplaza input.valuecon el 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;
      }

});

Como consecuencia, el usuario ve lo que innerTextdice la opción , pero la identificación única de option.valueestá disponible al enviar el formulario. Demostración jsFiddle

cobbystreet
fuente
Buena adición :)
Stephan Muller
1

Al hacer clic en el botón de búsqueda, puede encontrarlo sin un bucle.
Simplemente agregue a la opción un atributo con el valor que necesita (me gusta id) y búsquelo específico.

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

Usando PHP he encontrado una forma bastante simple de hacer esto. Chicos, usen algo como esto

<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>

El código de arriba permite que el formulario lleve la identificación de la opción también seleccionada.

23r0
fuente
Eso parece un desastre y huele a inyección SQL. No hagas eso.
Fusseldieb
@ 23r0, algunos comentarios sobre por qué esto puede haber sido rechazado; esta pregunta trata específicamente sobre el código de la interfaz. Incluir código backend (como PHP) puede resultar innecesario y confuso. Con respecto al HTML, espero que solo obtenga el último valor customer_id_realenviado, no el valor seleccionado.
Juan