jQuery (2015)
"The good parts"
GPL/MIT/COPYLEF - @molokoloco 2015 - http://b2bweb.fr
jQuery : We know jQuery !
jQuery : Une librairie mature
JavaScript fête ses 20 ans... et jQuery ses 8 ans !
Sur Google Trend on voit un pic quand jQuery est devenue la principale librairie JavaScript pour le front-end (2012), une apogée, son "monopole". Nous voyons maintenant une tendance à la stabilisation /baisse...
Une partie importante des développeurs actuels connaissent bien jQuery et cherchent donc moins des réponses sur Google
jQuery : Une librairie mature
jQuery toujours très populaire... http://libscore.com/#jQuery
jQuery : Une librairie mature
jQuery toujours très populaire... http://libscore.com/#jQuery
jQuery : Une librairie mature
jQuery toujours très populaire... http://libscore.com/#jQuery
jQuery : Rappel
jQuery is a cross-platform JavaScript library designed to simplify the client-side scripting of HTML.[2] jQuery is the most popular JavaScript library in use today.[3][4] jQuery is free, open-source software licensed under the MIT License.[1]
jQuery's syntax is designed to make it easier to navigate a document, select DOM elements, create animations, handle events, and develop Ajax applications.
jQuery also provides capabilities for developers to create plug-ins on top of the JavaScript library. The modular approach to the jQuery library allows the creation of powerful dynamic web pages and web applications.
Write less do more !
jQuery : Cadre d'utilisation
Pourquoi l'utiliser ?
- Sites personnels
- Sites profesionels
-
Prototypes composants/webapps
- Animations complexes d'UI
Gestion fine du DOM et des CSS(3) -
Manipulation d'éléments HTML5/JS
SVG, Vidéos, Canvas
- Formulaires
-
Ajax
- Faire un plugin
Pourquoi ne pas l'utiliser ?
- SSII / backends "métiers" complexes
-
Agences de "sites en marque blanche"
- Graphs complexes
- Web GL
-
Sofware UI
- Faster pussycat bare metal real time apps
jQuery : Cadre d'utilisation
Pourquoi l'utiliser ?
jQuery : Cadre d'utilisation
Pourquoi ne pas l'utiliser ?
jQuery : Idée reçu n°1
Le poids de jQuery, un problème ?
- jquery-2.1.4.min.js : 83Ko (gzippé, 30ko)
- Google logo (accueil) : 14Ko
- Facebook home (max. acceptable ?) : 1.9MB
Pas d'autres impacts. Namespace limité à "jQuery" et "$"
jQuery : Idée reçu n°2
La vitesse de jQuery un problème ?
- Test avec création de 60 élements DOM/seconde
http://molokoloco.github.io/jQuery.boxFx/#hyperSpace - Comparing methods for looping through an array in JavaScript
http://jsperf.com/native-loop-performance/16
- D'une manière générale, le DOM reste bien plus lent que le JS et le JS est rarement la cause de ralentissement dans l'interface sauf si il part d'une mauvaise conception au départ
- Perfs helpers : $.detach(), $.clone(), $.addClass('hidden'), ...
- Perfs killers : $.hide(), manipulate in loop or at the middle of a page when rendered... , ...
jQuery : Mon point de vue...
jQuery est une boîte à outil extrêmement pratique et simple pour être créatif dans la réalisation d'interfaces interactives et robustes
jQuery s'adresse autant aux débutants qu'aux experts, avec différents niveaux d'utilisations possibles
- Les défauts qu'on lui reproche sont souvent dû à sa popularité et à la publication de mauvais code (mauvaises utilisations)
- Cela reste un code simple et solide, à l'épreuve du temps
- jQuery laisse le champs libre à l'échange et la réutilisation du code
- Pas de limites autre que celles du DOM/CSS3 : On peut tout faire !
Boostrap, Wikipédia ou Worpdress utilisent jQuery ? Parce que ça marche ?
jQuery : Mon point de vue...
Mon point de vue et celui des autres...
jQuery : Mon point de vue...
jQuery, c'est avant tout du JavaScript...
The problem : The JavaScript problem is two-fold and can be described thus:
- JavaScript sucks. The depths to which JavaScript sucks are well-documented and well-understood. Its main faults are: lack of module system, weak-typing, verbose function syntax1, late binding2, which has led to the creation of various static analysis tools to alleviate this language flaw3, but with limited success4 (there is even a static type checker5), finicky equality/automatic conversion, this behaviour, and lack of static types.
- We need JavaScript. Using it for what it is good for, i.e. providing a platform for browser development, but not using the language per se, is therefore desirable, and many are working to achieve this, in varying forms. There are various ways to do it, but we ought to opt for compiling an existing language, Haskell, to JavaScript, because we do not have time to learn or teach other people a new language, garner a new library set and a new type checker and all that Haskell implementations provide.
jQuery : Mauvaise utilisation n°1
Premier problème du dévéloppeur JS et jQuery
Avec JavaScript on favorise la manipulation des classes CSS, qu'on applique ou qu'on l'enlève sur X éléments
jQuery : Mauvaise utilisation n°1
jQuery : Mauvaise utilisation n°2
Second problème du dévéloppeur JS et jQuery : savoir faire la part des choses !
- De jQuery à Vanilla JS... http://putaindecode.fr/posts/js/de-jquery-a-vanillajs/
- Yes, you probably don’t really need it… http://lea.verou.me/2015/04/jquery-considered-harmful/
Just a reminder that jQuery 2.x still fixes 88 bugs in modern browsers
to give you a consistent dev experience...
jQuery : Mauvaise utilisation n°2
$("#myLink").hide();
$("#myLink").addClass('hidden');
jQuery a little of bad, a lot of good...
One important performance recommendation: Do not use jQuery's hide() method. Ever. Classes are your friend. With hide(), jQuery is doing much more magic behind the scenes than developers expect. And that magic comes at a significant price.
jQuery : Mauvaise utilisation n°2
De jQuery à Vanilla JS, when it's easy :
$("a").click(function() { // Ok !
// code…
});
[].forEach.call(document.querySelectorAll("a"), function(el) {
el.addEventListener("click", function() {
// code…
});
});
- addEventListener n'est pas compatible avec IE7 et 8, la version de jquery oui.
- querySelectorAll n'est pas compatible avec IE7. forEach pas compatible 7 et 8.
- classList est supporté à partir de IE 10 seulement, et ne supporte même pas toggle.
- createElement ne peut créer qu'un élément, la version jquery est recursive.
- ...
Bon exemple "Vanilla"
$("a").click(function() {
document.location.hash = this.id; // ok !
});
$("a").click(function() {
document.location.hash = $(this).attr('id');
});
"Mauvais" exemple "Vanilla"
jQuery : Mauvaise utilisation n°3
Troisième problème du dévéloppeur JS et jQuery
Manque d'unifications des designs patterns :-(
"Wow, I thought I would know how to develop a jQuery plugin – but now I know that I don’t know anything… ;)"
The words of an expert :
jQuery : Mauvaise utilisation n°3
Le design pattern que j'ai choisi...
https://github.com/molokoloco/FRAMEWORK/blob/master/jquery.plugins/jquery.tooltips.js
/*
Workable / Testable jQuery Default Plugin Boilerplate
V1.5.4 - 12/07/13 - Molokoloco - Copyleft
Demo : http://jsfiddle.net/molokoloco/E3DbT/
Download : https://github.com/molokoloco/FRAMEWORK/blob/master/jquery.plugins/jquery.tooltips.js
Blog : http://www.b2bweb.fr/molokoloco/jquery-default-plugin/
Sources code examples (jQuery Plugins Patterns Boilerplates) :
http://docs.jquery.com/Plugins/Authoring
https://github.com/jquery-boilerplate/patterns/tree/master/patterns
http://addyosmani.com/resources/essentialjsdesignpatterns/book/#designpatternsjquery
http://coding.smashingmagazine.com/2011/10/11/essential-jquery-plugin-patterns/
http://kenneth.kufluk.com/blog/2010/08/chaining-asynchronous-methods-in-jquery-using-the-queue/
*/
///////////////////////////////////////////////////////////////////////////
// Usage examples //
///////////////////////////////////////////////////////////////////////////
/*
// Example of HTML setup ////////////////////
<a href="#" id="link1" class="tooltips" title="It's a test">Tooltip 1</a> !<br />
<a href="#" id="link2" class="tooltips" title="Click to update">Tooltip 2</a> !<br />
<a href="#" class="tooltips" data-url="http://www.b2bweb.fr/_COURS/api.php?id=link2">Tooltip AJAX</a> !<br />
<a href="https://github.com/molokoloco/FRAMEWORK/blob/master/jquery.plugins/jquery.tooltips.js" class="tooltips">https://github.com/molokoloco/FRAMEWORK/blob/master/jquery.plugins/jquery.tooltips.js</a> !<br />
<hr />
<a href="javascript:void(0);" id="remove">Remove Tooltip n°2</a>
// Example CSS styling ////////////////////
.myTooltip {
background:rgb(30, 145, 212);
background:rgba(30, 145, 212, 0.9);
color:#FFFD69;
position: absolute;
z-index: 9999999;
padding:4px 8px;
-moz-border-radius:3px;-webkit-border-radius:3px;-ms-border-radius:3px;-o-border-radius:5px;border-radius:3px;
}
.myTooltipWrap {
max-width: 180px;
display: inline-block;
vertical-align: middle;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// Example n°1 ////////////////////
$(function() {
// Appel du plugin sur les liens avec la class "tooltips"
$('a.tooltips').tooltip({ // Call plug in on our collection with optional obj of optionals args
css: 'myTooltip' // Ex. : user custom CSS for the tooltip element
});
$('a.tooltips').on('click', function(event) {
event.preventDefault();
$(this).tooltip('update', 'This is the new tooltip content! '+ Math.random()); // update tooltip ?
});
$('a#remove').on('click', function() {
$('a.tooltips')
.tooltip('destroy') // Remove Tooltip plugin !
.off('click'); // Remove our custom listener below
});
});
// Example n°2 ////////////////////
$('.multiplecolumns p a').each(function(i, e) {
var $this = $(e),
text = $this.text();
if (text.substring(0, 4) == 'http') {
text = text.replace(/https?\:\/\/(www\.)?/gi, '');
$this.text(text);
if (text.length > 23) {
$this
.addClass('myTooltipWrap')
.tooltip({css:'myTooltip'});
}
}
else if (text != $this.attr('href')) {
$this.tooltip({css:'myTooltip'});
}
});
*/
///////////////////////////////////////////////////////////////////////////
// Fichier à enregistrer sous le nom "jquery.tooltip.js" //
///////////////////////////////////////////////////////////////////////////
(function($) { // our plugin is inside a closure, it keep the NameSpace safe
var plugName = 'tooltip'; // Our plugin base name !
debug = false; // Edit here to debug ;)
var privatesMethods = { // Privates plugin methods
reposition: function(event) { // Move tooltip
var mousex = event.pageX + 20, // Get event X mouse coordinates
mousey = event.pageY + 20;
$(this).data(plugName)[plugName].css({top: mousey+'px', left: mousex+'px'});
},
show: function(event) {
if (debug) console.log(plugName+'.show()');
var $this = $(this), // Current link
data = $this.data(plugName); // Current tooltip data
if (!data[plugName]) { // Create One Tooltip Div
data[plugName] = $('<div class="'+data.options.css+'">'+data.title+'</div>').hide().appendTo('body');
$this.data(plugName, data); // Update data to keep our current tooltip
}
data[plugName].hide().fadeIn(600); // Start animation
$this.on('mousemove.'+plugName, $.proxy(privatesMethods.reposition, this)); // Listen mousemove, updating tooltip position
},
hide: function(event) {
if (debug) console.log(plugName+'.hide()');
var $this = $(this), // Current link
data = $this.data(plugName); // Current tooltip data
$this.off('mousemove.'+plugName);
if (data && data[plugName]) {
data[plugName]
.stop(true, false)
.fadeOut(400);
}
}
}; // End Privates plugin methods
var publicMethods = { // Publics plugin methods
init: function(options) {
if (debug) console.log(plugName+'.init()'); // Time to Log in console ;)
// Merge custom options with defaults options
options = $.extend(
true, // Deep merge object
{}, // Merge in a new object
$.fn[plugName].defaults, // Minimal default options
typeof options == 'object' && options // User override ?
);
return this.each(function() { // Iterate current(s) element(s) collection
var $this = $(this), // Supposed to be a link or something with a title attribute !
data = $this.data(plugName);
if (data) return; // If the plugin has already been initialized for this element ?
// Fetch tooltip static content...
var title = $this.data('tooltip') || $this.attr('title') || $this.attr('href'),
fetchUrl = $this.data('url');
if (fetchUrl) { // Do some Ajax request to fetch tooltip content ?
// http://api.jquery.com/jQuery.ajax/ - http://api.jquery.com/jQuery.proxy/
if (fetchUrl) $.ajax({
dataType:'jsonp',
url: fetchUrl,
context: $this, // Keep the actual context in the success callback
success: function(data) {
if (debug) console.log('fetchUrl success', data);
if (data && data.key1) {
$.proxy(publicMethods.update, this)(data.key1);
// publicMethods.update.apply(this, [data.key1]);
}
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log('JSON error : ', textStatus);
}
});
}
else if (!title) return; // Nothing to print ?
var data = { // Create a data stock per element
options: options, // Stock individual plugin options (if needed)
title: title // Original title
};
$this
.data(plugName, data) // Stock all properties... in current element
.attr('title', '') // Remove default title
.on('mouseenter.'+plugName, privatesMethods.show) // Event + this plugin NameSpace
.on('mouseleave.'+plugName, privatesMethods.hide);
// HOW TO KEEP THE CURRENT ELEMENT CONTEXT ?
// Newly created "tooltip elements" in privatesMethods.show() are accessible via
// $(this).data(plugName)['tooltip']
// Si on est dans le context d'une méthode ou $(this) == current link
// Exemple : $(this).data(plugName)['tooltip'].html('New tooltip title');
}); // End Iterate collection
},
update: function(content) { // Update tooltip content
if (debug) console.log(plugName+'.update(content)', content);
return this.each(function() {
var $this = $(this), // One link
data = $this.data(plugName); // Fetch data relative to our element
if (!data) return; // Nothing here
if (data[plugName]) {
data[plugName].html(content);
}
else {
data.title = content;
$this.data(plugName, data);
}
});
},
destroy: function() { // Remove plugin
if (debug) console.log(plugName+'.destroy()');
return this.each(function() {
$('.'+plugName).remove(); // Remove all tooltips elements
var $this = $(this),
data = $this.data(plugName);
if (!data) return; // Nothing here
$this
.attr('title', data['title'])
.off('.'+plugName) // Remove this tooltip event(s) using .namespaceg
.removeData(plugName); // Clear data
});
}
}; // End Publics plugin methods
// Here we map our plugin publics methods to jQuery : $.fn.tooltip = function()...
$.fn[plugName] = function(method) {
if (publicMethods[method]) return publicMethods[method].apply(this, Array.prototype.slice.call(arguments, 1));
else if (typeof method === 'object' || !method) return publicMethods.init.apply(this, arguments);
else $.error('Method '+method+' does not exist on jQuery.'+plugName+'.js');
};
// Default (overridables) public options
$.fn[plugName].defaults = {
css: plugName+'Element' // Tooltips have a defaut class of "tooltipElement"
};
})(window.jQuery);
Hightly configurable, data VS this, namespace, calling and passing arguments via events, ...
jQuery : Bonne utilisation n°1
Les "bons" usages de jQuery
- Le chaînage pour la construction du DOM
- La gestion des appels AJAX et JSONP
- Les selectors + $.filter()
- Les events et events customs
- Les promesses ("Promises")
- Faire un plugin "pour tous"
jQuery : Bonne utilisation n°1
Le chaînage pour la construction du DOM
$(function() {
// Liste d'"éléments" avec propriétés top, left, color
var shapes = [
{left:200, top:20, background:'red'},
{left:100, top:50, background:'orange'},
{left:50, top:120, background:'yellow'},
];
$.each(shapes, function(index, item) {
var cssProps = item; // item == shapes[index]
$.extend(cssProps, {
border: '1px solid black',
borderRadius: '10px',
width: function() {
return 50 + (Math.random() * 50);
}
});
$('<div/>').css(cssProps).appendTo('body');
});
});
- generate random elements
var popUp = function(message, delay) {
delay = delay || 5000;
$('<div />')
.addClass('ui-corner-all')
.css({opacity: 0.96, top: $(window).scrollTop() + 100})
.html('<h1>Message...</h1><p>'+message+'</p>')
.appendTo('body')
.hide()
.fadeIn(200)
.delay(delay)
.fadeOut(400, function(){ $(this).remove(); });
};
- create elements with chaining
jQuery : Bonne utilisation n°1
La gestion des appels AJAX et JSONP
- Appel des API Flickr images
var getLatestFlickrPics = function(tag, callback) {
var flickrFeed = 'http://api.flickr.com/services/feeds/photos_public.gne?tags='+ tag + '&tagmode=any&format=json&jsoncallback=?';
$.getJSON(flickrFeed, callback);
};
// Usage :
getLatestFlickrPics('ferrari', function(data){
$.each(data.items, function(i, item){
$('<img/>').attr('src', item.media.m).appendTo('body');
});
});
jQuery : Bonne utilisation n°1
Les selectors + $.filter()
- As fast as possible with the DOM...
$('li').filter(':even').css('background-color', 'red');
https://api.jquery.com/last-selector/
Because :last is a jQuery extension and not part of the CSS specification, queries using :last cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using :last to select elements, first select the elements using a pure CSS selector, then use .filter(":last").
jQuery : Bonne utilisation n°1
Les events et events customs
- Advanced examples...
// Trigger an artificial Keydown
var enterKeyEvent = $.Event('keydown', { // Our fake event
which: $.ui.keyCode.ENTER, // Custom argument we want...
keyCode: $.ui.keyCode.ENTER
});
$('input#search').trigger(enterKeyEvent); // Dispatch event (trigger)
// Custom event
$(window)
.on('startLoad', function(event) {
console.log('startPlay', event.percent);
// ... print loader
})
.on('startPlay', function(event) {
console.log('startPlay');
// ... remove loader
});
// Later...
$(window).trigger({type: 'startLoad', percent: '10'}); // Send our event to whom listen it
$(window).trigger('startPlay');
jQuery : Bonne utilisation n°1
Les promesses ("Promises")
- Very simples examples...
// Pour que ceci marche, les objets $.ajax() et $.animate()
// héritent déjà des propriétés de Deferred (Ainsi que leur shortcut)
// Example with the “$.when().done()” pattern...
var effect = function() {
return $('div').fadeIn(800).delay(1200).fadeOut();
};
$('button').on('click', function() {
$('p').append('Started...');
$.when( effect() ).done(function() {
$('p').append('Finished!');
});
});
// Example with only the “done” callback...
$.get("test.php").done(function() {
alert("$.get succeeded");
});
// Assign handlers immediately after making the request,
// and remember the jqxhr object for this request
var jqxhr = $.ajax('example.php')
.done(function() { alert("success"); })
.fail(function() { alert("error"); })
.always(function() { alert("complete"); });
// perform other work here ...
// Set another completion function for the request above
jqxhr.always(function() { alert("second complete"); });
jQuery : Bonne utilisation n°1
Faire un plugin "pour tous"
$('#rainbow').click(function() {
$(this).animate({ backgroundColor: "native; #00f; red; hsv(60, 60, 60); red; #00f; native" }, 5000);
});
amplify.store( "storeExample1", { foo: "bar" } );
amplify.store( "storeExample2", "baz" );
// retrieve the data later via the key
var myStoredValue = amplify.store( "storeExample1" ),
myStoredValue2 = amplify.store( "storeExample2" ),
myStoredValues = amplify.store();
myStoredValue.foo; // bar
myStoredValue2; // baz
myStoredValues.storeExample1.foo; // bar
myStoredValues.storeExample2; // baz
- ...
jQuery : Bonne utilisation n°2
Les bons plugins de jQuery !
Il existe des plugins de qualité ! Petits exemples...
- Velocity is an animation engine that works with and without jQuery. It's incredibly fast, and it features color animation, transforms, loops, easings, SVG support, and scrolling. It is the best of jQuery and CSS transitions combined http://julian.com/research/velocity/
- Smoke is the most complete jQuery Plugin and designed for use with Bootstrap3 http://alfredobarron.github.io/smoke/
- A lightweight jQuery plugin for complex two-way data binding in real time http://jquerymy.com
- Filter & sort magical layouts http://isotope.metafizzy.co/
- ... la liste est longue...
Danger : Les plugins qui ré-implémentent leur propre "jQuery core" (events, selector, proxy, etc). On se retrouve avec beaucoup de code en double lorsque qu'on les mélange entre eux...
jQuery : La force tranquille, en évolution
Au dela du "build modulaire", jQuery continue d'évoluer, pour le bien !
- Github + Publishing your jQuery plugin to npm, the quick and dirty way http://blog.npmjs.org/post/111475741445/publishing-your-jquery-plugin-to-npm-the-quick
- The successor to what is now version 1.11.1 will become jQuery Compat 3.0. The successor to jQuery 2.1.1 will be jQuery 3.0 http://blog.jquery.com/2014/10/29/jquery-3-0-the-next-generations/
- The #jQuery Foundation announce the 1.0 release of the Globalize project #i18n http://blog.jquery.com/2015/04/23/announcing-globalize-1-0/ support from @Adobe @IBM and @twitter
- Google transferred the Pointer Events polyfill to the jQuery Foundation http://blog.jquery.com/2015/04/21/announcing-pep-0-3-0/
jQuery : Mes supports de cours...
Mes supports de cours pour "débutants", en open source "Copyleft"
- Cours HTML/CSS
docs.google.com/document/d/1fgCnXNqcX_-Oz0ue1vuxuK-ZpzC3bH171iojkVl0m50/ - Cours/TP JavaScript
docs.google.com/document/d/1j9KsH-YtlYuMhmcPRlqtNJ_JdrD2JUiErmwEUTWt23I/ - Cours/TP jQuery
docs.google.com/document/d/1Sl4rGvuUeJRIGlFDU1_53LOSIT0HdHuoE6nsm1Pbo9k/ - Cours/TP Animations et JS
docs.google.com/document/d/18E4RstOSZlsHy2JFK1I2cFAS0-f7hC1olL-hIwOpgfA/
GPL/MIT/COPYLEF - @molokoloco 2015 - http://b2bweb.fr
jQuery : Aller plus loin...
Pour aller plus loin (pour les experts ?)...
- https://github.com/peutetre/zanimo (Promise based CSS3 transitions) et http://github.com/gre/qanimationframe (A simple Promise wrapper for requestAnimationFrame) based on Q
- #jQuery vs #GSAP: Cage Match http://greensock.com/jquery/
- Replacing jQuery with D3 http://blog.webkid.io/replacing-jquery-with-d3/
- http://facebook.github.io/react/ ...
- https://www.meteor.com ...
- ...
Merci !
Des questions ?
GPL/MIT/COPYLEF - @molokoloco 2015 - http://b2bweb.fr
Copy of jQuery "The good parts"
By rodolphe
Copy of jQuery "The good parts"
Is jQuery always relevant today ? TL;DR Yep !
- 990