Meestal een van de eerste dingen die je leert als je met jQuery events aan de slag gaat is het stoppen van het standaard gedrag van een browser. In de uitleg voor het click event kun je het volgende tegen komen:
$('a.clickme').click(function() {
$('#mydiv').show();
return false; // zorgt ervoor dat de browser de link niet volgt
});
Deze functie toont het #mydiv element en stopt daarna het standaard gedrag van een browser door de href van de link niet te volgen.
Het gebruik van return false is hier eigenlijk niet goed en veel beginners zullen door dit voorbeeld return false gaan gebruiken als ze het standaard gedrag van een browser willen stoppen. Ik zal twee belangrijke onderwerpen over het stoppen van browser events behandelen:
- Gebruik de juiste methode:
return false vs. preventDefault, stopPropagation en stopImmediatePropagation.
- Bovenaan, in het midden of onderaan: waar in de event callback moet je het standaard gedrag van de browser stoppen?
Let op: wanneer ik het heb over event bubbling dan bedoel ik dat events eerst worden uitgevoerd op het originele DOM element en vervolgens omhoog op elk parent element in de DOM structuur. Events bubbelen niet naar siblings of child elementen. Wanneer events naar beneden bubbelen heet dit event capturing.
Gebruik de juiste methode
De belangrijkste reden dat return false op deze manier wordt gebruikt is omdat het lijkt te doen wat we willen bereiken. Links worden niet gevolgd, formulieren worden niet gesubmit, etc. Waarom zouden we het dan niet gebruiken?
Wat return false precies doet
Wanneer return false wordt aangeroepen gebeuren er drie dingen:
event.preventDefault()
event.stopPropagation()
- Stop de uitvoering van de callback
Maar wacht even, zul je vast denken. Ik heb alleen maar de eerste actie nodig om standaard browser gedrag te stoppen…
De enige van bovenstaande drie acties die nodig is om het standaard van een browser gedrag te stoppen is preventDefault(). Tenzij je het bubbelen van het event (propagation) wilt stoppen kan return false voor verwarring zorgen. Een voorbeeld om het wat duidelijker te maken:
De HTML voor het voorbeeld:
<div class="artikel">
<h2><a href="#">Titel</a></h2>
<div class="content">...</div>
</div>
Wanneer er op de link in de h2 wordt geklikt vullen we de div.content met tekst:
$('.artikel h2 a').click(function() {
$(this).parent().next().html('Tekst voor in content');
return false;
});
Tot nu toe gaat het goed. Maar we willen ook graag de class “active” toevoegen aan elke div.artikel waar op wordt geklikt (of wanneer er op een child element van div.artikel wordt geklikt). Daarom maken we een extra handler voor het click event:
$('.artikel').click(function() {
$(this).addClass('active');
});
Werkt dit wanneer we op de link in het h2 element klikken? Nee! De reden waarom dit niet werkt is omdat we return false hebben gebruikt. return false betekent namelijk event.preventDefault(); event.stopPropagation(); en het click event zal dus nooit omhoog bubbelen naar div.artikel waardoor het click event nooit zal worden uitgevoerd.
Wat willen we dan wel?
preventDefault()
In de meeste gevallen waar je voorheen return false voor zou gebruiken is e.preventDefault() een betere keuze. Wanneer je preventDefault gebruikt moet je de event parameter wel beschikbaar stellen in je callback:
$('a.clickme').click(function(e) {
e.preventDefault(); // e == ons event
});
Dit doet precies wat we willen zonder dat andere elementen het click event niet meer ontvangen.
stopPropagation()
Soms wil je alleen het bubbelen van het event (propagation) stoppen. Een voorbeeld:
<div class="artikel">Een stukje tekst met <a href="#">een link</a> en nog wat meer tekst.</div>
Stel, je wilt dat er iets gebeurd wanneer er ergens in het div element wordt geklikt maar je wilt ook dat de link wordt gevolgd als er op wordt geklikt.
$('div.artikel').click(function() {
// doe iets
});
$('div.artikel a').click(function(e) {
// het standaard browser gedrag wordt niet gestopt
// en het event bubbelt niet omhoog
e.stopPropagation();
});
Als we in dit voorbeeld return false hadden gebruik zou het click event op div.artikel nooit worden uitgevoerd maar de gebruiker zal ook nog naar de link worden gestuurd.
stopImmediatePropagation()
Deze methode stopt de uitvoering van het event, ook voor alle andere handlers. Alle events voor een bepaald element worden uitgevoerd in de volgorde waarin ze zijn gezet. Een voorbeeld:
$('div a').click(function() {
// doe iets
});
$('div a').click(function(e) {
// doe nog iets
e.stopImmediatePropagation();
});
$('div a').click(function() {
// dit wordt nooit uitgevoerd
});
$('div').click(function() {
// dit wordt nooit uitgevoerd
});
Dit voorbeeld lijkt een beetje teveel van het goede maar je zult dit regelmatig tegen gaan komen. Hoe meer ingewikkelde code je schrijft hoe vaker nieuwe onderdelen events aan elementen zullen gaan hangen. Het is dan bijzonder handig als je weet hoe stopImmediatePropagation werkt!
return false
Gebruik return false alleen als je het resultaat van preventDefault() en stopPropgation() wilt en je code het toelaat om het standaard gedrag van een browser pas aan het einde van je callback te stoppen.
BOVENAAN, IN HET MIDDEN OF ONDERAAN
Omdat je tot nu toe altijd return false hebt gebruikt stond dit altijd aan het einde in je functie omdat hier na verder geen code meer hoefde te worden uitgevoerd. Met e.preventDefault() hebben we meer opties. Het kan op elke plek in de callback worden uitgevoerd. Maar waar kunnen we het dan het beste plaatsen?
1. Tijdens de ontwikkelfase moet het (bijna) altijd bovenaan staan
Wanneer je een AJAX formulier wilt versturen en dit geeft een Javascript error wil je natuurlijk niet dat het formulier je naar de volgende pagina stuurt voordat je de fout hebt gevonden.
2. In een live omgeving en je volgt het progressive enhancement principe, zet het dan onderaan in de callback of aan einde van je code.
Als je de gebruiksvriendelijkheid van een pagina wilt verbeteren door het gebruik van Javascript dan heeft het click event van je link of het submit event van je formulier als het goed is de juiste server side fallbacks voor wanneer een browser geen Javascript ondersteund (of uit heeft staan). Het voordeel hier is echter niet voor wanneer Javascript uit staat maar voor wanneer je code een fout bevat als Javascript aan staat. Een voorbeeld:
var data = {};
$('a').click(function(e) {
e.preventDefault(); // stop het standaard gedrag
// zorgt voor een fout want 'data.link' bestaat niet
$('body').append(data.link);
// De link op de a als server side fallback werkt nu niet meer en voor de gebruiker gebeurd er nu niets!
});
Nu een voorbeeld wanneer we preventDefault() onderaan plaatsen:
var data = {};
$('a').click(function(e) {
// zorgt voor een fout want 'data.link' bestaat niet
$('body').append(data.link);
// Deze regel wordt nog bereikt
// Maar de href op de link wordt als fallback gebruikt
e.preventDefault(); // stop het standaard browser gedrag
});
Hetzelfde geld voor het submit event voor formulieren, mits je een juiste fallback hebt gebruikt. Reken er nooit op dat je code altijd werkt. Het is veel beter om een goede fallback te hebben dan er vanuit gaan dat er nooit fouten zullen optreden!
3. In een live omgeving en alle functionaliteit maakt gebruik van Javascript
Het hoeft niet persé op de eerste regel te staan maar het moet zo vroeg mogelijk in je code logica worden aangeroepen. Wanneer een functionaliteit ten eerste al is toegevoegd met Javascript dan zou een fallback niet eens echt nodig moeten zijn. In dit geval kun je het daarom het beste zo vroeg mogelijk aanroepen zodat je geen onnodige # karaters in je URL krijgt of je pagina op en neer springt. Zorg er natuurlijk wel altijd voor dat je gebruikers genoeg feedback krijgen wanneer er iets fout gaat!
CONCLUSIE
Denk er aan dat je return false alleen gebruikt als het echt nodig is en zorg er voor dat je het standaard gedrag van een browser op de juiste plek in je callback stopt. Zet je code zo flexibel mogelijk op en stop met het onnodig gebruiken van return false!