Autocompletion is een patroon waarmee alle webgebruikers vertrouwd zijn. Wanneer u een zoekopdracht uitvoert, stelt uw zoekmachine termen voor. Wanneer u een nieuw e-mailbericht typt, stelt uw e-mailprogramma ontvangers voor. Deze functionaliteit is echter niet beschikbaar geweest voor webontwikkelaars zonder een niet-triviale hoeveelheid JavaScript. Een van de nieuwe HTML5-elementen, de , brengt deze autocomplete-functionaliteit nu naar het Web. In dit artikel zal ik beschrijven wat datalijsten zijn, wanneer ze kunnen worden gebruikt, wat hun beperkingen zijn en wat te doen voor browsers die ze niet ondersteunen. Laten we aan de slag gaan.
Voordat u begint
Wilt u een datalijst voor uw site maken zonder HTML5 te gebruiken? Gebruik JotForm om snel een aangepast vervolgkeuzemenu te maken – of zelfs een complexe dynamische vervolgkeuzelijst – zonder dat u hoeft te coderen.
Datalijsten maken
Om te laten zien hoe een datalijst werkt, beginnen we met een traditionele tekstinvoer:
- <label for=”favorite_team”>Favoriete team:</label>
- <input type=”text” name=”team” id=”favorite_team”>
In dit veld wordt de gebruiker gevraagd zijn of haar favoriete sportteam op te geven. Standaard krijgt de gebruiker geen extra hulp bij het invullen van het veld. Maar door een datalijst te gebruiken, kunt u de gebruiker een lijst met opties geven waaruit hij kan kiezen om het veld in te vullen. Definieer daartoe een datalijst met een optie-element voor elke suggestie:
- <datalist>
- <option>Detroit Lions</optie>
- <optie>Detroit Pistons</optie>
- <optie>Detroit Red Wings</optie>
- <optie>Detroit Tigers</optie>
- <!- etc… ->
- </datalist>
Om een datalist aan een invoerelement te koppelen, geef je het invoerelement een lijst-attribuut en de datalist een id-attribuut die overeenkomen. Hier volgt een voorbeeld:
- <label for=”favorite_team”>Favoriete team:</label>
- <input type=”text” name=”team” id=”favorite_team” list=”team_list”>
- <datalist id=”team_list”>
- <optie>Detroit Lions</optie>
- <optie>Detroit Pistons</optie>
- <optie>Detroit Red Wings</optie>
- <optie>Detroit Tigers</optie>
- <!- etc… ->
- </datalist>
Merk op dat het lijst-attribuut van de input en het id-attribuut van de datalist dezelfde waarde bevatten, “team_list”. Dit zorgt voor de koppeling tussen de twee.
Dat is het. Er is geen JavaScript nodig om een datalist te laten werken. Figuur 1 laat zien wat de gebruiker te zien krijgt in ondersteunende browsers nadat hij een D heeft getypt.
Figuur 1. Weergave van datalijsten (linksboven: Internet Explorer 10; rechtsboven: Firefox 18; linksonder: Chrome 24; rechtsonder: Opera 12)
Opmerking: Internet Explorer 10 en Opera vereisen niet dat de gebruiker een teken typt voordat hij suggesties te zien krijgt, terwijl Firefox en Chrome dat wel doen.
Option-elementen kunnen ook een waarde-attribuut hebben. Dit is handig wanneer een gebruiker niet weet welke code bij een bepaalde optie hoort. Neem de volgende Amerikaanse state-invoer:
- <label for=”state”>State:</label>
- <input type=”text” name=”state” id=”state” list=”state_list”>
- <datalist id=”state_list”>
- <option value=”AL”>Alabama</optie>
- <optiewaarde=”AK”>Alaska</optie>
- <optiewaarde=”AZ”>Arizona</optie>
- <optiewaarde=”AR”>Arkansas</optie>
- <!- etc ->
- </datalist>
Hier ziet de gebruiker een lijst met volledige staatnamen, maar wanneer de gebruiker een selectie maakt, wordt de tekstinvoer gevuld met de code van de staat in plaats van de volledige naam. Een voorbeeld hiervan is te zien in Figuur 2.
Figuur 2. Het selecteren van een datalijstoptie met bijbehorende waarde (Firefox 18)
Het autocomplete attribuut
Als de datalijsten in Figuur 1 en Figuur 2 u bekend voorkomen, komt dat doordat browsers autocomplete al lange tijd hebben geïmplementeerd. Alle browsers hebben een of ander mechanisme om de invoer van een gebruiker op te slaan zodat die later voor autocomplete kan worden gebruikt.
Auteurs kunnen het autocomplete attribuut gebruiken om te bepalen of de browser moet proberen om de gegevens van de gebruiker automatisch aan te vullen. De mogelijke waarden worden in het volgende voorbeeld getoond:
- <form>
- <!-
- Als het autocomplete attribuut niet is opgegeven, erft het de waarde
- van het bovenliggende formulier-element. Als dit niet is opgegeven, is de standaardwaarde van het autoaanvullen-attribuut van het
- formulier “aan”. Aangezien noch voor deze invoer, noch voor het
- formulier het attribuut is gespecificeerd, wordt de “aan”-status gebruikt.
- ->
- <input type=”text” name=”firstName”>
- <!
- De “aan”-status geeft aan dat de browser de waarde kan onthouden voor toekomstig
- gebruik en ook eerder opgeslagen waarden kan suggereren.
- ->
- <input type=”text” name=”address” autocomplete=”on”>
- <!-
- De status “uit” vertelt de browser niet de ingevoerde waarde voor
- deze invoer op te slaan, noch eerder ingevoerde waarden voor te stellen. Dit moet
- worden gebruikt als de gegevens gevoelig zijn of als de waarde nooit zal worden hergebruikt.
- ->
- <input type=”text” name=”secret” autocomplete=”off”>
- </form>
Wat is nu het verschil tussen het autocomplete-attribuut en datalijsten? Het autocomplete attribuut vertelt de browser of hij de gebruiker opties moet geven om in te vullen op basis van eerdere invoer en of hij de ingevoerde waarde moet opslaan voor toekomstige invulling. Datalijsten zijn door de auteur verstrekte lijsten met suggesties die altijd worden weergegeven, ongeacht de eerdere invoer.
Eén voorbehoud is dat als u het autocomplete-attribuut op “off” zet, datalijsten niet in Opera worden weergegeven. Hier volgt een voorbeeld:
- <!-
- Deze datalijst zal nooit in Opera worden weergegeven omdat het autocomplete-attribuut
- op “off” is gezet. Hij wordt wel in alle andere ondersteunende browsers weergegeven.
- ->
- <input type=”text” list=”pets” autocomplete=”off”>
- <datalist id=”pets”>
- <optie>kat</optie>
- <optie>Hond</optie>
- <optie>Hamster</optie>
- <optie>schildpad</optie>
- </datalist>
Andere invoertypes
Hoewel autocompletion traditioneel wordt geassocieerd met tekstuele invoer, kunnen datalijsten ook worden gebruikt voor een aantal van de nieuwe HTML5-invoertypen. Denk aan het bereik invoertype, dat het mogelijk maakt om een slider form element te maken. Door dit te combineren met een datalijst, kun je punten op de reeks aan de gebruiker voorstellen.
Bijv. de volgende invoer vraagt de gebruiker om een donatie tussen $5 en $200 dollar.
- <label for=”donatie”>Donatiebedrag (USD):</label>
- <input type=”range” name=”donation” id=”donatie” list=”donatie_lijst”
- stap=”5″ min=”5″ max=”200″>
- <datalist id=”donatie_lijst”>
- <optie></optie>
- <optie></optie>
- <optie></optie>
- <optie></option>
- </datalist>
Figuur 3 en figuur 4 tonen de weergave van een bereikinvoer in Chrome 24 en Internet Explorer 10, respectievelijk.
Figuur 3. Bereikinvoer met datalijst (Chrome 24)
Figuur 4. Range Input with Datalist (Internet Explorer 10)
U kunt zien dat in elke browser een vinkje wordt weergegeven voor elke optie in de datalijst. Bovendien zet Chrome de schuifbalk vast op deze vooraf gedefinieerde waarden als de gebruiker de schuifbalk in de buurt ervan beweegt.
Helaas zijn Internet Explorer en Chrome op dit moment de enige browsers die datalijsten voor bereikinvoer ondersteunen. Figuur 5 toont de ondersteuning van datalijsten op veelgebruikte invoertypen in moderne browsers.
Figuur 5.
Wanneer een datalijst gebruiken
Omdat datalijsten geen ingebouwd mechanisme hebben om de gebruiker te verplichten een bepaalde optie te kiezen, zijn ze zeer geschikt voor ingangen die elke waarde kunnen aannemen. Het eerdere voorbeeld van het opgeven van een sportteam past hier. Hoewel de datalijst teams voorstelde, was de gebruiker vrij om elke waarde in te voeren.
Omgekeerd faalt het voorbeeld van de Amerikaanse staat voor deze test, omdat er een beperkte set geldige waarden is die de gebruiker moet opgeven. Een dergelijk geval kan beter worden afgehandeld met het select-element, omdat dat een selectie afdwingt.
Een uitzondering hierop vormen ingangen met een groot aantal geldige selecties. Neem bijvoorbeeld de traditionele vervolgkeuzelijst met landen in figuur 6.
Figuur 6. Standard Country Drop-Down
Deze lijst belemmert gebruikers omdat ze honderden opties moeten doorlopen om het land te vinden dat ze zoeken. Een autocomplete-functie past goed bij deze use-case, omdat een gebruiker de lijst snel kan filteren door een of twee karakters in te typen.
Hier ziet u hoe de landselectie kan worden geïmplementeerd met een datalijst:
- <label for=”country”>Country:</label>
- <input type=”text” id=”land” list=”country_list”>
- <datalist id=”country_list”>
- <option value=”AF”>Afghanistan<
- <optie value=”AX”>Ailandeilanden</optie>
- <optie value=”AL”>Albanië</optie>
- <optiewaarde=”DZ”>Algerije</optie>
- <!- meer ->
- </datalist>
Figuur 7 laat zien wat de gebruiker te zien krijgt nadat hij een U heeft getypt.
Figuur 7. Een datalijstbenadering voor het landformulierveld
Een waarde afdwingen
Hoewel u met datalijsten van nature niet kunt eisen dat een optie wordt geselecteerd, kunt u eenvoudig een validatie toevoegen die dat wel doet. De volgende code maakt bijvoorbeeld gebruik van de HTML5 constraint-validatie-API om een dergelijke validatie toe te voegen:
- // Zoek alle ingangen op het DOM die via hun lijstattribuut aan een datalijst zijn gebonden.
- var inputs = document.querySelectorAll(‘input’);
- for (var i = 0; i < inputs.length; i++) {
- // Wanneer de waarde van de input verandert…
- inputs.addEventListener(‘change’, function() {
- var optionFound = false,
- datalist = this.list;
- // Bepaal of er een optie bestaat met de huidige waarde van de input.
- for (var j = 0; j < datalist.options.length; j++) {
- if (this.value == datalist.options.value) {
- optionFound = true;
- break;
- }
- gebruik de setCustomValidity functie van de Validation API
- // om een gebruiker feedback te geven als de waarde niet bestaat in de datalist
- if (optionFound) {
- this.setCustomValidity(“);
- } else {
- this.setCustomValidity(‘Selecteer een geldige waarde.’);
- }
- });
- }
De constraint validation API is geïmplementeerd in alle browsers die datalijsten ondersteunen, dus als de datalijst werkt, zou de validatie ook moeten werken. Als de gebruiker nu probeert een formulier in te dienen met een invoer die een datalijst bevat (en hij heeft geen optie geselecteerd), krijgt hij de foutmelding te zien die wordt weergegeven in Figuur 8.
Figuur 8. Constraint Validation API Error (Internet Explorer 10)
Het is belangrijk op te merken dat de constraint validation API de noodzaak van server-side validatie niet wegneemt. Gebruikers die met oudere browsers werken, hebben de constraint validation API niet tot hun beschikking, en kwaadwillende gebruikers kunnen client-side scripts eenvoudig omzeilen.
Weliswaar werkt deze aanpak in moderne browsers, maar hij biedt een onbruikbare gebruikersinterface voor gebruikers met browsers die geen ondersteuning bieden. Gebruikers wordt verteld dat ze een geldige waarde moeten selecteren, maar als hun browser geen datalijsten ondersteunt, kunnen ze de opties niet zien. Als je van plan bent deze aanpak te gebruiken, is het dus essentieel om een UI te bieden die in alle browsers werkt. Dit kan worden bereikt door te detecteren of de browser datalijsten ondersteunt en vervolgens polyfilling toe te passen.
Browsers die datalijsten niet ondersteunen
Op het moment van schrijven worden datalijsten voor tekstinvoer ondersteund in Internet Explorer 10, Firefox 4+, Chrome 20+, en Opera, waardoor helaas een groot aantal gebruikers buiten de boot valt.
In tegenstelling tot veel nieuwe HTML5-functies, hoeft voor de meeste gebruikssituaties geen extra werk te worden verricht in browsers die datalijsten niet ondersteunen. De lijst met opties die u aanbiedt, zijn standaard slechts suggesties; gebruikers met browsers die geen ondersteuning bieden, hoeven het tekstveld dus alleen maar in te vullen zonder suggesties.
Echter kunnen sommige fallback-opties worden gebruikt om gebruikers met browsers die geen ondersteuning bieden, een completere ervaring te bieden.
Fallback to Alternate HTML Content
Eén optie, populair gemaakt door Jeremy Keith, is om gebruik te maken van het feit dat browsers die het datalist-element niet ondersteunen, toch child-elementen aan de gebruiker tonen. Het volgende voorbeeld laat zien hoe het land datalist voorbeeld kan worden aangepast en terug kan vallen op het gebruik van <select>:
- <label for=”country”>Country:</label>
- <datalist id=”country_list”>
- <select name=”country”>
- <optiewaarde=”AF”>Afghanistan</optie>
- <optiewaarde=”AX”>Ailandeilanden</optie>
- <optiewaarde=”AL”>Albanië</optie>
- <optiewaarde=”DZ”>Algerije</optie>
- <optiewaarde=”AS”>Amerikaans-Samoa<
- <!- meer ->
- </select>
- Indien anders, graag specificeren:
- </datalist>
- <input type=”text” name=”country” id=”country” list=”country_list”>
De UI die wordt gepresenteerd aan gebruikers in browsers die datalijsten ondersteunen, zal niet veranderen, maar gebruikers die werken met browsers zonder ondersteuning zien nu een select element met de landopties en een tekstvak dat ze kunnen gebruiken om een willekeurige waarde in te voeren. Dit is te zien in Figuur 9.
Figuur 9: Een select fallback voor datalijsten (Internet Explorer 9)
Polyfilling
Een functie die de select fallback niet biedt, is het autoaanvul-gedrag dat datalijsten van nature bieden. Als dat belangrijk is voor de datalijsten die je wilt toevoegen, is een tweede mogelijkheid het polyfillen van een datalist-implementatie.
Om te beginnen, moet je eerst bepalen of de browser van de gebruiker datalijsten ondersteunt. De populaire feature detection library Modernizr biedt zo’n test, zoals hier te zien is:
- if (Modernizr.input.list) {
- //De browser ondersteunt het list attribuut en datalists.
- } else {
- //De browser ondersteunt noch het list attribuut noch datalists.
- }
- }
Door deze aanpak te gebruiken, kun je nu een polyfill maken van een datalist implementatie voor gebruikers in browsers die dat niet ondersteunen. Hoewel er verschillende polyfills beschikbaar zijn voor datalijsten, geef ik de voorkeur aan het gebruik van jQuery UI’s autocomplete widget. De volgende code toont een polyfill implementatie:
- var datalist,
- listAttribute,
- options = ;
- // Als de browser het list attribuut niet ondersteunt…
- if (!Modernizr.input.list) {
- // Voor elke tekstinvoer met een lijst-attribuut…
- $(‘input’).each(function() {
- // Zoek het id van de datalist bij de input
- // Zoek aan de hand hiervan de datalist die bij de input hoort.
- listAttribute = $(this).attr(‘list’);
- datalist = $(‘#’ + listAttribute);
- // Als de invoer een corresponderend datalist-element heeft…
- if (datalist.length > 0) {
- options = ;
- // Bouw de lijst met opties op om aan de autocomplete widget door te geven.
- datalist.find(‘option’).each(function() {
- options.push({ label: this.innerHTML, value: this.value });
- });
- // Zet de invoer om in een jQuery UI autocomplete widget.
- $(this).autocomplete({ source: options });
- }
- });
- // Verwijder alle datalijsten uit het DOM, zodat ze niet worden weergegeven.
- $(‘datalist’).remove();
- }
Figuur 10 toont de weergave van het voorbeeld van de landenlijst in Safari met de jQuery UI autocomplete polyfill.
Figuur 10. Land Datalist Polyfilled Using jQuery UI’s Autocomplete Widget(Safari)
Het is u misschien opgevallen dat de autocomplete widget van jQuery UI standaard overeenkomt met tekens overal in de opties, terwijl datalijsten alleen overeenkomen met opties aan het begin van de tekenreeks. In tegenstelling tot de native datalist, kunt u de autocomplete widget aanpassen zodat de opties worden weergegeven zoals u dat wilt.
Het volgende voorbeeld laat zien hoe u een autocomplete functie kunt bouwen die alleen aan het begin van de tekenreeks met opties matcht:
- <input type=”text” id=”autocomplete”>
- <script>
- var options = ;
- $(‘#autocomplete’).autocomplete({
- // req zal een object bevatten met een “term” eigenschap die de waarde bevat
- // die momenteel in de tekstinvoer staat. responseFn moet worden aangeroepen met de opties
- // om aan de gebruiker te tonen.
- source: function (req, responseFn) {
- // Escape any RegExp meaningful characters such as “.”, or “^” from the
- // keyed term.
- var term = $.ui.autocomplete.escapeRegex(req.term),
- // ‘^’ is het RegExp teken om aan het begin van de string te matchen.
- // ‘i’ vertelt de RegExp om een hoofdletter ongevoelige match te doen.
- matcher = new RegExp(‘^’ + term, ‘i’),
- // Loop over de opties en selecteert alleen de opties die overeenkomen met de RegExp.
- matches = $.grep(options, function (item) {
- return matcher.test(item);
- });
- // Geef de gematchte opties terug.
- responseFn(matches);
- }
- });
- </script>
Oldere versies van Internet Explorer
Om een polyfill van het datalist-element in oudere versies van Internet Explorer te laten werken, moet je twee extra stappen nemen.
Optie-elementen
De eerste is dat Internet Explorer versie 9 en eerder vereisen dat optie-elementen directe kinderen van select-elementen zijn. Als ze dat niet zijn, worden ze niet herkend en zijn ze niet zichtbaar voor de polyfill.
Gelukkig is dit eenvoudig te omzeilen. Door gebruik te maken van voorwaardelijk commentaar, kun je een select element plaatsen rond de opties alleen voor deze versies van Internet Explorer. Zie dit voorbeeld:
- <datalist>
- <><select><<>
- <optie>Apple</optie>
- <optie>Banaan</optie>
- <optie>Kers</optie>
- <!- etc ->
- <><select><<>
- </datalist>
Internet Explorer versie 9 en eerder zal de opties nu op de juiste manier detecteren. Internet Explorer 10 wordt niet beïnvloed, omdat voorwaardelijk commentaar in Internet Explorer 10 uit de parser is verwijderd. Alle andere browsers zullen de opmerkingen ook negeren.
HTML5 Shiv
In Internet Explorer versie 8 en eerder kunnen onbekende elementen geen kinderen bevatten. Dus zelfs als de optie-elementen van een datalijst binnen select-elementen staan, worden ze nog steeds niet herkend.
Gelukkig is er ook voor dit probleem een eenvoudige oplossing. De populaire HTML5 Shiv-bibliotheek staat toe dat elementen die onbekend zijn voor Internet Explorer 6-8 toch kinderen kunnen hebben. Deze shiv wordt standaard meegeleverd in Modernizr; zorg ervoor dat het selectievakje “html5shiv” is ingeschakeld wanneer u uw download configureert.
Beperkingen van Datalijsten
Hoewel datalijsten perfect zijn voor het toevoegen van suggesties aan tekstinvoer, lijden ze aan een gebrek aan aanpassingsmogelijkheden die veel moderne webapplicaties nodig hebben. Je kunt bijvoorbeeld niet het volgende met datalijsten doen:
- Css gebruiken om de weergave of de weergave van de opties te wijzigen.
- De positionering van de lijst bepalen.
- Het aantal getypte tekens bepalen voordat de browser de resultaten aan de gebruiker toont.
- Bepaal wat als een overeenkomst wordt beschouwd (hoofdlettergevoeligheid, overeenkomst aan het begin van een string versus overal, enzovoort).
- Bind de invoer aan een server-side datasource.
Dit betekent dat als je een van deze functies nodig hebt, je moet kijken naar een meer robuuste autocomplete oplossing. De jQuery UI autocomplete widget biedt de mogelijkheid om dit alles en nog veel meer te doen. De autocomplete widget ondersteunt ook alle browsers vanaf Internet Explorer 7 zonder de noodzaak van een polyfill. Voor meer informatie over de autoaanvullen-widget, kunt u de demo’s en API documentatie bekijken. (De autocomplete widget kan alleen tekst-gebaseerde invoer verwerken, dus het zal u niet kunnen helpen voor enkele van de meer gespecialiseerde invoertypes zoals bereik en kleur.)
Wrapping Up
Datalists bieden een snelle, native manier om invoersuggesties aan de gebruiker te laten zien. Omdat de opties slechts suggesties zijn, is het in veel situaties niet nodig om een fallback te bieden voor browsers die dat niet doen; gebruikers van deze browsers krijgen de suggesties dan gewoon niet te zien.
Voor situaties waarin je wel een functionele datalijst wilt bieden aan alle gebruikers, kun je de ondersteuning detecteren en de functionaliteit polyfill-en voor browsers die dat niet doen.
Weliswaar zijn datalijsten geweldig voor het bieden van suggesties, maar ze zijn beperkt in de functionaliteit die ze bieden. Als u een meer robuuste autoaanvullen-oplossing nodig hebt, is de autoaanvullen-widget van jQuery UI een goede plek om te beginnen.