JQuery introductie voor developers

 
07 januari 2010

Dit is een korte Nederlandse introductie in jQuery. JQuery wordt steeds meer gebruikt om webpagina’s interactiever te maken, Microsoft omarmt het inmiddels in .Net en het wordt gebruikt door veel sites, onder andere de site van Barack Obama (en nu dus het Witte Huis). Dit artikel is bedoeld om iemand inzicht te geven in waar jQuery toe in staat is, en hoe je er relatief netjes en snel code voor kunt schrijven.

Wat is jQuery?

jQuery is een javascript-library, die het makkelijker maakt om html-pagina’s aan te passen, en dus functionaliteit te verbeteren/toe te voegen. Een van de zienswijzen bij de jQuery-makers is dat javascript ‘unobtrusive’ zou moeten zijn, dus dat het de html-pagina alleen verrijkt, en dat de pagina nog zou moeten werken zonder javascript. Maar in de praktijk zal je het redelijk snel zo kunnen inzetten (vooral via ajax-calls) dat dit niet meer geldt.

Een andere zienswijze is dat het in alle browsers hetzelfde zou moeten werken. Dit geldt niet helemaal voor alle versies, op de homepage van jQuery staat welke browsers vanaf welke versie ondersteund worden. Ook moet het natuurlijk snel werken en niet al te groot zijn. Voor dat laatste is het mooi meegenomen dat verreweg de meeste browserverschillen meegenomen worden in de jQuery-library. Verder zijn de meeste methodes compact en goed leesbaar.

JQuery is ook vrij makkelijk uitbreidbaar, er zijn veel plugins voor jQuery beschikbaar. Deze geven al een goed idee van wat voor dingen er mogelijk zijn. Zie bijvoorbeeld hier of hier, maar even zoeken en je vind meer voorbeelden.
De plugins bestaan meestal uit 1 javascript-bestand en wat css (met soms wat plaatjes) voor de opmaak.

Voorwerk

Een basiskennis van HTML, CSS en javascript is prettig om te hebben als je met jQuery aan de slag gaat. Ik neem even aan dat degene die dit leest voldoende weet van de HTML/CSS (voor CSS: betekenis van het cascading, en de betekenis van ‘#’, ‘.’, ‘,’, ‘:’ en ‘>’ zijn wel handig om te weten). Voor de javascript is de engelse wiki-pagina een goed punt om te starten. Het belangrijkste is dat functies ‘first-class citicens’ zijn, het gebruik maakt van prototypes voor classes, en dat het een dynamisch en ‘weakly-typed’ taal is. Ook is het makkelijk om een object met willekeurige properties aan te maken:
var urlObject = {url: "http://www.sogyo.nl"}; zorgt ervoor dat urlObject een object is met 1 parameter, url. Ook een lijst is zo gemaakt:
var vowels = ['a','e','i','o','u'];

Verder is (zoals op de wiki staat) debuggen inderdaad belangrijk. Naar mijn mening de beste tool hiervoor is nog altijd firebug, ook al beperk je je dan tot firefox. Andere browsers hebben vaak soortgelijke tools ingebakken, maar intuitief is dit nog steeds een van de besten. Het bied je de mogelijkheid om errors/warnings te zien in de console, de dom-boom te bekijken, te kijken hoe de CSS van een element wordt opgebouwd (en de CSS valt on-the-fly te veranderen), breakpoints te zetten in javascript en te kijken hoe snel alles opgehaald wordt.

Als je meer fan bent van logging, kun je hier gebruik maken van console.log(objectOmTeLoggen), wat logischerwijs het objectOmTeLoggen logt naar de console (waarna je erop kunt klikken om te kijken wat er precies in zit). Om helemaal correct te zijn kun je eerst controleren of het console-element ook bestaat, anders kunnen fouten optreden.
Ook zijn in de console de request en response van een AJAX-call goed te zien en de tijd die zo’n call duurt, niet onbelangrijk. En als laatste kun je ook even testen bij de console of een bepaald javascript stukje de gewenste uitkomst heeft, door het gewoon in te vullen. Ik vond het zelf onder andere erg handig bij het controleren of een regular expression wel werkte.

Als laatste laten browsers vrij veel toe wat betreft javascript, en dus is een afspraak over hoe je nette code schrijft/controleert aan te raden. Hierbij kan JSLint helpen.

JQuery basis

Het eerste wat je meestal wilt doen is de tag(s) te pakken krijgen die je wilt veranderen/verrijken. Je weet pas zeker dat de hele HTML-pagina (in principe nog zonder plaatjes) is ingeladen nadat het ready-event is afgevuurd van het html-document, dus dat is het eerste goede moment om veranderingen aan te brengen. Dit doe je op de volgende manier:

$(document).ready(function(){
  // Code hier
});

Vervolgens kun je via de basis-functie $() de elementen pakken die je wilt. Dit gaat via een CSS-syntax, waarbij CSS3 ook ondersteund wordt (en meer). Dus om bijvoorbeeld elke even rij van elke tabel te krijgen gebruik je $('table tr:nth-child(even)'), zoals je dat in CSS3 ook zou kunnen doen.  Wat je terugkrijgt is een jquery-object die bestaat uit een array van tags die aan je query voldoen, en een grote hoeveelheid functies die je kunt uitvoeren op de tags in de array. In dit geval wil je waarschijnlijk elke even rij een kleurtje geven, wat waarschijnlijk beter via CSS kan maar ook hier mogelijk is:

$('table tr:nth-child(even)')
   .css('background-color', '#ddd')
   .css('color', 'rgb(0,40,244)');

Het gevolg is dat van elk element de style wordt aangepast om dit te bereiken. Hier zie je ook dat jQuery chaining toepast: Als je een methode hebt die alleen iets verandert aan het element zelf (en typisch in gewone Java of .Net-code void zou teruggeven), geef dan het object zelf terug zodat je daarop meteen weer een methode kan afvuren. Bij teveel methodes achter elkaar uitvoeren wordt dit natuurlijk onoverzichtelijk, dus pas daarvoor op.
Bij deze methode en bepaalde andere methodes is het ook mogelijk om een object met de goede properties mee te geven, dan wordt deze code:

$('table tr:nth-child(even)').css({
   'background-color': '#ddd',
   color: 'rgb(0,40,244)'
});

Tijd voor een ander voorbeeld: stel dat we de meerdere divs hebben, en we willen dat alle div’s dezelfde breedte en hoogte hebben, waarbij 1 div met id ‘leading’ de dimensies bepaalt. Dan krijgen we de code:

var leading = $('#leading');
$('div').height(leading.height())
        .width(leading.width());

Hier zien we dat jQuery veel ‘shorthands’ gebruikt, soms voor dingen die ook vrij kort anders kunnen worden geschreven. De basisfunctie voor welk attribuut dan ook van een tag op te vragen is dan ook $j.attr('attribuutnaam') (als er meer elementen in $j zitten, geeft het de attribuut van het eerste element terug), en voor het zetten/veranderen is deze $j.attr('attribuutnaam','nieuwe waarde').

Voor de rest zijn er nog veel functies die de xml-boom doorlopen, dus waardoor je bijvoorbeeld de ouders/kinderen op kan vragen, en is er iets ingebakken voor animaties (feitelijk het meerdere keren hertekenen van een object met tussenliggende waarden, eye-candy en dus maakt een groot deel van plugins/sites hier gebruik van).
Een methode die wat minder intuitief maar wel nuttig is: de closest(selector)-methode geeft zichzelf terug als hij aan de selector voldoet, of de parent indien die voldoet, of de parent daarvan etcetera.

Wat er precies mogelijk is qua css-achtige tags (selectors) en functies is te vinden in de documentatie van jQuery. Meestal zijn hier ook demo’s bij de documentatie te vinden.

Ingewikkelder dingetjes

We hebben ook nog event-listeners. Hiervoor heb je 2 basisfuncties: $j.bind('eventName', someFunction); en $j.live('eventName', someFunction);. De eerste bindt de functie aan het gegeven event bij alle elementen die in het jquery-object zitten, de tweede zorgt ervoor dat ook alle later toegevoegde elementen die aan je selector voldoen worden meegenomen. Dus bijvoorbeeld

$('a').bind('mouseover', function(e) {
  $(this).html("Welkom");
});

Zorgt ervoor dat als je over een tag gaat op de huidige pagina, de tekst verandert naar “Welkom”. this is het element waarop je de event handler hebt gezet, aangezien dit geen jQuery-object is moeten we dat er even van maken (this.innerHtml = "Welkom"; had hier ook gewerkt).
Maar dit gebeurt dus niet met tags die je later toevoegt, daarvoor had je live moeten gebruiken. Er zijn voor de bind-methode genoeg shorthands, waarvan hover en click wel het meeste voorkomen. Events worden trouwens ook naar bovenliggende elementen doorgeschoten, als je zeker wilt weten wat het target was moet je het anders aanpakken:

$('#containingDiv').click(function(e) {
  var clickedElement = $(e.target);
  clickedElement.append(',clicked!');
  return false;
})

e is nu het event dat afgeschoten is, en in javascript is e.target het element waar daadwerkelijk op geklikt is. Dus we voegen nu wat tekst toe aan het element waarop geklikt is. return false; zorgt er trouwens voor dat hij niet naar nog hoger bovenliggende elementen wordt gestuurd.

Het volgende ding is een iets uitgebreide demo voor de map-methode in de jQuery documentatie:

$("#result").click(function(){
   $(this).html(
      $("input").map(function(){
         return $(this).val();
      }).get().join(", "));
});

Deze is wat ingewikkelder (met opzet, je ziet hier al dat goede uitlijning belangrijk is, en je dit eigenlijk begrijpelijker wilt maken). De click-methode zet weer een onclick-functie op het element.
Hier zie je een beetje wat je terugkrijgt. Het is dus feitelijk een opsomming van alles wat ingevuld is in het formulier. Maar hoe gaat dat nu eigenlijk?

We gaan de html vervangen binnen de result-tag. Maar wat is hier het argument? $("input") geeft een jquery-object van alle inputs op de pagina terug, en we gebruiken een functie ‘map’ om de array in dit jquery-object om te gooien naar een andere array. Dit wordt bepaald door de functie, en die gebruikt weer $(this), wat blijkbaar hier het huidige element is in de array. Wat je teruggeeft is het element dat het huidige element gaat vervangen. In dit geval de val()-methode, wat de value van de input teruggeeft. Vervolgens wordt via get() de javascript-array opgevraagd die bij het jQuery-object hoort en via de javascript-functie join wordt daar één string van gemaakt.

Verder zijn de meeste jQuery-demo’s nu te stappen, vooral de demo’s bij animate zijn nog wel nuttig om even te bekijken, eyecandy blijft belangrijk.

Uitbreidingen

Soms wil je net een andere methode hebben voor jQuery dan beschikbaar is, of wil je je eigen plugins gaan schrijven. Dit doe je voornamelijk door de lijst met functies bij een jQuery-object uit te breiden. Eentje die mij een keer van pas kwam is de volgende:

$.fn.exists = function(){
   return this.length>0;
};

Ik wil controleren of er een element in het jQuery-object zit, en als ik het aan de hand van een id heb opgevraagd vind ik het duidelijker om te vragen of hij inderdaad bestaat. De $.fn zorgt ervoor dat hij de functie inderdaad opneemt (prototyping kan handig zijn).

Dit gaat natuurlijk ook op voor moeilijker extra functies. De volgende methode is om een tag cloud te genereren aan de hand van wat gegevens die of uit een ajax-call komen of meegegeven worden. De gegevens worden aangeleverd als een array of JSON-gegevens, en bestaat uit een lijst met woorden en hun bijbehorende gewichten.

jQuery.fn.createTagCloud = function() {
 var args = arguments[0] || {};
 var self = this;
 createTags = function(element, data) {
  var min = -1, max = 0;
  for (var i = 0; i< data.length; i++)
   if (data[i].weight > max) {
    max = data[i].weight;
   }
   if (data[i].weight < min || min == -1) {
    min = data[i].weight;
   }
  }
  if (max - min == 0) {
   max += 1;
  }
  element.html('');
  for (var i = 0; i<data.length; i++) {
   var tag = data[i];
   var relative = (args.fontMaxSize - args.fontMinSize)/(max - min);
   var size = (relative * (tag.weight - min)  + args.fontMinSize) + 'px';
   var span = $('<span></span>')
     .css('white-space', 'nowrap')
     .css('font-size', size )
     .html(tag.name);
   element.append(span).append(' ');
  }
 }
 if (!args.data) {
  $.ajax({
   url: args.url,
   data: "id=" + args.id,
   dataType: "json",
   success: function(data) {
    createTags(self, data)
   }
  });
 } else {
  createTags(self, args.data)
 }
};

En deze functie kan dan aangeroepen worden op de volgende manier:

$('#tagcloud').createTagCloud({
   data: [{name="java", weight=10},
          {name="jQuery", weight=7},
          {name="javascript", weight=4},
          {name=".Net", weight=3}],
   fontMinSize: 12,
   fontMaxSize: 24
});

Er zijn enkele dingen nieuw. De eerste is is het gebruik van args om argumenten op te geven, maar dit is eigenlijk een iets ondoorzichtige manier in javascript, en niet jQuery-gerelateerd.
Ook het creëren van een html-element is nieuw, en deze manier geldt vooral voor jQuery 1.3 en hoger (hiervoor was dit langzamer, $(document.createElement('span')) was een orde sneller). Ik vind dit persoonlijk iets mooier.
Als laatste is de ajax-call nieuw. Dit wordt eigenlijk het aanroepen van een vrij normale methode. Alleen het meegeven van de functie bij succes is wel interessant (er is ook een functie error mee te geven). Deze krijgt het teruggekregen bericht automatisch mee, en dus is dit de beste plaats om dat wat je van plan was te gaan doen.

Als je dingen te ingewikkeld maakt moet je je opeens bezig houden met performance. Dit artikel gaat over hoe je echt rekening houdt hiermee, en geeft je ook een idee over hoe efficiënt om te gaan met initialisatie. Het artikel is sowieso het lezen waard, maar echte performanceproblemen zul je voor een relatief simpele site niet tegenkomen.


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Development, Overige talen en platformen

Tags: , ,


Reacties (7)

  • Patrick schreef:

    Off-Topic: North Face heeft de beste spam-link geplaatst die ik ooit heb gezien!

    On-Topic: Zeker een mooi artikel. Heb wel eens eerder JQuery gezien, maar niet veel mee gedaan. Ik vond de “.live” functie een leuke injectie die ik nog niet kende evenals de toevoeging van closures voor het JQuery framework.

    Geplaatst op 30 oktober 2012 om 14:29 Permalink

  • johan scholten schreef:

    Nog even een paar toevoegingen:
    enkele interessante methodes: http://net.tutsplus.com/tutorials/javascript-ajax/20-helpful-jquery-methods-you-should-be-using/

    En nog een interessant gebruik van de jQuery UI autocomplete: http://net.tutsplus.com/tutorials/javascript-ajax/how-to-use-the-jquery-ui-autocomplete-widget/
    (klein deel PHP, maar dat is te vervangen :P)

    Geplaatst op 02 april 2010 om 12:21 Permalink

  • Johan Scholten schreef:

    Bedankt. Ik ben niet echt toegekomen aan de echte inhoud (hoe je het logisch opzet), maar de tekst was nu al wat lang.

    Ik heb het trouwens gemaakt met mezelf van 1.5 jaar geleden in gedachten (Dus kennis van programmeertalen als java en python, maar weinig javascript/css ervaring). En zelfs dan zou ik het 3 keer door moeten lezen om het echt te vatten.
    Maar volgens mij brengt het wel over hoe jQuery een beetje in elkaar zit, ook al is het doorlezen van de documentatie om een indruk te krijgen van de functies van jQuery erg handig als je er echt mee aan de slag wilt.

    Geplaatst op 29 januari 2010 om 13:33 Permalink

  • Matthijs Mast schreef:

    Handig artikel als je niet bekent bent met jscript of andere script gerelateerde talen (ondergetekende)

    Geplaatst op 19 januari 2010 om 13:02 Permalink

  • Andries Nieuwenhuize schreef:

    Leuke intro.

    Geplaatst op 11 januari 2010 om 14:05 Permalink

    • Ibrahim schreef:

      The new Live Elements are really cool. Just this lttile effort and life is much easier! I think the complex explanation you’re talking about is rather simple:Right at the moment $(“p”).click() is fired it searches for all p-Elements and binds/adds an event to these (triggering the event results e.g. in calling a function).The function does whatsoever.If you add an other p-Element afterwards there is nothing in the system that links the previous action “bind function” to event of adding a new p-Element.And that is usally wise because maybe you don’t want all the new p-Elements to be linked to the function.Nevertheless it is extremly usefull to be able to do that.So long,JT

      Geplaatst op 04 juli 2012 om 16:28 Permalink

  • Mooi uitgebreid overzicht, Johan!

    Geplaatst op 08 januari 2010 om 16:49 Permalink