Fronteers — vakvereniging voor front-end developers

Webrichtlijn 48, 49 & 50: Links en client-side scripts

Bij het gebruik van client-side script in combinatie met een link: maak de scriptfunctionaliteit een uitbreiding op de basisfunctionaliteit van de link. (R-pd.8.5) Bij het gebruik van client-side script in combinatie met een link: indien de link nergens naartoe leidt, confronteer de bezoeker zonder ondersteuning voor client-side script dan niet met een niet-werkende link. (R-pd.8.6) Bij het gebruik van client-side script in combinatie met een link: indien noodzakelijk, gebruik client-side script als een uitbreiding op server-side functies. (R-pd.8.7)

<a href="javascript:foo();"> is natuurlijk verschrikkelijk evil! Doe dan op z'n minst <a href="#" onclick="foo();"> (of onclick="javascript:foo();", wat wel zo professioneel staat). Maar wat als je deze link met JavaScript genereert?

Wat doe je als je een lijst met 20 links hebt, waarvan je er slechts 5 wilt tonen (althans, dat wil de ontwerper). Onder de lijst staat een mooi linkje met 'Meer links', die de overige 15 toont? Hoe voeg je dit met JavaScript toe en wat zet je in het href attribuut van de link? Hoort dit eigenlijk wel een <a>'tje te zijn?

Tabbladen zijn volgens mij een mooi voorbeeld van hoe je links en scripting kunt combineren. Hoe los je dit op, als je het zelf script? Zorg je ervoor dat de links bookmarkable zijn, of in een nieuwe browser tab zijn te openen? Gebruik je return false; in de links, of laat je de browser 'springen' naar #het-id?

Hoe ver ga je bij bijvoorbeeld carrouselletje met sfeerafbeeldingen, waar je met JavaScript doorheen kunt klikken? Is dit iets wat je op de back-end ook laat werken, of vinden we dat te veel gedoe? En in dat laatste geval, wat zet je in de href=""? En dezelfde vraag voor blokken tekstuele content, die in een carrousel staan, en waar je stoere Web 2.0 Ajax (niet AJAX) effectjes overheen gooit.

En na al dat geneuzel met het nadenken over twee paden in je site (eentje met en eentje zonder JavaScript) kom je erachter dat je het toch nog niet goed doet. Want je mooie Ajax carrousel werkt misschien 'perfect' met en zonder JavaScript, maar hoe werkt het bijvoorbeeld in screenreaders die JavaScript wel ondersteunen, maar animaties met verdwijnende en verschijnende content niet uit kunnen drukken? ARIA, met Live Regions? Of vinden we het al goed genoeg dat we de webrichtlijnen naleven?

Reacties

1 Niek Kouwenberg op 01-07-2008 om 13:37 uur:
Al je vragen worden beantwoord met het gebruik van unobtrusive Javascript. Het principe hiervan is om (zoals MVC) de Javascript functionaliteit geheel te scheiden van de HTML. Dus ook geen onclick attributen meer.

Hierdoor moet de HTML an sich dus al goede inhoud bevatten, dus geen href="#" of lege A-tags. Door middel van een Javascript include en initialisatie in de head wordt bestaande HTML functionaliteit uitgebreid.

Een uitstekend voorbeeld hiervan is Lightbox2, een Javascript library waarmee je afbeeldingen kunt bekijken in een inline popup. Je linkt jouw thumbnail naar de grote afbeelding (<a href="large.jpg" rel="lightbox"><img src="thumb.jpg" alt=" /></a>) en Lightbox2 zorgt er dan zelf voor dat de link niet geopend wordt, maar deze getoond wordt in de inline popup.

Hetzelfde principe kun je overal toepassen zoals bijvoorbeeld bij een slideshow. Je maakt een linkje volgende aan in de HTML (<a href="slide2.html">volgende</a>) en koppelt daar via jouw Javascript een onclick actie aan die via Ajax de inhoud van de volgende slide ophaalt.
2 Krijn op 01-07-2008 om 13:48 uur:
Niek: ik ga hierboven volledig uit van unobtrusive JavaScript. Laat je niet afleiden door onclick="" :)

Tabbladen kun je al in de pagina zetten en 'inklappen' met JavaScript. Wat doe je dan met de linkjes (die door JS gegenereerd worden)? Zelfde met de lijst met 20 links. En bij de carrousel met afbeeldingen kan het zijn dat je de afbeeldingen al in de pagina zet, zodat je er met JavaScript doorheen kunt lopen.

Een uitstekend voorbeeld hiervan is Lightbox2, een Javascript library waarmee je afbeeldingen kunt bekijken in een inline popup. De Vorige, Volgende en Sluiten links bevaten href="#"...
3 Niek Kouwenberg op 01-07-2008 om 14:10 uur:
Als er iets door Javascript gegenereerd wordt, heb je al Javascript, dus is er geen fallback meer nodig. Dit zie je dus ook bij Lightbox2, waarbij de links ook door Javascript zijn gegenereerd. Toch is dit niet de ideale situatie aangezien ik met een middle-click een nieuwe tab open met url#. Helemaal leuk zou zijn als de volgende afbeelding dan ook gelinkt werd.

Bij jouw voorbeeld met 20 links in een lijst, zou ik door middel van Javascript een nieuwe <ul> maken in de DOM (display:none;) voor de laatste 15 links, en deze uit de originele <ul> verwijderen. Daarna zou ik een nieuwe link toevoegen aan de originele <ul> waaraan mijn Javascript event is gekoppeld om het vervolg van de lijst ook te laten zien.

Echter bij een carrousel zou ik de afbeeldingen niet in de HTML zetten; voor het geval dat je dadelijk 100 afbeeldingen wilt laten zien. Je zou zo'n pagina in één keer binnen moeten halen. Het is veel gebruikersvriendelijk om deze "speciale features" later te laden, zodat de gebruiker reeds de inhoud van de pagina kan lezen.
4 Krijn op 01-07-2008 om 14:14 uur:
"... Daarna zou ik een nieuwe link toevoegen aan de originele <ul> waaraan mijn Javascript event is gekoppeld om het vervolg van de lijst ook te laten zien." - En wat wordt de markup voor je link? Daar ging het me om :)
5 Niek Kouwenberg op 01-07-2008 om 14:30 uur:
Dat zal dan <li><a href="" onclick="[...]">Meer...</a></li> zijn. (onclick inline of unobstrusive zal geen verschil maken.)

Het gebruik van een A-tag met een leeg href attribute is om optimaal CSS te kunnen gebruiken, wat je bijvoorbeeld bij een SPAN niet kunt.

En zoals ik al aangaf; Je hebt geen fallback meer nodig omdat je toch al aan het Javascripten bent. Wat ik wel zou doen is een return false in mijn onclick event zetten (indien unbostrusive event.stopPropagation()), je wilt natuurlijk niet dat de browser doorgaat naar welke linkje dan ook.
6 Sander A op 02-07-2008 om 14:41 uur:
Qua CSS lijkt een span me geen probleem (behalve dan voor de :hover in IE6 misschien, maar dat zou je natuurlijk ook weer met JS op kunnen lossen ;-)). Voor keyboard-navigatie is echter simpelweg een link vereist, tenzij je een tabindex toevoegt heb ik mij onlangs laten vertellen -- http://www.quirksmode.org/presentations/kingsofcode2008/kingsofcode.pdf, pag. 60 (PDF, 620kB) -- maar dat wil je waarschijnlijk niet.
7 Sander A op 02-07-2008 om 14:52 uur:
@Niek: Waarom maak je eigenlijk een nieuwe lijst aan?

Ik zou de bewuste list items gewoon verbergen op page load en deze weer tonen als er op de link geklikt wordt.
Ik weet trouwens ook niet of ik die 'JS-link' aan de lijst toe zou voegen. Je kunt je afvragen of ie daarin thuishoort al kun je natuurlijk ook stellen dat ie staat voor de verborgen items. Het hangt een beetje van de implementatie af denk ik: gaat het om een eenmalige actie waarbij de JS-link vervangen wordt door de oorspronkelijke items of is het een toggle-link? In het eerste geval kan ik me voorstellen dat de JS-link zelf in de lijst geplaatst wordt.
8 Niek Kouwenberg op 02-07-2008 om 16:50 uur:
Nadat ik het gepost had dacht ik zelf ook: stom, een nieuwe lijst. Maar ik had er wel een reden voor.

Een <li> heeft als display waarde list-item, waardoor je na de display: none; deze ook weer terug moet zetten. Dit heb ik ooit eerder geprobeerd, maar niet alle browsers deden dit correct.

Mijn oplossing was dus ook twee <ul>'s waarbij ik de laatste verberg en weer laat zien.

Ik kan nu niet testen of bovenstaande ook klopt, of dat ik toen iets verkeerd heb gedaan, maar dat was in ieder geval de gedacht erachter.
9 Krijn op 02-07-2008 om 16:57 uur:
We moeten vaker discussies over markup hebben! ;)
10 ppk op 05-07-2008 om 14:45 uur:
Niek,

Als je een list-item een display: none geeft,

li.style.display = 'none';

zou je theoretisch de oude stijl weer moeten kunnen herstellen door de inline display weer leeg te maken:

li.style.display = '';

Nu wordt de inline style verwijderd, waardoor de default style van de browser weer aangeroepen wordt.

Ik heb dit truukje niet met <li>s getest, maar wel met <tr>s en daar werkt het prima: geen browserproblemen.
11 Niek Kouwenberg op 06-07-2008 om 18:57 uur:
Peter-Paul,

Er leuk trucje, ik heb er nog nooit zo over nagedacht. Maar eigenlijk heel logisch als je bedenkt dat de Javascript toch alleen het style attribuut manipuleert.

Ik heb het ook cross-browser getest met een <ul> en <ol>, en het werkt feilloos.

Maar in tegenstelling tot wat ik dacht doet IE6 niets vreemds met display: list-item. Zelfs display: block zorgt voor hetzelfde resultaat, terwijl in andere browsers (volgens de standaard) hierbij de bullets niet worden getoond.
12 Mathias op 22-12-2008 om 17:50 uur:
Zaken als <a href="#"> zijn volgens mij sowieso 'fout', of de markup nu gegenereerd is door JavaScript of geschreven door een webontwikkelaar.

Deze discussie hangt samen met de definitie van een link. Voor mij is een link een verwijzing naar een volwaardige URL, dewelke kopieerbaar is, en aldus zonder meer in een nieuw tabblad / venster geopend kan worden.

Dit sluit echter niet uit dat er gevallen zijn waarin je een element er wil laten uitzien als een link (met hover-effect en aangepaste cursor). Analoog met CSS / HTML staat deze visuele opsmuk echter los van de gebruikte markup, als je 't mij vraagt...
13 Koen Willems op 23-12-2008 om 01:56 uur:
@ Mathias:

'Zaken als <a href="#"> zijn volgens mij sowieso 'fout', of de markup nu gegenereerd is door JavaScript of geschreven door een webontwikkelaar.'

Dat mag je me uitleggen!

IK zou niet weten wat er mis mee zou zijn als je unobtrusive met Javascript een <a href="#"> maakt.

Sterker nog: ik geloof dat ik dat zeer regelmatig doe.

:-)
14 Mathias op 23-12-2008 om 09:26 uur:
Koen, de rest van m'n bericht legde dat zowat uit. Wanneer jij <a href="#"> gebruikt, wil je eigenlijk helemaal geen link leggen, maar gewoon een element aanmaken dat toevallig eigenschappen heeft van een link (als in: het is klikbaar). Het is dan echter geen link, omdat je de 'koppelingslocatie' niet zomaar kan kopiëren, hergebruiken, en met hetzelfde scherm eindigen als wanneer je op het element zou klikken. Daarom is er in zo'n geval volgens mij geen sprake van een link, en mag je dat zo ook niet coderen. In de plaats kan je een ander HTML-element gebruiken; eentje waarbij de eindgebruiker niet verward wordt door de mogelijkheden om de 'koppelingslocatie te kopiëren' of de 'koppeling in een nieuw venster / tabblad te openen'.
15 Sander A op 24-12-2008 om 11:46 uur:
@Mathias:

Maar in dat geval is het betreffende element niet bereikbaar via het toetsenbord. Door er een link van te maken is dat wel het geval.
16 Mathias op 24-12-2008 om 12:07 uur:
Inderdaad Sander, maar dat is dan weer op te lossen door de elementen een tabindex="0" te geven. Zie ook http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#sequential-focus-navigation: "If the tabindex value is a zero, the user agent must allow the element to be focused, should allow the element to be reached using sequential focus navigation, and should follow platform conventions to determine the element's relative order.

Ja, dit is een link naar de HTML 5 spec, maar het principe zou voor HTML 4 hetzelfde moeten zijn. En dit werkt inderdaad (nog) niet correct op enkele browsers (in Firefox zijn de elementen bijvoorbeeld wel selecteerbaar via het toetsenbord, maar kan er niet op 'geklikt' worden).

Trouwens, gebruikers die met hun toetsenbord willen werken, gebruiken toch Opera ;)

Serieus nu, ikzelf offer graag een beetje toetsenbordfunctionaliteit (ten gevolge van onvolledige browserimplementaties) op voor de voorheen door mij beschreven semantische logica.
17 Sander A op 25-12-2008 om 23:41 uur:
"Serieus nu, ikzelf offer graag een beetje toetsenbordfunctionaliteit (ten gevolge van onvolledige browserimplementaties) op voor de voorheen door mij beschreven semantische logica."

Dat gaat mij toch net een stap te ver. Hoezeer ik webstandaarden en best practices en al dat moois ook nastraaf vind ik dat praktische implementaties toch boven de meer theoretische specificaties gaan. Zeker waar het toegankelijkheid betreft.

Zou je ermee kunnen leven wanneer de link naar zichzelf verwijst: <a href="#print" id="print">Print</a>? Niet dat het echt iets toevoegt, maar toch.
Plaats een reactie