Pokaż etykiety danych, ale prześlij rzeczywistą wartość

100

Obecnie <datalist>element HTML5 jest obsługiwany w większości głównych przeglądarek (z wyjątkiem Safari) i wydaje się interesującym sposobem dodawania sugestii do danych wejściowych.

Wydaje się jednak, że istnieją pewne rozbieżności między implementacją valueatrybutu a tekstem wewnętrznym w <option>. Na przykład:

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

Jest to obsługiwane inaczej w różnych przeglądarkach:

Chrome i Opera:
Datalist w Chrome / Opera

FireFox i IE 11:
Datalist w FireFox

Po wybraniu jednego, dane wejściowe są wypełniane wartością, a nie tekstem wewnętrznym. Chcę tylko, aby użytkownik zobaczył tekst („Odpowiedź”) w menu rozwijanym i w danych wejściowych, ale przekazał wartość 42podczas przesyłania, tak jak w selectprzypadku.

Jak sprawić, by wszystkie przeglądarki wyświetlały na liście rozwijanej etykiety (tekst wewnętrzny) <option>, ale wysyłały valueatrybut po przesłaniu formularza?

Stephan Muller
źródło
1
Myślę, że Firefox i IE11 są błędne; zgodnie ze specyfikacją 1 i 2 , wydaje się, że value=""powinno mieć pierwszeństwo przed ciągiem znaków w tagach, gdy tylko jest value=""zadeklarowane. Zatem sugestią byłoby uczynienie „odpowiedzi” swoim atrybutem wartości.
TylerH
1
@TylerH Nie - Firefox i IE11 są poprawne tutaj: Atrybut label zapewnia etykietę dla elementu. Etykieta elementu opcji jest wartością atrybutu zawartości etykiety, jeśli taki istnieje, lub, jeśli go nie ma, wartością atrybutu tekstowego IDL elementu. w3.org/TR/html5/forms.html#attr-option-label
easwee
1
@easwee To po prostu mówi, że etykieta powinna być tym, co jest w etykiecie, jeśli istnieje etykieta, i że wartość powinna być zawarta w wartości, jeśli istnieje wartość. Nie określa, który z nich ma pierwszeństwo.
TylerH

Odpowiedzi:

116

Zwróć na to uwagę datalist to nie to samo, co plik select. Pozwala użytkownikom wprowadzić niestandardową wartość, której nie ma na liście, i niemożliwe byłoby pobranie alternatywnej wartości dla takich danych wejściowych bez uprzedniego zdefiniowania jej.

Możliwe sposoby obsługi danych wejściowych użytkownika to przesłanie wprowadzonej wartości tak, jak jest, przesłanie pustej wartości lub zapobieżenie przesłaniu. Ta odpowiedź dotyczy tylko dwóch pierwszych opcji.

Jeśli chcesz całkowicie zabronić wprowadzania danych przez użytkownika, być może selectbyłby lepszym wyborem.


Aby wyświetlić tylko wartość tekstową optionw menu rozwijanym, używamy do tego tekstu wewnętrznego i pomijamy valueatrybut. Rzeczywista wartość, którą chcemy przesłać, jest przechowywana w niestandardowym data-valueatrybucie:

Aby to przesłać, data-valuemusimy użyć pliku <input type="hidden">. W tym przypadku pomijamy name="answer"zwykłe wejście i przenosimy je do ukrytej kopii.

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

W ten sposób, gdy tekst w oryginalnym wejściu zmieni się, możemy użyć javascript, aby sprawdzić, czy tekst również występuje w pliku datalisti pobrać jego data-value. Ta wartość jest wstawiana do ukrytych danych wejściowych i przesyłana.

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

Identyfikator answeroraz answer-hiddenzwykłe i ukryte dane wejściowe są potrzebne, aby skrypt wiedział, które wejście należy do której ukrytej wersji. W ten sposób możliwe jest umieszczenie wielu inputadresów na tej samej stronie, przy czym jeden lub więcej datalistzawiera sugestie.

Wszelkie dane wejściowe użytkownika są przesyłane bez zmian. Aby przesłać pustą wartość, gdy dane wejściowe użytkownika nie są obecne w danych, zmień hiddenInput.value = inputValuenahiddenInput.value = ""


Działające przykłady jsFiddle: zwykły javascript i jQuery

Stephan Muller
źródło
2
jak poradziłbyś sobie z opcjami z tym samym tekstem wewnętrznym, ale różnymi wartościami danych? np. jsfiddle.net/7k3sovht/78
bigbearzhu
1
O ile wiem, nie ma sposobu, aby odróżnić te dwie opcje. Ponieważ nie istnieje żadne zdarzenie użytkownika do odsłuchania, nie można wiedzieć, które z nich faktycznie wybrał; wiemy tylko, jaka wartość znalazła się w polu wejściowym.
Stephan Muller
@StephanMuller świetna odpowiedź dla kogoś, kto uczy się HTML5 tak jak ja, dzięki! Jak wyczyściłbyś pole tekstowe dla wersji demonstracyjnej po przesłaniu?
Chris22
Czysty dokument jquery.querySelector ('input [lista]'). 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 + '- ukryte'). val ($ (this) .attr ('wartość-danych'));}});});
Piotr Kazuś
@StephanMuller dzięki za udostępnienie, bardzo przydatne, działam tak, jak zakodowałeś, jednak otrzymuję wartość danych tylko podczas używania backspace w danych wejściowych, a nie podczas przesyłania lub nawet wybierania innego elementu, co może być nie tak?
digitai
49

Rozwiązanie, którego używam, jest następujące:

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

Następnie uzyskaj dostęp do wartości, która ma zostać wysłana na serwer, używając JavaScript w następujący sposób:

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


Mam nadzieję, że to pomoże.

Hezbollah Shah
źródło
Z jakiegoś powodu jestem niezdefiniowany?
Dejell
1
Rozwiązywanie problemów z console.log na każdym kroku. Upewnij się, że używasz tej samej wartości identyfikatora, którą dzwonisz. Sprawdź też średniki.
Hezbollah Shah
1
to rozwiązanie to świetny pomysł! - udało mi się po prostu wdrożyć to rozwiązanie w kilka minut. najpierw zaktualizowałem mój inputelement atrybutem data-value="1". Następnie napisałem następujący blok w miejscach, w których wartość była if (typeof $(this).attr('data-value') != 'undefined') { var id = $(this).attr('name'); val = $('#'+id).find('option[value="'+val+'"]').attr('data-value'); }
chwytana
1
Ale co, jeśli masz dwa podpisy równe z różnymi wartościami danych
Richard Aguirre
1
Dzięki Hezbollah Shah, naprawdę zaoszczędziłeś tyle godzin
ABODE
5

Zdaję sobie sprawę, że może to być trochę za późno, ale natknąłem się na to i zastanawiałem się, jak radzić sobie z sytuacjami z wieloma identycznymi wartościami, ale różnymi kluczami (zgodnie z komentarzem bigbearzhu).

Dlatego nieznacznie zmodyfikowałem odpowiedź Stephana Mullera:

Datalist z nieunikalnymi wartościami:

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

Gdy użytkownik wybierze opcję, zastępuje przeglądarka input.valuez valuetego datalistwariantu zamiastinnerText .

Poniższy kod następnie sprawdza, czy występuje optionz tym value, wypycha go do ukrytego pola i zastępuje input.valuego 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;
      }

});

W konsekwencji użytkownik widzi wszystko, co innerTextmówi opcja , ale unikalny identyfikator option.valuejest dostępny po przesłaniu formularza. Demo jsFiddle

cobbystreet
źródło
Miły dodatek :)
Stephan Muller
1

Po kliknięciu przycisku wyszukiwania można go znaleźć bez pętli.
Po prostu dodaj do opcji atrybut o potrzebnej wartości (lubię id) i wyszukaj go dokładnie.

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

Używając PHP, znalazłem dość prosty sposób na zrobienie tego. Chłopaki, po prostu użyj czegoś takiego

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

Powyższy kod pozwala formularzowi nosić identyfikator również wybranej opcji.

23r0
źródło
To wygląda na bałagan i pachnie jak zastrzyk SQL. Nie rób tego.
Fusseldieb
@ 23r0, garść opinii na temat tego, dlaczego głosowanie nad tym głosowaniem zostało odrzucone; to pytanie dotyczy w szczególności kodu frontendu. Dołączanie kodu zaplecza (takiego jak PHP) może być niepotrzebne i mylące. Jeśli chodzi o HTML, spodziewam się, że zawsze otrzymasz tylko ostatnią wartość customer_id_realprzesłanej, a nie wybraną wartość.
Jan