Trucsweb.com

Trucsweb.com

Javascript

Sans jQuery

RDFFav

jQuery : Équivalent Javascript

Peut on enfin se passer de la librairie jQuery? D’ici là, voici un document de référence des plus exhaustif.jQuery, ECMAScript, querySelector, classList, JSONjQuery : Équivalent Javascript

Légé et sans jQuery

Ça fait déjà un bon bout de temps que je veux faire ce tutoriel, ou plutôt ce document de référence. Quoi de plus insolite (voire inutile pour les vieux webmestres comme moi, qui ont vu le web d’avant jQuery) que de tomber sur des codes de rien du tout qui requièrent néanmoins une vaste librairie pour s’exécuter! J’ai bien compris le besoin et même abdiqué au besoin pour passer moi-même à jQuery. jQuery a permis de populariser moult bidules, menu déroulant, accordéon, boîte de dialogue et son fameux slider. Que dire des ses nombreux effets.

Mais c’est surtout pour la portabilité et la compatibilité que jQuery était dur à battre. Il a aussi pour un temps palier à un manque de flexibilité du CSS3. J’irais jusqu’à dire que jQuery est derrière la motivation de plusieurs nouveautés du CSS3. Il faut aussi dire pour sa défense qu’il est utilisé à peu près partout. Et c’est quant à moi un défaut. Si les standards sont indispensables en programmation, les monopoles sont quant à eux dangereux.

C’était avant le duo CSS3 / HTML5!

Le paysage du Web script est en train de prendre un nouveau virage. Ou plutôt de revenir au chemin d’origine. On n’est pas encore à remplacer la librairie des anciens projets mais on peut recommencer à créer des sites Web en pure Javascript en utilisant uniquement les outils dit natif du navigateur, sans devoir charger d’immense librairie quasi inutile dans la grande majorité des cas... Non seulement le CSS3 à rendu accessible un grand nombre d’effets et de fonctionnalités qui compense largement jQuery. Plusieurs méthodes ou raccourcies historiquement associées à jQuery sont maintenant possibles avec le nouveau Javascript du HTML5. Le ECMAScript 5. Par exemple le sélecteur querySelectorAll. Personnellement j’adore l’objet classList pour manipuler les classes...

Compatibilité

Navigateurs avec le support ECMAScript 5 (ES5) : Firefox 4, Chrome 19, Safari 6, Opera 12.10 et Internet Explorer 10.

// IE9 est aussi compatible mais seulement en « mode strict ».
// JavaScript
"use strict";
// Restrictions au niveaux des variables globales
// des nombres octaux, de la méthode eval()...
// À éviter avec des librairies. Sinon, vaut mieux s’y préparer!

// Exemple pour s’assurer qu’une méthode est compatible :
<script>
if (document.querySelector){
 // Compatible
} else {
// Incompatible
}
</script>
Simple, puissant et gratuit. Mais :
  1. Grosse librairie, lourde;
  2. Trop souvent inutile quand on n’a besoin d’un seul livre;
  3. À la merci d’un tierce partie, jQuery Foundation;
  4. Plusieurs équivalents en pure CSS3 alors que le Javascript se désactive;
  5. Et surtout, à la merci de moteurs externes au navigateur. Question performance, je préfère de loin les outils natifs du navigateur.
Comparaison

Vous n’êtes pas encore convaincu? Jetez un œil sur cette comparaison des plus éloquentes par Craig Buckler. Notamment la méthode document.getElementsByClassName et son score tout à fait spectaculaire jusqu’à 60 fois plus rapide!

Code Duré
// jQuery 2.0
var c = $("#comments .comment");
4,649 ms
// jQuery 2.0
var c = $(".comment");
3,437 ms
// native querySelectorAll
var c = document.querySelectorAll("#comments .comment");
1,362 ms
// native querySelectorAll
var c = document.querySelectorAll(".comment");
1,168 ms
// native getElementById / getElementsByClassName
var n = document.getElementById("comments");
var c = n.getElementsByClassName("comment");
107 ms
// native getElementsByClassName
var c = document.getElementsByClassName("comment");
75 ms
Voilà donc une liste d’équivalent Javascript fort pratique, près à l’emploi :

Un des exemples les plus rependu est le sélecteur jQuery qui a l’avantage d’être simple, et de régler plusieurs détails. Contrairement au nœud ou la liste de nœuds du DOM retourné par le Javascript. Via l’ID, la classe ou la balise. Notez que jQuery utilise un moteur de sélecteur (Sizzle) qui double le travail déjà effectué par le navigateur! On connait déjà :

// jQuery
$("#monID").get(0); // Retourne seulement le premier nœud
// JavaScript
document.getElementById('monID');
// Retourne seulement le premier nœud

// jQuery
$('.maClasse');
// JavaScript
document.getElementsByClassName('maClasse');
// Retourne une liste de nœuds

// jQuery
$('div');
// JavaScript
document.getElementsByTagName('div');
// Retourne une liste de nœuds

On peut d’ailleurs simuler le populaire sélecteur jQuery « $ » : (voir l’exemple de Jeffrey Way pour une solution complète)

// JavaScript
var $ = function(el) {
  return document.querySelectorAll(el);
};
// Usage 
$('.maClasse');
Sélecteur querySelector / querySelectorAll
Liste de nœuds statiques

Un staticNodeList est une collection statique des éléments du DOM. Statique puisqu’elle n’est pas affecté par les changements dynamiques apportés sur l’arbre du document, comme la suppression, l’ajout et la modification d’un de ces éléments.

Entièrement natif, plus robuste et beaucoup plus rapide, les méthodes querySelector() et querySelectorAll() permet de saisir un sélecteur CSS via un paramètre et retourner les éléments sélectionnés comme des nœuds du DOM. La méthode querySelector cherche dans l’ensemble ou dans une partie du document et retourne seulement le premier nœud de la staticNodeList (ou null).

La puissante méthode querySelectorAll() se comporte exactement comme querySelector() à l’exception qu’elle ne retourne pas seulement le premier élément qui correspond au sélecteur CSS spécifié par le paramètre, mais la liste des nœuds au complet comme un « staticNodeList » avec la propriété length pour obtenir le nombre d’éléments sélectionnés.

Compatible Firefox 3.1+, IE8+ (seulement en mode standard) et Safari 3.1+.

querySelector
// JavaScript 5
// Retourne la première instance du ID « monID »
document.querySelector('#monID');
// Retourne la première instance de la classe « maClass »
document.querySelector('.maClass');
// Retourne la première instance de la balise P avec la classe class="petit"
document.querySelector('p.petit');
// Retourne la première instance de le premier élément de type img (image) à l’intérieur du "#conteneur"
document.querySelector('#conteneur img:nth-of-type(1)');
// Retourne la première instance coché des boîtes à cocher à l’intérieur du "#conteneur"
document.querySelector('#conteneur input[type="checkbox"]:checked');
// Retourne le formulaire dont l’action est action="form.asp"
document.querySelector('form[action="feedback.php"]');
// Retourne la première image dont l’adresse (src) débute par "http"
document.querySelector('img[src^="http"]');
// Retourne la première image dont l’adresse (src) se termine par ".gif"
document.querySelector('img[src$=".gif"]');
// Retourne la dernière instance d’une liste à puce.
document.querySelector('ul.maListe li:last-child');

// Retourne la première instance de l’élément #evenement ou #galerie
// selon celui trouvé en premier dans l’arbre du document
document.querySelector('#evenement, #galerie');
querySelectorAll
// Retourne la liste de nœuds des instances de la classe « maClass »
document.querySelectorAll('.maClass');
// Retourne la liste de nœuds des instances de la balise « div »
document.querySelectorAll('div');
// Retourne la liste de nœuds « sélectionnés » des options de chaque élément SELECT
document.querySelectorAll('option[selected="selected"]');
// Retourne la liste de nœuds des premières cellules du tableau #monTableau
document.querySelectorAll('#monTableau tr>td:nth-of-type(1)');
// Retourne la liste de nœuds de tous les hyperliens qui débute par "http"
document.querySelectorAll('a[src^="http"]');
// Retourne la liste de nœuds de tous les hyperliens qui contient la chaine "trucsweb"
document.querySelectorAll('a[href*="trucsweb"]'); 

// Retourne la liste de nœuds des éléments #evenement et #galerie (inclusivement)
document.querySelectorAll('#evenement, #galerie');

// jQuery « find »
$(’#conteneur’).find('li');

// JavaScript
document.querySelectorAll('#conteneur li');
// JavaScript beaucoup plus rapide et surtout compatible CSS2.1 (IE8)
document.getElementById('conteneur').getElementsByTagName('li');

Todd Motto suggère même une petite fonction _ pour conforter les accros au jQuery. Je passe mon tour ;-)

var _ = function ( elem ) {
  return document.querySelectorAll( elem );
}
// Utilisation
var oMaClasse = _('.maClasse');
La méthode getElementsByClassName

Il y a aussi une nouvelle méthode à signaler quand il s’agit de recueillir facilement des éléments du document HTML. La méthode getElementsByClassName(), native du navigateur, est encore plus rapide. Comme son nom l’indique, cette méthode renvoie une collection d’élément(s) en fonction de leur nom de classe partagée. Il est préférable d’utiliser cette méthode au lieu des méthodes document.querySelectorAll() / document.querySelectorAll() lorsque vous désirez obtenir seulement la liste d’éléments identifiés par une classe. Compatible FF3, Opéra 9.5 et Safari 3. Malheureusement, IE8 n’est pas compatible.

// Retourne les éléments de classe "maClasse"
document.getElementsByClassName("maClasse");

// Retourne les éléments de classe "maClasse" et "taClasse" (inclusivement)
document.getElementsByClassName("maClasse taClasse");
Manipulation du « Document object model » (DOM)

Rien de nouveau à ce niveau, d’ailleurs jQuery utilise les méthodes natives du JavaScript se contentant de simplifier les instructions à la manière de la petite fonction de Todd Motto (voir plus haut).

// Ajout de contenu HTML au DOM
// jQuery utilise la méthode innerHTML
$("#conteneur").append("<p>Paragraphe ajouté</p>");

// Javascript
document.getElementById("conteneur").innerHTML += "<p>Paragraphe ajouté</p>";

// Ou mieux encore pour l’amateur du XML en moi, quoique plus complexe. Travailler directement sur les nœuds de l’arbre du document.

var p = document.createElement("p");
p.appendChild(document.createTextNode("Paragraphe ajouté");
document.getElementById("conteneur").appendChild(p);

// Ajout de contenu HTML à un élément précis
// jQuery
$("<div id=boite></div>").appendTo('body');

// Rien de nouveau en JavaScript
var div = document.createElement("div");
div.id = "boite";
document.body.appendChild(div);


// Suprimer un nœud du DOM avec jQuery
$("#conteneur").empty();

// En JavaScript
document.getElementById("conteneur").innerHTML = null;
// ou
var c = document.getElementById("conteneur");
while (c.lastChild) c.removeChild(c.lastChild);
// ou (pour un seul nœud.
document.getElementById("conteneur").innerHTML = "";

// Supprimer tous les élément d’un conteneur. 
// Avec jQuery
$("#conteneur").remove();

// En JavaScript:
var c = document.getElementById("conteneur");
c.parentNode.removeChild(c);

// Ciblage
// jQuery
$("#conteneur").siblings();

// En JavaScript
var c = document.getElementById("conteneur");
Array.prototype.filter.call(c.parentNode.children, function(child){
  return child !== c;
});

// Ajouter du texte
// Avec jQuery
$("#conteneur").text("Chaine de caractère");

// En JavaScript
var c = document.getElementById("conteneur");
c.textContent = "Chaine de caractère";


// Ajouter du HTML
// Avec jQuery
$("#conteneur").html("Chaine de caractère");

// En JavaScript
c.innerHTML = "Chaine de caractère";


// Remplace du HTML
// Avec jQuery
$("#conteneur").replaceWith("Chaine de caractère");

// En JavaScript
var c = document.getElementById("conteneur");
c.outerHTML = "Chaine de caractère";

// Nœud précédent
// Avec jQuery
$("#conteneur").prev();

// En JavaScript
var c = document.getElementById("conteneur");
c.previousElementSibling;

// Nœud suivant
// Avec jQuery
$("#conteneur").next();

// En JavaScript
var c = document.getElementById("conteneur");
c.nextElementSibling;


// Nœud parent
// Avec jQuery
$("#conteneur").parent();

// En JavaScript
var c = document.getElementById("conteneur");
c.parentNode;

// Position x/y
// Avec jQuery
$("#conteneur").position();

// En JavaScript
var c = document.getElementById("conteneur");
{left: c.offsetLeft, top: c.offsetTop}


// Vider
// Avec jQuery
$("#conteneur").empty();

// En JavaScript
var c = document.getElementById("conteneur");
c.innerHTML = "";

Manipulation de classes avec le nouvel objet HTML5 « classList »

Compatible Firefox 3.6, Opera 11.50, Chrome 8, IE10 et Safari 5.1. Sinon, il existe deux scripts pour étendre la compatibilité à IE8 et Android 2.1

Petite fonction pour vérifier le support « classList » par le navigateur :

if("classList" in document.createElement("a")) {
    // compatible classList
} 

Ajouter une classe

// jQuery
$('div').addClass('maClasse');

// JavaScript 5
var div = document.querySelector('div');
div.classList.add('maClasse');

Supprimer une classe

// jQuery
$("div").removeClass("maClasse");

// JavaScript 5
var div = document.querySelector("div");
div.classList.remove("maClasse");

Contient une classe ou non

// jQuery
$("div").contains("maClasse");

// JavaScript 5
var div = document.querySelector("div");
div.classList.contains("maClasse");

// Exemple
if(div.classList.contains("maClasse") {
  div.classList.remove("maClasse");
} else {
  div.classList.add("maClasse");
}

Interchanger une classes (toggle)

// jQuery
$('div').toggleClass('maClasse');

// JavaScript 5
// Ajoute ou supprime la classe si elle est présente ou non.
var div = document.querySelector('div');
div.classList.toggle('maClasse', false);
// Le deuxième paramètre, pas toujours supporté
// Force ou non l’ajout de la classe, si elle existe déjà.

Interchanger deux classes (switch)

// jQuery
$('div').switchClass( "maClasse1", "maClasse2" );

// JavaScript
// Il n'existe malheureusement pas d'équivalent de la méthode .switchClass en Javascript.
// Premier exemple
var div = document.querySelector("div");
div.classList.remove("maClasse1");
div.classList.add("maClasse2");

Vous n’avez qu’une seule classe à interchangeable? rien ne vaut la bonne vielle méthode .className, un doux plaisir Script. Pourquoi s’en passer!

// JavaScript
var div = document.querySelector("div");
div.className = (div.className=="maClasse1") ? "maClasse1" : "maClasse1";

Nombre de classe

// jQuery
$('div').length();

// JavaScript 5
var div = document.querySelector('div');
div.classList.length;

// Pour récupérer directement une classe
// du classList il y a la méthone « item ».
var div = document.querySelector('div');
div.classList.item(x);

Modifier une propriété d’une classe.

// jQuery
$('.maClasse').css('color', 'red');

// Javascript
[].forEach.call( document.querySelectorAll('.maClasse'), function(el) {
  el.style.color = 'red';
});

On utilise [].forEach.call() pour vraiment simuler la méthode jQuery qui boucle tous les éléments de la classe « maclasse ».

// La même chose plus compatible (sans forEach)
var oMaClasse = document.getElementsByClassName('maClasse'),
   i = oMaClasse.length; 
while ( i-- > 0 && (oMaClasse[i].style.color = 'red') );
// Ou
for(var i=0, len = oMaClasse.length; i<len; i++) {
  oMaClasse[i].style.color = 'red';
}

Avant jQuery on procédait en modifiant le nom de la classe en manipulant la chaîne de caractère. Ce qui devenait vite complexe lorsqu’un élément avait plus d’une classe. Voir les fonctions « addClass, removeClass, toggleClass » de Jeffrey Way pour normaliser la manipulation de classe avec les vieux navigateurs et ce avec des expressions régulière. Notez qu’il est préférable d’utiliser une bonne vielle boucle JavaScript, plus performant que les expressions régulière...

Justement, jQuery ne fait aucune différence entre une seule instance ou plusieurs. Alors que les méthodes natives en Javascript n’affectent que la première instance. Par exemple pour modifier l’ensemble des éléments d’une même classe, c’est la même instruction en jQuery :

// jQuery, affecte toutes les instances.
var oMonObjet = $('.maClasse');
oMonObjet.addClass('autreClasse');

// JavaScript, affecte seulement la première instance.
var oMonObjet = document.querySelector('.maClasse');
oMonObjet.classList.add('autreClasse');

// JavaScript, affecte toutes les instances du « NodeList ».
var oMonObjet = document.querySelectorAll('.maClasse');
for (var i = 0; i < oMonObjet.length; i++) {
  oMonObjet[i].classList.add('autreClasse');
}
Manipulation des attributs

Fouillez moi, pourquoi on passe ici directement pas la méthode sans passer au préalable par un classList ou plutôt un AttrList? Mais aussi simple que le jQuery et d’ailleurs plus sémantique. À faire rougir jQuery :

Modifier un attribut
// jQuery
$('.maClasse').attr('disabled', true);

// JavaScript
document.querySelector('.maClasse').setAttribute('disabled', true);
Supprime un attribut
// jQuery
$('.maClasse').removeAttr('disabled');

// JavaScript
document.querySelector('.maClasse').removeAttribute('disabled');
Récupère un attribut
// jQuery
$('.maClasse').attr('title')

// JavaScript
document.querySelector('.maClasse').getAttribute('title')

// Ou
// jQuery
$(’[monAttribue="valeur"]’);

// JavaScript
document.querySelectorAll(’[monAttribue="valeur"]’);
Attributs personnalisés « Data-* »

Une nouveauté HTML5 très pratique. Vous avez peut-être été frustré à répétition par le validateur de la W3C en utilisant des attributs en XHTML strict à des fins de manipulation interne en JavaScript. C’est terminé avec le nouvel attribut personnalisable Date-*. Exemple :

<div class="edimestre" data-nom="Oznog" data-fonction="Analyste-programmeur">
  Contenu visible
</div>

<script>
// jQuery
alert($('.edimestre').data('nom'));

// JavaScript
alert(document.querySelector('.edimestre').getAttribute('data-nom'));
</script>
Par Pseudo-classe

Sélectionner chaque champ de saisie d’un formulaire qui est invalide.

// jQuery
$('#monFormulaire :invalid');

// JavaScript (IE 10+)
document.querySelectorAll('#monFormulaire :invalid');
Enfants

Sélectionner tous les enfants d’un élément.

// jQuery
$('#monParent').children();

// JavaScript (avec « comment » et nœud texte)
document.getElementById('myParent').childNodes;

// ou
// JavaScript (IE 9+) (sans « comment » & nœud texte)
document.getElementById('monParent').children;

Sélectionner un enfant à partir de l’attribut « ng-click ».

// jQuery
$('#monParent').children('[ng-click]');

// ou

$('#monParent > [ng-click]');

// JavaScript
document.querySelector('#monParent > [ng-click]');
Descendants

Trouver toutes les ancres sous #monParent.

// jQuery
$('#monParent A');

// JavaScript
document.querySelectorAll('#monParent A');
Éléments exclus

Sélectionner tous les éléments <div> sauf ceux avec la classe CSS « maClasse ».

// jQuery
$('DIV').not('.maClasse');

// ou 

$('DIV:not(.maClasse)');

//JavaScript (IE 9+)
document.querySelectorAll('DIV:not(.maClasse)');

Ray Nicholus propose ce petit protptype pour trouver les sélecteurs.
window.$ = function(selector) {
    var selectorType = 'querySelectorAll';

    if (selector.indexOf('#') === 0) {
        selectorType = 'getElementById';
        selector = selector.substr(1, selector.length);
    }

    return document[selectorType](selector);
};

Le nouveau module « dataset API » du HTML5 est assez bien supporté. Combiné aux nouveaux sélecteurs que dire maintenant de la manipulation JSON!

Manipulation JSON (JavaScript Object Notation)

Wow, je suis épaté ici, faut dire que le gros de la job est fait par l’objet JSON et sa méthode parse. Je ne suis pas un fan du JSON que j’attribue au manque de support XML historique du PHP (avant PHP5). C’est en fait une technique du siècle dernier, dérivé du format CSV. Le seul et unique avantage du JSON c’est la simplicité de son poids plume, à part les accolades et les double grouillement c’est des données, que des données. Sinon personnellement ma drogue à moi c’est le XML mur à mur. Bon, le PHP mais surtout à cause du AJAX (tout comme l’ActionScript) qui l’utilise abondamment, c’est quand même du « JavaScript Object Notation ». Mais cette fois à cause de jQuery et probablement de l’équipe Google qui le préfère visiblement au XML largement négligé justement à cause du JSON. Je serais curieux de savoir ce que Tim Berners-Lee en pense, ou encore ce que l’équipe Netscape en dirait. Dire que le XML (c’est à dire extensible!) est bien supporté par les navigateurs modernes.

Enfin, ce n’est pas toujours facile d’extraire des données du JSON. Voilà donc une méthode fort utile pour manipuler du JSON. Pour reprendre l’exemple de l’attribut personnalisé « Data-* » :

//Exemple de chaine JSON
{ 'nom' : 'Oznog', 'fonction' : 'Analyste-programmeur' }

<div class="oJSON" data-edimestre="{ 'nom' : 'Oznog', 'fonction' : 'Analyste-programmeur' }">
  Contenu visible
</div>
<script>
// jQuery
var monElement = $('.oJSON').data('edimestre');
var monJSON = $.parseJSON(monElement); // Ici le miracle!
alert(monJSON.nom); // Retourne : Oznog
alert(monJSON.fonction); // Retourne : Analyste-programmeur

// JavaScript
var monElement = document.querySelector('.oJSON').getAttribute('data-edimestre');
var monJSON = JSON.parse(monElement); // Ici le miracle sans jQuery!
alert(monJSON.nom); // Retourne : Oznog
alert(monJSON.fonction); // Retourne : Analyste-programmeur
</script>

Que dire de plus, fantastique!

Manipulation des événements

Une fonctionnalité jQuery compacte, très pratique, notamment au niveau de la compatibilité. Mais en JavaScript il suffit aussi d’utiliser la méthode addEventListener. Voilà un exemple en bouclant tous les hyperliens du document.

// Le très simple jQuery
$('a').on('click', maFonction);
// Ou
$('a').on('click', function () {...});

// Le simple JavaScript
document.querySelector('a').onclick = function () {...};

// jQuery
$('#monFormulaire').on('submit', function () {...});

// JavaScript
document.querySelector('#monFormulaire').onsubmit = function () {...};

// jQuery
$('#monChamp').on('change', function () {...});

// JavaScript
document.querySelector('#monChamp').onchange = function () {...};

Malheureusement le JavaScript n’affecte que la première instance. Contrairement au jQuery qui affecte toutes les occurrences du document. Pour ce faire il faut donc utiliser le sélecteur querySelectorAll et boucler le résultat avec forEach.

// Javascript
[].forEach.call( document.querySelectorAll('a'), function(el) {
   el.addEventListener('click', function() {
     // Clic sur un hyperlien
     // NOTE : Référence de l’objet avec « this »
  }, false);
});

// Exemple simplifié avec la fonction « addEvent » de
// Jeffrey Way
var oLiens = document.getElementsbyTagName('a');
addEvent(oLiens, 'click', maFonction);

// Plus tordu, ajouter un événement aux items d’une liste à puces mais uniquement sur les hyperlien.
// jQuery
$('ul').on('click', 'a', maFonction);

// JavaScript
// Cette fois avec la nouvelle méthode matchesSelector
// pour déterminer la cible (target)
var oListeApuces = document.getElementsByTagName('ul');
oListeApuces.addEventListener('click', function(e) {
   if ( e.target.matchesSelector('ul a') ) {
      // Clic sur un hyperlien de la liste à puce.
   }
}, false);

// Exemple simplifié avec la fonction « matches » de 
// Jeffrey Way
var oListeApuces = document.getElementsByTagName('ul');
document.addEventListener('click', function(e) {
   if ( matches.call( e.target, 'ul a') ) {
      // Clic sur un hyperlien de la liste à puce.
   } 
}, false);

// Exemple sans « match » avec simplifié avec la fonction
// « addEvent » de Jeffrey Way
var oListeApuces = document.getElementsByTagName('ul');
addEvent(oListeApuces, 'click', function() {
   var target = e.target || e.srcElement;
   if ( target && target.nodeName === 'A' ) {
      // Clic sur un hyperlien de la liste à puce.
   }
});

Ce dernier exemple démontre bien la différence entre les deux méthodes. jQuery ajoute l’événement sur la liste à puces et vérifie ensuite que le clic est fait sur un hyperlien. Alors que la méthode JavaScript doit au préalable sélectionner les listes à puces pour ensuite vérifier si le clic est fait sur une hyperlien de l’objet.

Le standard addEventListener/removeEventListener et l’exception attachEvent/detachEvent

Incompatible Internet Explorer, addEventListener est néanmoins le nouveau standard. Voilà les deux méthodes en exemple suivit de la parfaite fonction compatible de John Resig :

// Ajouter un événement W3C
document.addEventListener('click', maFunction() {
    // ...
}, false);

// Ajouter un événement IE
document.attachEvent('click', maFunction() {
    // ...
}, false);

// Supprimer un événement W3C
document.removeEventListener('click', maFunction() {
    // ...
}, false);

// Supprimer un événement IE
document.detachEvent('click', maFunction() {
    // ...
}, false);
Encore la faute à Microsoft

Enfin Microsoft a tardé à être compatible et encore. Microsoft avait créé sa propre méthode alors que addEventListener existait déjà là. Plus simple que l’exemple de Jeffrey Way, il y a les fonctions de John Resig. Il suffit d’utiliser le attachEvent pour Internet Explorer et addEventListener pour les autres navigateur. Point intéressant, on a deux fonctions, une pour ajouter un événement et une autre pour supprimer un événement.

// Attacher et supprimer un événement par John Resig
function addEvent( obj, type, fn ) {
  if ( obj.attachEvent ) {
    obj['e'+type+fn] = fn;
    obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
    obj.attachEvent( 'on'+type, obj[type+fn] );
  } else
    obj.addEventListener( type, fn, false );
}
function removeEvent( obj, type, fn ) {
  if ( obj.detachEvent ) {
    obj.detachEvent( 'on'+type, obj[type+fn] );
    obj[type+fn] = null;
  } else
    obj.removeEventListener( type, fn, false );
}

// Usage
addEvent(objet, 'click', mafonction );
removeEvent( objet, , mafonction );

// Exemple d’utilisation
addEvent( document.getElementsByClassName('maClasse'), 'click', function(){ alert(this.innerHTML); });

Source : Flexible Javascript Events.

ready et DOMContentLoaded

Une autre bonne pratique instaurée par jQuery est de s’assurer que l’ensemble de l’arbre du DOM est chargé avant de le manipuler. Vous avez sans doute déjà exécute une fonction JavaScrip qui manipule le DOM avant que la page soit chargée. Soit parce que l’instruction est dans l’entête (Head) c’est-à-dire exécuté avant même que la construction du DOM soit commencée. Ou encore directement dans le document, toujours avant que le DOM soit tout à fait chargée. Il suffit de placer votre code en bas de page. Ou encore utiliser l’événement on load dans la balise body. Noter que la version jQuery est beaucoup plus précise puisqu’elle attend non seulement que la page, mais que toutes les images et les documents externes soient chargés... Pour reproduire exactement ce résultat en JavaScript il faut utiliser une autre librairie(le domready de Google) alors aussi bien utiliser jquery si c’est obligatoire. Sinon DOMContentLoaded fait très bien l’affaire.

// Avant le HTML5
<body onload="maFonction">

// jQuery, exécute la fonction aussitôt le DOM chargé
$(document).ready(maFonction)

// JavaScript
document.addEventListener('DOMContentLoaded', function() {
   // Le DOM est chargé
});

// Internet Explorer
document.readyState != 'loading';

// Fonction combinée.
function documentPret(maFonction) {
  if (document.readyState != 'loading'){
    maFonction();
  } else {
    document.addEventListener('DOMContentLoaded', maFonction);
  }
}
Naviguer parmi les nœuds
// jQuery cible le nœud suivant
$('#list').next();

// JavaScript
var oSuivant = document.querySelector('#oListe').nextElementSibling; // À partir IE9

// Encore mieux
var oListe = document.getElementById('oListe'),
  oSuivant = oListe.nextSibling;

// Seulement un vrai nœud, et non pas un nœud texte...
// nodeType > 1 égal sans doute un nœud de type texte
while ( next.nodeType > 1 ) next = next.nextSibling;
Boucler une matrice (array) avec forEach

Le JavaScript n’avait pas de méthode native pour boucler une matrice, alors que le jQuery le permet :

// jQuery
var maMatrice = ['item1', 'item2', 'item3', 'item4']
$.each( maMatrice, function ( index, value ) {
  alert(value);
});

// JavaScript
var maMatrice = ['item1', 'item2', 'item3', 'item4']
for ( var i = 0; i < maMatrice.length; i++ ) {
  alert(maMatrice[i]);
}

// Nouvelle méthode forEach HTML5
['item1', 'item2', 'item3', 'item4'].forEach(function(){
  // ...
});
// Ou
var maMatrice = ['item1', 'item2', 'item3', 'item4'];
maMatrice.forEach(function(){
  // ...
});
Fonctions de Jeffrey Way

Ces premières fonctions permettent une compatibilité beaucoup plus large de la manipulation de classes. Il s’agit essentiellement de modifier, ajouter ou supprimer une classe en tenant compte si l’élément a plus d’une classes.

// Fonctions « addClass, removeClass, toggleClass » de Jeffrey Way pour normaliser la manipulation de classe.
var box = document.getElementById('box'),

    hasClass = function (el, cl) {
        var regex = new RegExp('(?:\\s|^)' + cl + '(?:\\s|$)');
        return !!el.className.match(regex);
    },

    addClass = function (el, cl) {
        el.className += ' ' + cl;
    },

    removeClass = function (el, cl) {
        var regex = new RegExp('(?:\\s|^)' + cl + '(?:\\s|$)');
        el.className = el.className.replace(regex, ' ');
    },

    toggleClass = function (el, cl) {
        hasClass(el, cl) ? removeClass(el, cl) : addClass(el, cl);

    };

// Usage
addClass(box, 'drago'); 
removeClass(box, 'drago');
toggleClass(box, 'drago');

Pour normaliser le modèle addEventListener de la W3C avec le attachEvent d’Internet Explorer tout en simplifiant le code à la manière jQuery.

// Fonction « addEvent » de Jeffrey Way pour simuler le jQuery
var addEvent = (function () {
  var filter = function(el, type, fn) {
    for ( var i = 0, len = el.length; i < len; i++ ) {
      addEvent(el[i], type, fn);
    }
  };
  if ( document.addEventListener ) {
    return function (el, type, fn) {
      if ( el && el.nodeName || el === window ) {
        el.addEventListener(type, fn, false);
      } else if (el && el.length) {
        filter(el, type, fn);
      }
    };
  }
  return function (el, type, fn) {
    if ( el && el.nodeName || el === window ) {
      el.attachEvent('on' + type, function () { return fn.call(el, window.event); });
    } else if ( el && el.length ) {
      filter(el, type, fn);
    }
  };
})();

// usage
addEvent( document.getElementsByTagName('a'), 'click', fn);

La fonction « matches » permet de normaliser cette fois le préfixe pour chaque navigateur (!) Bien que le matchesSelector soit de plus en plus supporté, plusieurs navigateurs utilisent malheureusement son propre préfixe.

// Fonction « matches » de Jeffrey Way pour normaliser les préfixes
var matches;

(function(doc) {
   matches = 
      doc.matchesSelector ||
      doc.webkitMatchesSelector ||
      doc.mozMatchesSelector ||
      doc.oMatchesSelector ||
      doc.msMatchesSelector;
})(document.documentElement);

// Usage
document.addEventListener('click', function(e) {
   if ( matches.call( e.target, 'ul a') ) {
      // Clic
   } 
}, false);

Ce dernier exemple permet de simuler le sélecteur jQuery « $ ». Ça fonctionne mais ce n’est certes pas conseillé puisqu’elle ne supporte pas les sélecteurs CSS complexes, des vieux navigateurs notamment. Il faut dire que la librairie jQuery est beaucoup plus optimisée à ce niveau et prend en charge la compatibilité au sens large. Mais c’est un bon exemple du fonctionnement du moteur de jQuery.

// Fonction pour simuler le sélecteur jQuery « $ ».
if ( !document.getElementsByClassName ) {
  document.getElementsByClassName = function(cl, tag) {
    var els, matches = [],
      i = 0, len,
      regex = new RegExp('(?:\\s|^)' + cl + '(?:\\s|$)');
	 
      // Si aucune balise n’est sécifiée
      // capturer l’ensemble du DOM 
      els = document.getElementsByTagName(tag || "*");
      if ( !els[0] ) return false;
      for ( len = els.length; i < len; i++ ) {
        if ( els[i].className.match(regex) ) {
          matches.push( els[i]);
        }
      }
      return matches; // retourne une matrice si un élément a la bonne classe
  };
}
 
// Valide seulement le id, la classe, ou la balise.
var $ = function(el, tag) {
  var firstChar = el.charAt(0);
  if ( document.querySelectorAll ) return document.querySelectorAll(el);
  switch ( firstChar ) {
    case "#": return document.getElementById( el.slice(1) );
    case ".": return document.getElementsByClassName( el.slice(1), tag );
    default : return document.getElementsByTagName(el);
  }
};

// Usage
$('#conteneur');
$('.maClasse'); // Retourne un élément de classe « maClasse »
$('.maClasse', 'div'); // Retourne un élément DIV de classe « maClasse »
$('p'); // Retourne tous les éléments P

Source : From jQuery to JavaScript: A Reference

Chainage d’instructions

Le chainage jQuery est aussi populaire pour combiner plusieurs instructions en une seule. Et pourtant c’est une des forces du Javascript, notamment avec les opérateur, les expressions régulière... Mais le Javascript ne permet pas de chainage d’instruction à la manière jQuery, ni le « with » du VBScript. Par exemple, la combinaison suivante tout à fait valide en jQuery :

// Chainage jQuery
$('#momDiv').height(100).fadeIn(200);

Ce n’est pas vraiment une nouvelle fonctionnalité qu’une habitude à prendre. En programmation orientée objet Javascript il faut simplement assigner les valeurs mais pour pouvoir les manipuler de manière chainé, il faut aussi retourner les valeurs, question de ne pas devoir assigner à une variable à chaque fois. Et ainsi pouvoir chainer les manipulations dans une seule instruction. Pour la forme, un exemple. Personnellement j’éviterais cette façon de faire.

// Exemple Javascript de chainage

var monObjet = function() {
  this.id = 'monID';
  this.nom = 'nomNom';
};

monObjet.prototype.setId = function(id) {
  this.id = id;
  return this; // Important d’avoir un retour.
};

monObjet.prototype.setNom = function(nom) {
  this.nom = nom;
  return this; // Important d’avoir un retour.
};

monObjet.prototype.maMethode = function() {
  console.log(
    'Exemple : ' + this.id + ', ' +this.nom;
  );
  return this; // Important d’avoir un retour.
};

// Sans chainage
var unObjet = new monObjet();
unObjet.setId('mon ID');
unObjet.setNom('mon nom');
unObjet.maMethode();

// Sortie
// « Exemple : mon ID, mon nom »

// Avec chainage
new monObjet()
  .setId('mon ID')
  .setNom('mon nom')
  .maMethode();

// Sortie
// « Exemple : mon ID, mon nom »
<ul>
  <li id="foo">foo</li>
  <li id="bar">bar</li>
  <li id="baz">baz</li>
</ul>

jQuery
alert('Index: ' + $('#bar').index();

Javascript
function indexInParent(node) {
    var children = node.parentNode.childNodes;
    var num = 0;
    for (var i=0; i<children.length; i++) {
         if (children[i]==node) return num;
         if (children[i].nodeType==1) num++;
    }
    return -1;
}
Conclusion

Quelle lourdeur, que j’espère même bonifier au fil des nouveautés. À consulter au scalpel. Mais c’est justement de poids dont il est question. Et même de surpoids quand on réalise le nombre d’opérations dupliquées par les moteurs jQuery. Cette page de références permet déjà de convertir rapidement un bout de code, un exemple trouvé à la volée, afin d’éviter de charger la librairie jQuery au complet pour une simple instruction JavaScript. Et un peu pour dire aux nouveaux webmestres que derrière le jQuery, il y a toujours le Javascript! On est toutefois très loin de voir disparaitre le jQuery car il s’adresse aussi aux « gabarits industriels » (Framework), à l’open source jusqu’aux derniers designs gratuits qui pullulent sur le Web! Car faut bien le dire, c’est surtout ça le jQuery, une communauté, une technologie ouverte, libre et accessible (comme un navigateur en passant! Merci à Mozilla).

D’ailleurs on peut lire sur la page d’accueil de jQuery « CSS3 Compliant » ou Conforme CSS3, prise en charge des sélecteurs CSS3! Au-delà de la syntaxe, le jQuery c’est surtout ces « widjets » et ses effets de transition, largement dépassé par Scriptaculous’ Effects.js en passant! C’est davantage au niveau du CSS3, tout particulièrement des transitions et des animations et « keyframe », des transparences, ou encore du SVG que l’on doit orienter tout nouveau développement.

Références
, Analyste programmeurConception oznogco multimédia (http://oznogco.com), Trucsweb
Dernière mise à jour :

Commentaires

  • Intéressant,à part que votre étude réduit jQuery à un outil pour parcourir et manipuler le Dom. De plus cette bibliothèque est antérieure à HTML5 et le selectors API. L'objectif était justement de pallier à ces limites que l'on peut toujours voit dans les problèmes de compatibilité avec IE. On peut d'ailleurs se demander si jQuery n'a pas influencé l'API L'article prouve certainement que l'intérêt d'utiliser jQuery est moindre aujourd'hui mais les statistiques d'utilisation et le fait que d'autres frameworks JavaScript passent par jQuery montrent que ses jours ne sont toujours pas comptés
    64x64
    Bruno
    Date (GMT) : 2017-04-21 21:25:1 (UTC +0000)
    • Salut, Effectivement, c'est pourquoi j'écris « c’était avant le duo CSS3 / HTML5! » et les problèmes de compatibilité étaient là bien avant jQuery. On devait passer par toute sorte d'astuce et parfois vallait mieux rester simple. Avant les vieux IE, comme IE4 et ses gadgets, il y avait les vieux Netscapes qui survivaient! C'est une base, et un fourretout. Je suis désolé de ne pas avoir fait le tour complet du jardin. J'aimerais bien, j'y vais aux pratiques. Avec le HTML5, et ...sky is the limit, et ce n'est pas tout le monde qui utilise le potentiel de jQuery. Alors que c'est carrément le JavaScript qu'on peut éviter avec le CSS3, notamment avec les animations, les transitions, les transparences, le SVG etc. Une histoire qui ne se termine jamais... Ceci dit, j'utilise et je programme aussi en jQuery bien entendu. Mais quand une alternative est possible, c'est toujours pratique d'avoir des composantes qui ne sont pas dépendantes de la fameuse librairie. Le Web à ceci de pratiquer qu'il peut être morcelé, parfois en de très petit objet. Un petit Ajax par-ci par-là. On n'a pas à travailler avec des hyper structures à chaque fois. Mais une fois sur deux (si pas 80%) des codes donnée en exemple, comme parcourir le DOM justement, sont en jQuery. Je pense qu'on a le jQuery facile... Merci.
      64x64
      oznog
      Date (GMT) : 2017-04-21 23:22:37 (UTC +0000)


    Ajouter un commentaire
    Votre adresse de courriel ne sera pas publiée. * L'astérisque indique les champs obligatoires.
    Votre évaluation du tutoriel

    8/10 sur 1 revues.
           Visites : 33253 - Pages vues : 34074
    X

    Trucsweb.com Connexion

    Connexion

    X

    Trucsweb.com Mot de passe perdu

    Connexion

    X

    Trucsweb.com Conditions générales

    Conditions

    Responsabilité

    La responsabilité des Trucsweb.com ne pourra être engagée en cas de faits indépendants de sa volonté. Les informations mises à disposition sur ce site le sont uniquement à titre purement informatif et ne sauraient constituer en aucun cas un conseil ou une recommandation de quelque nature que ce soit.

    Aucun contrôle n'est exercé sur les références et ressources externes, l'utilisateur reconnaît que les Trucsweb.com n'assume aucune responsabilité relative à la mise à disposition de ces ressources, et ne peut être tenue responsable quant à leur contenu.

    Droit applicable et juridiction compétente

    Les règles en matière de droit, applicables aux contenus et aux transmissions de données sur et autour du site, sont déterminées par la loi canadienne. En cas de litige, n'ayant pu faire l'objet d'un accord à l'amiable, seuls les tribunaux canadien sont compétents.

    X

    Trucsweb.com Trucsweb

    X

    Trucsweb.com Glossaire

    X

    Trucsweb.com Trucsweb

    X

    Trucsweb.com Trucsweb

    Conditions

    Aucun message!

    Merci.

    X
    Aucun message!
    X

    Trucsweb.com Créer un compte

    Créer un compte

    .
    @