mardi 26 août 2014

Q.lib.js : Création d'une bibliothèque jQuery like, plus efficiente - Part.03 - Attributs, Properties et CSS.

Le sélecteur $_.na


Q.lib.js possède un sélecteur de nodes possédant la propriété name.
  • DOM
  • document.getElementsByName("nameValue");
  • jQuery
  • $("[name='test1']");
  • Q.lib.js
  • $_.na("test1");

Q.lib.js est entre 200 et 300 fois plus rapide que jQuery sur ce selecteur

Code

$_.na = function(n) {
 return document.getElementsByName(n);
};

Méthode .attr()


  • .attr(attributeName)
  • Lit la valeur du premier élément dans l'ensemble des éléments sélectionnés.
    • jQuery
    • var title = $( "em" ).attr( "title" );
    • Q.lib.js
    • var title = $_.tn( "em" ).attr( "title" );
  • .attr(attributeName, value)
  • Définit la valeur d'un attribut de l'ensemble des éléments sélectionnés.
    • jQuery
    • $( "#photo1" ).attr("title", "My first picture" );
    • Q.lib.js
    • $_.id( "photo1" ).attr( "title", "My first picture" );
  • .attr(attributes)
  • définit la valeur de plusieurs attributs de l'ensemble des éléments sélectionnés (passage d'un objet clé/valeur).
    • jQuery
    • $( "img" ).attr({title:"A picture of me", alt:"Album N°3"});
    • Q.lib.js
    • $_.tn( "img" ).attr({title:"A picture of me", alt:"Album N°3"});

Code

// ====================================================
// .attr()
// ----------------------------------------------------
// Get the value of an attribute for the first element
// in the set of matched elements or set one or more 
// attributes for every matched element.
// ----------------------------------------------------
Node.prototype.attr = function(a, v) {
 if (arguments.length == 2) {
  this.setAttribute(a,v);
  return this;
 }
 
 if (typeof a === 'string') {
  return this.getAttribute(a);
 }
 
 if (typeof a === 'object') {
  for (var k in a) {
   this.setAttribute(k,a[k]);
  }
  return this;
 }
};

NodeList.prototype.attr = function() {
 var args = [].slice.apply(arguments);

 for (var i=0, L=this.length; i < L; i++) {
  this[i].attr.apply(this[i], args);
 }
 
 return this;
};

HTMLCollection.prototype.attr = NodeList.prototype.attr;

Méthode .prop()


  • .prop(propertyName)
  • Lit la valeur de la propriété du premier élément dans l'ensemble des éléments sélectionnés.
    • jQuery
    • var title = $( "#cb1" ).prop( "checked" );
    • Q.lib.js
    • var title = $_.id( "cb1" ).prop( "checked" );
  • .prop(propertyName, value)
  • Définit la valeur d'une propriété de l'ensemble des éléments sélectionnés.
    • jQuery
    • $( "[name='cb']" ).prop( "checked", true );
    • Q.lib.js
    • $_.na( "cb" ).prop( "checked", true );
  • .prop(properties)
  • définit la valeur de plusieurs propriétés de l'ensemble des éléments sélectionnés (passage d'un objet clé/valeur).
    • jQuery
    • $( "[name='cb']" ).prop({checked:true, disabled:true});
    • Q.lib.js
    • $_.na( "cb" ).prop({checked:true, disabled:true});

Code

// ====================================================
// .prop()
// ----------------------------------------------------
// Get the value of a property for the first element in
// the set of matched elements or set one or more
// properties for every matched element.
// ----------------------------------------------------
Node.prototype.prop = function(p, v) {
 if (arguments.length == 2) {
  this[p] = v;
  return this;
 }

 if (typeof p === 'string') {
  return this[p];
 }
 
 if (typeof p === 'object') {
  for (var k in p) {
   this[k] = p[k];
  }
  return this;
 }
};

NodeList.prototype.prop = function() {
 var args = [].slice.apply(arguments);

 for (var i=0, L=this.length; i < L; i++) {
  this[i].prop.apply(this[i], args);
 }
 return this;
};

HTMLCollection.prototype.prop = NodeList.prototype.prop;

Méthode .css()


  • .css(propertyName)
  • Lit la valeur réelle de la propriété CSS (getComputedStyle) du premier élément dans l'ensemble des éléments sélectionnés.
    • jQuery
    • var color = $( "#mydiv" ).css( "background-color" );
    • Q.lib.js
    • var color = $_.id( "mydiv" ).css( "background-color" );
  • .css(propertyName, value)
  • Définit la valeur de la propriété CSS de l'ensemble des éléments sélectionnés.
    • jQuery
    • $( "p" ).css( "color", "red" );
    • Q.lib.js
    • $_.tn( "p" ).css( "color", "red");
  • .css(properties)
  • définit la valeur de plusieurs propriétés CSS de l'ensemble des éléments sélectionnés (passage d'un objet clé/valeur).
    • jQuery
    • $( "div" ).css({"color":red, "background-color":"yellow"});
    • Q.lib.js
    • $_.tn( "div" ).css({"color":red, "background-color":"yellow"});

Code

// ====================================================
// .css()
// ----------------------------------------------------
// Get the value of a style property for the first 
// element in the set of matched elements or set one or
// more CSS properties for every matched element.
// ----------------------------------------------------
Node.prototype.css = function(c, v) {
   if (arguments.length == 2) {
      this.style[$_.cC(c)] = v;
      return this;
   }

   if (typeof c === 'string') {
      return window.getComputedStyle(this).getPropertyValue(c);
   }
 
   if (typeof c === 'object') {
      for (var k in c) {
         this.style[$_.cC(k)] = c[k];
      }
      return this;
   }
};

NodeList.prototype.css = function() {
   var args = [].slice.apply(arguments);

   for (var i=0, L=this.length; i < L; i++) {
      this[i].css.apply(this[i], args);
   }
   return this;
};

HTMLCollection.prototype.css = NodeList.prototype.css;

Résumé


Q.lib.js sait travailler avec toutes les propriétés associées à un élément : attributs, classes CSS, propriétés booléenne (checked, disabled, ...) et enfin les propriétés CSS classiques.
Nous avons introduit un nouveau sélecteur ($_.na) très rapide permettant de sélectionner en général des éléments de formulaires par leur nom.

Nous verrons dans le prochain article comment la librairie prend en charge les évènements.

samedi 16 août 2014

Q.lib.js : Création d'une bibliothèque jQuery like, plus efficiente - Part.02 - Opérations sur les classes CSS

Méthodes

  • .addClass()
  • Cette méthode permet d'ajouter une ou plusieurs classes à un élément ou une liste d'éléments sélectionnés.
    • jQuery
    • $("#myid").addClass("top active");
    • Q.lib.js
    • $_.id("myid").addClass("top", "active");
  • .hasClass()
  • Cette méthode renvoie true si l'élément possède la classe recherchée, sinon renvoie false. Si le sélecteur est une liste d'éléments, elle renvoie true si au moins un élément possède la classe recherchée.
    • jQuery
    • $("#myid").hasClass("top");
    • Q.lib.js
    • $_.id("myid").hasClass("top");
  • .removeClass()
  • Cette méthode permet de supprimer une ou plusieurs classes à un élément ou une liste d'éléments sélectionnés.
    • jQuery
    • $("p").removeClass("top active");
    • Q.lib.js
    • $_.tn("p").removeClass("top", "active");
  • .replaceClass()
  • Cette méthode permet de remplacer une classe par une autre d'un élément ou d'une liste d'éléments sélectionnés.
    • jQuery
    • $("#myid").removeClass("old").addClass("new");
    • Q.lib.js
    • $_.id("myid").replaceClass("old", "new");
      ou
      $_.id("myid").removeClass("old").addClass("new");
  • .toggleClass()
  • Ajoute ou supprime une ou plusieurs classes d'un élément ou d'une liste d'éléments sélectionnés selon la présence ou non de la ou des classes dans le ou les éléments sélectionnés ou de la valeur du second.
    Les second paramètres optionels [switch] pour jQuery et [force] pour Q.lib.js fonctionnent de manière identique:true force l'ajout, false force la suppression.
    • jQuery
    • $("p").toggleClass("highlight blue");
    • Q.lib.js
    • $_.tn("p").toggleClass("highlight blue");

Code

// ==== NODE
Node.prototype.addClass = function() {
	var args = [].slice.apply(arguments);
	var cl = this.classList;
	cl.add.apply(cl, args);
	return this;
};

Node.prototype.hasClass = function(c) {
	return this.classList.contains(c);
};

Node.prototype.removeClass = function() {
	var args = [].slice.apply(arguments);
	var cl = this.classList;
	cl.remove.apply(cl, args);
	return this;
};

Node.prototype.replaceClass = function(p, n) {
	this.classList.remove(p);
	this.classList.add(n);
	return this;
};

Node.prototype.toggleClass=function(c, force) {
	var cl = c.split(' '),
		 ii = cl.length;

	if (arguments.length == 1) {
		for(var i=0; i<ii; i++) {
			this.classList.toggle(cl[i]);
		}
		return this;
	}

	for(var i=0; i<ii; i++) {
		this.classList.toggle(cl[i], force);
	}
	return this;
};

// ==== NODELIST
NodeList.prototype.addClass = function() {
	var args = [].slice.apply(arguments);
	var L = this.length;
	for (var i=0; i < L; i++) {
		var cl = this[i].classList;
		cl.add.apply(cl, args);
	}
	return this;
};

NodeList.prototype.hasClass = function() {
	var args = [].slice.apply(arguments);
	var L = this.length;
	for (var i=0; i < L; i++) {
		var cl = this[i].classList;
		if (cl.contains.apply(cl, args) === true) {
			return true;
		}
	}
	return false;
};

NodeList.prototype.removeClass = function() {
	var args = [].slice.apply(arguments);
	var L = this.length;
	for (var i=0; i < L; i++) {
		this[i].classList.remove.apply(this[i].classList, args);
	}
	return this;
};

NodeList.prototype.toggleClass = function(c, force) {
	var L = this.length;
	
	if (arguments.length == 1) {
		for (var i=0; i < L; i++) {
			this[i].toggleClass(c);
		}
		return this;
	}

	for (var i=0; i < L; i++) {
		this[i].toggleClass(c, force);
	}
	return this;
};

// ==== HTMLCOLLECTION
HTMLCollection.prototype.addClass = NodeList.prototype.addClass;
HTMLCollection.prototype.hasClass = NodeList.prototype.hasClass;
HTMLCollection.prototype.removeClass = NodeList.prototype.removeClass;
HTMLCollection.prototype.toggleClass = NodeList.prototype.toggleClass;



Résumé

Q.lib.js possède les mêmes méthodes (plus .replaceClass) que jQuery pour gérer les classes CSS, que ce soit au niveau syntaxique, sémantique et fonctionnel, en plus rapide bien sûr.
Si vous désirez et pouvez améliorer le code, faites moi signe \0.

jeudi 14 août 2014

Q.lib.js : Création d'une bibliothèque jQuery like, plus efficiente - Part.01


Pourquoi créer une nouvelle lib?

Comme nous l'avons vu dans les précédents articles, jQuery souffre d'un handicap majeur dans le cadre des applications mobiles/sites mobiles, à savoir une vitesse d’exécution très faible en regard des performances natives de Javascript, associée à sa lourdeur.

Pour autant, jQuery est largement utilisé par les web-designers, il est incontournable dans beaucoup de frameworks. L'idée est donc de concevoir une librairie dont l'utilisation mime au mieux dans sa syntaxe celle de jQuery tout en étant conçue pour être entre 4 et 20 fois plus rapide et beaucoup moins lourde (jQuery 2.1.1: 247Ko et 84Ko minimifiée).

Entrons dans les entrailles de Q.lib.js

A propos des sélecteurs

jQuery utilise uniformément la syntaxe $("selector") pour sélectionner n'importe quel type d'éléments du DOM. Et c'est principalement à cause de cette syntaxe uniforme qu'il se trouve pénalisé en temps d'exécution. Mais par ailleurs cette syntaxe est beaucoup plus compacte par exemple qu'un document.getElementById classique.

L'idée dans Q.lib.js est donc d'encapsuler ces méthodes natives par une fonction.

La syntaxe des sélecteurs de Q.lib.js est de la forme: $_.selectorfunction("string")

Q.lib.js propose 5 fonctions de sélection :
  • $_.id("myid") équivalent à
    • $("#myid")
    • ou
    • document.getElementById("myid")
  • $_.tn("p") équivalent à
    • $("p")
    • ou
    • document.getElementsByTagName("p")
  • $_.cn("myclass") équivalent à
    • $(".myclass")
    • ou
    • document.getElementsByClassName("myclass")
  • $_.qs("p:first-child") équivalent à
    • $("p:first")
    • ou
    • document.querySelector("p:first-child")
  • $_.qa("[data-sample]") équivalent à
    • $("[data-sample]")
    • ou
    • document.querySelectorAll("[data-sample]")


Code

$_ = new Object;

// ====================================================
// SELECTORS WRAPPERS
// ====================================================

$_.id = function(id) {
 return document.getElementById(id);
};

$_.tn = function(tn) {
 return document.getElementsByTagName(tn);
};

$_.cn = function(cn) {
 return document.getElementsByClassName(cn);
};

$_.qs = function(q) {
 return document.querySelector(q);
};

$_.qa = function(q) {
 return document.querySelectorAll(q);
};

Les cas de querySelector et querySelectorAll

querySelector peut ètre utilisé à partir d'un élément (ce qui réduit le champ de recherche). Un prototype .qs a donc été ajouté aux objets de type Node.
Il en va de même pour querySelectorAll. Un prototype .qa a donc été ajouté aux objets de type Node.

Code

Node.prototype.qs = function(q) {
 return this.querySelector(q);
}

Node.prototype.qa = function(q) {
 return document.querySelectorAll(q, this);
}

Résumé

Dans cette première partie nous avons vu la syntaxe des sélecteurs optimisés de Q.lib.js. Les développeurs étant censés savoir quel est la sélection à effectuer, mémoriser ces sélecteurs là ne devraient pas être difficile, pas plus que de les écrire vu leur longueur.

Dans la 2ème partie nous verrons comment Q.lib.js gère les classes CSS.

lundi 11 août 2014

Conversion JQUERY -> DOM natif / ES5

Première étape

Dans cet article nous allons voir comment substituer les principales fonctions d'accès au DOM de jQuery par celles d'ES5 et du Javascript classique.
Bien sûr l'écriture est moins concise que celle utilisée par jQuery, mais les exemples sont là à titre informatif. Nous verrons dans un prochain article comment encapsuler tout cà pour avoir à la fois la concision d'écriture et la performance, avec une syntaxe très proche de celle de jQuery, bref le beurre et l'argent du beurre.


Les sélecteurs jQUERY

sélecteur ID 

Sans doute le plus simple à remplacer.
// jQuery
$('#myID');

// JavaScript
document.getElementById('myID');
Retour : single Node.

sélecteur Classe

Les classes sont intensivement utilisées dans les développements HTML/CSS avec jQuery, mais là encore la vitesse est dramatiquement faible par rapport à une solution native.
// jQuery
$('.myClass');

// JavaScript
document.getElementsByClassName('myClass');
Retour : HTMLCollection.

sélecteur Tags

Idem que pour le sélecteur de classe sauf que là on sélectionne  tous les tags de même nom.
// jQuery
$('div');

// JavaScript
document.getElementsByTagName('div');
Retour : HTMLCollection.


sélecteur CSS selectors

On doit sans aucun doute les méthodes querySelector et querySelectorAll à jQuery vu l'usage des CSS selectors dans les applications et frameworks basés sur cette librairie. 
document.querySelector('') renvoie le premier Node d'une NodeList, quelque soit le nombre d'objets Node trouvés. document.querySelectorAll('') retourne toujours une NodeList.

/*
 * Don't use querySelector or querySelectorAll for
 * selecting classes, tagname or Id but for 
 * complexe selectors!
 */

// Grab the last list Node of .someList unordered list
document.querySelector('ul.someList li:last-child');

// Grab some data-* attribute
document.querySelectorAll('[data-toggle]');
Retour : Node ou NodeList.


Manipuler les classes CSS

Manipuler les classes css d'un ou de plusieurs éléments est chose courante avec jQuery. On peut désormais effectuer ces manipulations nativement à l'aide de l'objet classList, qui se montre bien sûr bien plus rapide que l'implémentation jQuery.

Add class

Ajouter une classe à un ou plusieurs éléments.
// jQuery
$('div').addClass('myClass');

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

Remove class

Enlever une classe à un ou plusieurs éléments.
// jQuery
$('div').removeClass('myClass');

// JavaScript
var div = document.querySelector('div');
div.classList.remove('myClass');

Toggle class

Ajouter/supprimer une classe selon sa présence ou non.
// jQuery
$('div').toggleClass('myClass');

// JavaScript
var div = document.querySelector('div');
div.classList.toggle('myClass');


Has Class

Détermine si un élément possède une classe donnée.
// jQuery
$('#mydiv').hasClass('myClass');

// JavaScript
var div = document.getElementById('#mydiv');
div.classList.contains('myClass');

Manipuler les styles CSS

La méthode css() de jQuery est elle aussi très pratique, mais bien sûr bien plus lente qu'en natif.

// jQuery
$(elem).css({
  "background" : "#F60",
  "color" : "#FFF"
});

// JavaScript
var elem = document.querySelector(elem);
elem.style.background = '#F60';
elem.style.color = '#FFF';

Conclusion

Pour ceux qui n'appréhendent Javascript que par le biais de jQuery, il me paraissait intéressant de leur montrer comment écrire des fonctionnalités similaires en natif, et donc en fait, ce qui se cache un peu derrière cette bibliothèque.

Dans le prochain article je vais commencer à vous décrire une bibliothèque personnelle - Q.lib.js - (Q pour Quick) permettant d'utiliser une syntaxe quasi identique à celle de jQuery au niveau des méthodes mais évidemment beaucoup plus performante.


jeudi 7 août 2014

Web Mobile : faut-il abandonner JQUERY - part 2

Touches pas à mon JQUERY

Je l'avoue, je savais bien que l'article précédent susciterait la polémique. Il ressemble à un troll, a le goût du troll mais ce n'en est pas un.

En fait le problème avec JQUERY et d'autres libs orientées DOM et widgets est qu'ils ont été conçus à la base pour une exploitation PC. Et donc, les web-designers, les concepteurs de frameworks (bootstrap par exemple) l'utilisent intensément, accroissant ainsi la dépendance, mais aussi l'expérience des concepteurs.

Je comprends donc très bien les réactions du type "mais si tu nous enlève jquery, comment on va faire?", ou bien "qu'as tu donc à nous proposer pour réaliser aussi facilement un site ou une webapp?", ou bien encore "montre nous une de tes réalisations pour voir...". 

On notera que l'objet principal de l'article, à savoir les performances très faibles de JQUERY n'est pas remis en cause, il est même largement admis y compris par ceux qui critiquent ce même article.


C'est quoi le problème?

À mon avis il y a une incompréhension du concept même de mobilité de la part des designers. Ils pensent "présentation" avant "contenu.
Leurs objections vont de "Comment je vais faire de jolis trucs, pleins de beaux widgets, facilement sans mon framework favori?", à "Et tu fais comment pour faire des graphiques sans ...", en passant par "il faut que çà plaise au client, même si celui-ci a mauvais goût", etc... ad libitum.


Un petit mot pour les clients... et les autres

Je viens d'un monde où l'on privilégie dans l'ordre :

  1. La sécurité
  2. L'information
  3. L'efficacité / L'utisabilité
  4. L'économie des ressources
Ces 4 principes sont à l'origine de mon discours actuel, et le monde professionnel dans lequel j'évolue est celui dans lequel un médecin ou un enquêteur de l'OMS ou toute autre ONG doit pouvoir déterminer les besoins en sang ou antibiotiques ou plasma suite à l'afflux de blessés à l'hopital de Misrata par exemple, ou bien encore relever des témoignages, photos à l'appui suite à un massacre de plus au Soudan du sud...

Alors les discussions esthético-dev.facile ne me concernent pas vraiment dans le sens où ce sont des variables totalement subjectives et surtout dépendant de la mode (bootstrap par exemple).

Vous noterez que les opinions émises se concentrent sur 'comment faire sans ...'. En aucun cas ces concepteurs se demandent si leur façon d’aborder la mobilité est justifiée puisque pour eux, par définition le web c'est du web et qu'on a juste un problème de plus à régler à cause de la taille des écrans.

CROSS-BROWSER IS NOT CROSS-PLATFORM

Voilà quelque chose que les web-designers n'ont pas dut vraiment assimiler. Ce n'est pas vraiment de leur faute il est vrai. Beaucoup d'entre eux (et moi le premier) ont abordé le concept de mobile-site à partir d'un site web classique préexistant. Déjà, cette approche ne permet pas de véritablement prendre en compte la spécificité des usages mobiles, et ce traduit souvent par un appauvrissement sémantique sur la version mobile sans pour autant faciliter la consultation.
Par exemple, combien de sites d'information permettent de sauvegarder les nouveaux articles pour une consultation offline en cas de coupure réseau voulue ... ou pas? Combien de sites marchand permettent de conserver le panier sur le device et de reprendre ses achats le soir en rentrant à la maison alors qu'on ne s'est toujours pas identifié en tant que nouveau client?

Ainsi, que l'on parte d'un site web préexistant ou que l'on aborde une réalisation cross-browser (en considérant donc qu'un navigateur mobile n'est qu'une forme limitée des navigateurs classiques) on passe à coté de la spécificité de la navigation mobile.

Penser Mobile

Suite aux avis unanimes me demandant de proposer une alternative à JQUERY, je ne répondrait pas de façon manichéenne qu'il faut (faudrait) le remplacer par tel framework ou telle lib.js, parce que, comme je viens de l'expliquer ci-dessus le problème n'est pas en soit JQUERY ou Angular ou même Bootstrap, mais l'architecture.

Ma réponse ou plutôt mes réponses iront dans ce sens. Définir les besoins et contraintes liées à la mobilité, comment concevoir une webapp efficiente, un site-mobile réellement adapté à la mobilité, le tout avec des propositions de solutions techniques.

Ces éléments que j'apporterai au cours des articles suivant, enrichis de vos commentaires, nous permettront sans aucun doute de faire converger différentes solutions vers une véritable expérience de la mobilité.



lundi 4 août 2014

Web Mobile: devons nous abandonner JQUERY?

Pourquoi abandonner JQUERY?

Tout d'abord soyons bien clair: JQUERY n'est PAS un véritable framework, c'est tout au plus un ensemble de wrappers et de polyfills permettant 1) de simplifier l'écriture, 2) d'assurer la compatibilité du code entre les différents navigateurs (principalement Internet Explorer!!!).

Il est vrai que de nos jours, beaucoup de web-designers ont commencé leur apprentissage avec JQUERY, mais, à l'heure de HTML5 et du web mobile il est temps de mettre un terme à sont utilisation, d'une part parce que la compatibilité du code Javascript au sein des principaux navigateurs mobiles est déjà là (et ce ne sont pas les parts de marché marginales d'IE mobile qui m'intéressent ici), d'autre part parce que JQUERY est beaucoup trop LOURD et LENT!

JQUERY c'est lourd?

jquery.mobile-1.4.3.min.js    : 198Ko
jquery.mobile-1.4.3.min.css : 207Ko 

Soit 405Ko à télécharger (configuration par défaut), tout ça pour gérer du DOM ...

JQUERY c'est lent?

Oui! Très lent même!

Démonstration avec chrome mobile

ici on teste JQUERY vs querySelector en sélectionnant une classe CSS.

jQuery('.selectme').hide
17 552 Ops/s
document.querySelector('.selectme').style.display = 'none';
505 819 Ops/s

La version querySelector est environ 29 fois PLUS RAPIDE!


Et ce n'est guère mieux sur l'accès par un id ...

var d = $('#myId');
68 136 Ops/s
var d = document.getElementById('#myId');
1 281 515 Ops/s


La version native est environ 19  fois PLUS RAPIDE!

L'utilisation de JQUERY implique mécaniquement l'utilisation de 20 fois plus environ de cycles CPU soit une consommation 20 fois supérieures à ce que l'on pourrait obtenir en utilisant du JS  moderne et standard... et dans le monde des webapps et du mobile la problématique vitesse/consommation est un point très important.
 

Conclusion (provisoire)


Nous verrons dans les prochains articles comment concevoir des applis ou des sites mobiles légers et rapides en s'affranchissant des frameworks qu'affectionnent tant les webdesigners qui à mon avis n'ont plus leur place à l'heure du HTML5. Nous verrons aussi quelques techniques pour optimiser notre code.