jQuery
fonctionnalités avancées
    Rémi Prévost — ConFoo 2011
Rémi Prévost
 Développeur Web

@remi + http://remiprevost.com
But
de cette présentation
But




              Pas
      un tutoriel/pitch jQuery
But




              Survol
      des fonctionnalités moins connues
But




         Sa force
      Courbe d’apprentissage facile
$(".surprise").click(function() {
	 $(this).fadeOut("slow");
});
But




         Ensuite?
      Beaucoup plus de possibilités
But




      2006          2011

      1.0 → 1.5
        45 Ko → 208 Ko
But



 •    Utilitaires
 •    Événements
 •    Animations
 •    Manipulations
 •    Parsers
 •    Data
 •    Deferreds
Utilitaires
Pour sauver du temps
Utilitaires




  Implémentations
      natives
Utilitaires




                   $
     Ne touchent pas aux objets natifs
Utilitaires




              $.map
Modifie chaque élément d’un tableau
var noms = ["jack", "kate", "john", "james", "claire"]

$.map(noms, function(nom) { return nom.toUpperCase(); });
=> ["JACK", "KATE", "JOHN", "JAMES", "CLAIRE"]
$(".user-name").map(function() { return $(this).text(); } );
=> ["@remi", "@garno", "@jmlacroix", "@vincentroyc"]
Utilitaires




              $.grep
              Filtre un tableau
var    personnes = [
   {   nom: "Jack Sheppard", evil: false },
   {   nom: "Benjamin Linus", evil: true },
   {   nom: "Kate Austen", evil: false }
];

$.grep(personnes, function(p) { return p.evil });
=> [{ nom: "Benjamin Linus", evil: true }]

$.grep(personnes, function(p) { return p.evil }, false);
=> [{ nom: "Jack Sheppard", … }, { nom: "Kate Austen", … }]
Utilitaires




              $.inArray
          Vérifie la présence d’éléments
var numeros = [4, 8, 15, 16, 23, 42];

$.inArray(numeros, 16);
=> 3

$.inArray(numeros, 64);
=> -1
Utilitaires




              $.merge
              Fusionne deux tableaux
var tailies = ["libby", "bernard", "eko"];
var middlies = ["jack", "charlie", "eko"];

$.merge(tailies, middlies);
=> ["libby", "bernard", "eko", "jack", "charlie", "eko"]
Utilitaires




              $.extend
              Fusionne deux objets
function push_button(options) {
	 options = $.extend({
     delay: 108,
     input: [4, 8, 15, 16, 23, 42]
   }, options);
	 return "Wait for "+options.delay+" minutes.";
}

push_button({ delay: 64 });
=> "Wait for 64 minutes."
$.extend($.easing, {
	 customEasing : function(p, n, firstNum, diff) {
	 	 return firstNum - diff / p;
	 }
});
Utilitaires




 •   $.map
 •   $.grep
 •   $.inArray
 •   $.merge
 •   $.extend
Utilitaires




              Pas assez?
                Underscore.js
Utilitaires




 •   _.reduce
 •   _.uniq
 •   _.keys
 •   _.values
 •   _.times
Événements
  Lier et déclencher
Événements




                Base
             Événements faciles
$(".element").click(function() {});
$(".element").dblclick(function() {})

$(".element").mouseover(function() {});
$(".element").mouseout(function() {});

$(".element").keyup(function() {});
$(".element").keydown(function() {});
$(".element").keypress(function() {});
$(".element").bind("click", function() {});
$(".element").bind("dblclick", function() {})

$(".element").bind("mouseover", function() {});
$(".element").bind("mouseout", function() {});

$(".element").bind("keyup", function() {});
$(".element").bind("keydown", function() {});
$(".element").bind("keypress", function() {});
$(".element").bind({
  click: function() {},
  dblclick: function() {},
  mouseover: function() {},
  mouseleave: function() {}
});
Événements




             Arbitraires
Déclencher n’importe quoi (vraiment)
$.fn.bind("anything", function() {
	 // this
});

$.fn.trigger("anything");
$("#element").bind("anything", function() {
	 // this
});

$("#element").trigger("anything");
Événements




             Méthodes
              événementielles
$("#vote-count").bind("increase", function() {
  $(this).text(parseInt($(this).text) + 1);
});

$("#upvote-button").bind("click", function() {
  $("#vote-count").trigger("increase");
});
function increase_vote_count() {
  var elem = $("#vote_count");
  elem.text(parseInt(elem.text()) + 1);
}

$("#upvote-button").bind("click", increase_vote_count);
function VoteCount(selecteur) {
  this.element = $(selecteur);
  this.increase = function() {
    this.element.text(parseInt(this.element.text()) + 1);
  }
}

vote_count = new VoteCount("#vote-count");

$("#upvote-button").bind("click", function() {
  vote_count.increase();
});
Événements




             Arguments
        Encore plus personnalisables
$(".tweets").bind("add-tweet", function(event, tweet) {
   $(this).append("<li>"+tweet.text+" — @"+tweet.user+"</li>");
})

$(".tweets").trigger("add-tweet", [{
  user: "garno",
  text: "Tweeting live from @remi’s presentation at #confoo!"
}])
Événements




                 Tout
        peut être lié à un événement
var Island = { is_magic: true };

$(Island).bind("move", function() {
	 alert(this.is_magic); // true
});

$(Island).trigger("move");
Événements




             $.fn.live
        Pour les éléments inexistants
$("#tweets a.user").live("click", function() {
	 // Faire quelque chose
});




$(document).bind("click", function(event) {
	 if ($(event.target).is("#tweets a.user")) {
	 	 // Faire quelque chose
	 }
})
$(".user").live("click", function() {
	 // Faire quelque chose
});




$(document).bind("click", function(event) {
	 if ($(event.target).is(".user")) {
	 	 // Faire quelque chose
	 }
})
Événements




      $.fn.delegate
        Plus performant que $.fn.live
$("#tweets").delegate("a.user", "click", function() {
	 // Faire quelque chose
});




$("#tweets").bind("click", function(event) {
	 if ($(event.target).is("a.user")) {
     // Faire quelque chose
	 }
})
Événements




               $.fn.die
             Le contraire de « live »
$("#tweets a.user").bind("click", function() {
  // Quelque chose
});

$("#tweets *").unbind("click");
$("#tweets a.user").live("click", function() {
	 // Faire quelque chose
});

$("a.user").die("click"); // ne fonctionnera pas
$("#tweets a.user").die("click"); // fonctionne!
Événements




 $.fn.undelegate
   Le contraire de « delegate » (duh)
$("#tweets").delegate("a.user", "click", function() {
	 // Faire quelque chose
});

$("#tweets").undelegate("a", "click"); // ne fonctionne pas!
$("body").undelegate("#tweets a.user", "click"); // non plus!

$("#tweets").undelegate("a.user", "click"); // fonctionne!
Événements




        Namespaces
             Pour éviter la confusion
// contenu de application.js
$("#contenu a.user").bind("click", function() {
	 // Faire quelque chose avec un utilisateur…
});




// contenu de janalytics.js
$("a").bind("click", function() {
	 // Enregistrer ce “click”
});

$("a").unbind("click"); // retire *tous* les “click”
// contenu de janalytics.js
$("a").bind("click.jAnalytics", function() {
	 // Enregistrer ce “click”
});

$("a").unbind("click.jAnalytics"); // tous les “click”

$("a").unbind(".jAnalytics"); // tous les événements
Événements




 •   $.fn.bind
 •   $.fn.trigger
 •   $.fn.live + $.fn.die
 •   $.fn.delegate + $.fn.undelegate
 •   Namespaces
Animations
 Au-delà de « fadeOut »
Animations




               Base
             Animations faciles
$("#menu").slideUp();
$("#menu").slideDown();
$("#menu").slideToggle();

$("#fantome").fadeIn();
$("#fantome").fadeOut();
$("#fantome").fadeToggle();
Animations




    $.fn.animate
     Animations pour « power-users »
$("#element").animate({
	 left: "-=200px",
	 height: "toggle",
	 width: "toggle"
},{
	 duration: 1000,
	 easing: "linear",
	 complete: function() {},
	 step: function() {},
	 queue: false,
    specialEasing: {
	 	 height: "easeIn",
	 	 width: "easeOut"
    }
});
Animations




         $.fn.queue
             Gérer la file d’attente
$("#element-1")
.animate({ fontSize: "14em" })
.animate({ width: "+=200px" })
.animate({ height: "+=200px" });

$("#element-2")
.animate({ fontSize: "14em" }, { queue: false })
.animate({ width: "+=200px" })
.animate({ height: "+=200px" });
Animations




             $.fn.delay
              Animer avec un délai
$("#element")
.animate({ fontSize: "14em" })
.delay(1000)
.animate({ width: "+=200px" })
.delay(1000)
.animate({ height: "+=200px" });
Animations




             $.fn.delay
   Pas seulement pour les animations
$("#ajax-mais-tantot").delay(2000).queue(function() {
	 $(this).load("/ajax_content.html")
});
$("#element").bind("click", function() {
  $(this).delay(2000).queue(function() {
    $(this).css("background", "yellow");
  });
});
Animations




             $.fx.off
    Soyons gentils avec les plus lents
$.fx.off = true;
Animations




 •   $.fn.animate
 •   $.fn.queue
 •   $.fn.delay
 •   $.fx.off
Support
Tester la compatibilité du navigateur
Support




          $.browser
           Vous devriez l’éviter
if ($.browser.webkit) {
	 // Chose que seulement Webkit supporte (pour l’instant)
} else if ($.browser.mozilla) {
	 // Chose que seulement Gecko supporte (pour l’instant)
} else if ($.browser.msie) {
	 // Chose que seulement IE supporte (pour l’instant)
}
Support




     Implémentation
          Pas réputation
if (typeof window.WebSocket != "undefined") {
	 // Quelque chose de cool avec les sockets
} else {
	 // Quelque chose de cool en… AJAX?
}
Support




          $.support
          Vous devriez le modifier
$.support.ajax
$.support.boxModel
$.support.hrefNormalized
$.support.opacity
$.extend($.support, {

  webSockets : (function() {
    return typeof window.WebSocket != "undefined"
  }).call(),

  canvas : (function() {
    var canvas = document.createElement("canvas");
    return !!(canvas.getContext && canvas.getContext("2d"));
  }).call()

});
if ($.support.canvas) {
	 // Quelque chose de cool avec <canvas>
} else {
	 // Quelque chose de cool… des images?
}
Support




          Pas assez?
             Modernizr
Support




 •   Modernizr.borderradius
 •   Modernizr.geolocation
 •   Modernizr.localstorage
 •   Modernizr.draganddrop
 •   Modernizr.addTest
Manipulations
 Modifier le DOM facilement
Manipulations




                    Base
                Manipulations faciles
$(".article").append("<div class="share-this"></div>");

$("<strong>!!!</strong>").insertAfter(".important");

$("Attention! Spoiler").insertBefore(".spoiler");

$(".no-javascript-message").remove();
Manipulations




    $("<foo />")
    Création de noeuds DOM jQuery
var ennemi = $("<div />", {
	 "class": "ennemi",
	 text: "|---0-0---|",
	 data: { name: "Blinky" },
	 click: function() {
	 	 alert($(this).data("name")+ " a été cliqué!");
	 }
});

ennemi.appendTo(".planche-de-jeu")
ennemi.css("background", "cyan");
Manipulations




            $.fn.clone
                Cloner des noeuds
$(".dolly").clone().appendTo("#ferme");

$(".dolly").clone(true).appendTo("#ferme");

$(".dolly").clone(true, false).appendTo("#ferme");
Manipulations




       $.fn.detach
       Supprimer du DOM seulement
$("#lacet").bind("boucler", function() { alert("yay!") });

var lacet = $("#lacet").detach();
// #lacet n’est plus dans le DOM

lacet.appendTo("#souliers");
// #lacet est de retour dans le DOM

lacet.trigger("boucler"); // yay!
Parsers
Analyseurs intégrés à jQuery
Parsers




  $.parseJSON
          Analyse de JSON
var data = '{ "name": "John Locke" }';
var person = $.parseJSON(data);

person.name
=> "John Locke"
Parsers




     $.parseXML
  Parcourir du XML comme du HTML
var data = "<personne><nom>John Locke</nom></personne>";
    data += "<personne><nom>James Ford</nom></personne>";

var personnages = $($.parseXML(data));

personnages.find("personne").map(function() {
  return $(this).find("nom").text().split(" ")[0];
});
=> ["John", "James"]
Data
Stocker des données
Data




       $.fn.data
Des données dans des objets jQuery
$(".unstoppable-button").data("count", 0);

$(".unstoppable-button').bind("click", function() {
  $(this).data("count", $(this).data("count") + 1);
});
// Afficher le “count” du bouton
$(".report-button").bind("click", function() {
  var count = $(".unstoppable-button").data("count");
  alert("The other button has been clicked "+count+" times");
});

// Remettre le "count" à zéro
$(".reset-button").bind("click", function() {
  $(".unstoppable-button").data("count", 0);
});

// Supression de toutes les méta-données du bouton
$(".unstoppable-button").removeData();
Data




       Événements
          de données
// Modification du "get"
$(".unstoppable-button").bind("getData", function(event, key) {
  if (key == "count") {
    return jQuery.data(this, key) * 2;
  }
});

$(".unstoppable-button").data("count", 10);
$(".unstoppable-button").data("count");
=> 20
$("#slideshow").data("position", 0).bind({
  "forward" : function() {
     $(this).data("position", $(this).data("position") + 1);
  },
  "backward" : function() {
     $(this).data("position", $(this).data("position") - 1);
  },
  "changeData", function(event, key, value) {
     if (key != "position") { return true; }
     if (value >= $(this).children().length) {
       jQuery.data(this, "position", 0);
     } else if (value == -1) {
       jQuery.data(this, "position", $(this).children().length - 1);
     }
  }
});

$("#slideshow").trigger("forward"); // position=1
$("#slideshow").trigger("forward"); // position=2
$("#slideshow").trigger("forward"); // position=0
$("#slideshow").trigger("backward"); // position=2
Data




       HTML5 data
       Stocker de façon sémantique
Data




           Avant
  Des données dans les attributs HTML
<!-- HTML -->
<li rel="5" class="story"><a href="#">Show</a></li>



// Javascript
$("li.story a").click(function() {
  var id = $(this).parent().attr("rel");
  $("#content").load("/stories/"+id);
});
<!-- HTML -->
<li rel="5|remi" class="story"><a href="#">Show</a></li>



// Javascript
$("li.story a").click(function() {
  var data = $(this).parent().attr("rel").split("|");
  var id = data[0];
  var user = data[1];
  $("#content").load("/"+user+"/stories/"+id);
});
Data




         Après
       Le plugin $.metadata
<!-- HTML -->
<li class="story {id:5}"><a href="#">Show</a></li>

// Javascript
$("li.story a").click(function() {
  var data = $(this).parent().metadata();
  $("#content").load("/stories/"+data.id);
});
<!-- HTML -->
<li class="story {id:5,user:'remi',category:6}"><a
href="#">Show</a></li>



// Javascript
$("li.story a").click(function() {
  var data = $(this).parent().metadata();
  $("#content").load("/"+data.user+"/stories/"+data.id);
});
Data




       Maintenant
       Les attributs « data » de HTML5
<!-- HTML -->
<li data-id="5" class="story"><a href="#">Show</a></li>



// Javascript
$("li.story a").click(function() {
  var id = $(this).parent().data("id");
  $("#content").load("/stories/"+id);
});
<!-- HTML -->
<div data-remote="foo" class="block-1"></div>
<div data-remote="true" class="block-2"></div>

// Javascript
$("div.block-1").data("remote");
=> "foo"

$("div.block-2").data("remote");
=> true
Data




 •   $.fn.data
 •   Événements « getData » + « setData »
 •   $.fn.attr
 •   $.metadata
 •   $.fn.data + HTML5
Deferred
Gestion facile de callbacks
Deferred




           $.Deferred
           Résolution, rejet et attente
var request = $.Deferred();

request.done(function() { alert("succes!"); });
request.fail(function() { alert("erreur!"); });

request.resolve();
=> "succes!"

// OU

request.reject();
=> "erreur!"
Deferred




Deferred AJAX
           Exemple classique
var request = $.get("/feed")

request.done(function(data) {
	 console.log(data.user.name);
});

request.fail(function(error) {
	 console.log(error.message);
});
var request = $.get("/feed")

request.done(function(data) {
	 console.log(data.user.name);
});

request.fail(function(error) {
	 console.log(error.message);
});
var request = $.get("/feed")

request.done(function(data) {
	 console.log(data.user.name);
});

request.fail(function(error) {
	 console.log(error.message);
});
Deferred




           $.fn.then
           Déclarer des callbacks
var request = $.get("/feed");

request.then(
	 function() { alert("done!"); },
	 function() { alert("failed!"); }
);
Deferred




             $.fn.when
           Gérer des deferred multiples
var request = $.get("/feed")

$.when(request)
.then(
   function() { alert("done!") },
   function() { alert("failed!") }
);
var request1 = $.get("/feed")
var request2 = $.get("/users")

$.when(request1, request2)
.then(
	 function() { alert("everything has succeed!") },
	 function() { alert("something has failed.") }
);
Deferred




           Possibilités
            Un wrapper d’API
// Exemple par Michael Bleigh (intridea.com)
Twitter = {
  search: function(query) {
    var dfr = $.Deferred();
    $.ajax({
      url: "http://search.twitter.com/search.json",
      data: { q: query },
      dataType: "jsonp",
      success: dfr.resolve
    });
    return dfr.promise();
  }
}

Twitter.search("#confoo").done(function(data) {
  alert(data.results[0].text);
});
Deferred




 •   $.Deferred
 •   $.fn.resolve
 •   $.fn.reject
 •   $.fn.then
 •   $.fn.when
Bref
Fonctionnalités avancées
Ressources




 •   api.jquery.com
 •   james.padolsey.com
 •   jaubourg.net
 •   ejohn.org/apps/workshop/adv-talk
Questions?
 Commentaires?
     @remi

jQuery — fonctionnalités avancées