GPL/MIT/COPYLEF - @molokoloco 2015 - http://b2bweb.fr
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 toujours très populaire... http://libscore.com/#jQuery
jQuery toujours très populaire... http://libscore.com/#jQuery
jQuery toujours très populaire... http://libscore.com/#jQuery
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 !
Pourquoi l'utiliser ?
Pourquoi ne pas l'utiliser ?
Pourquoi l'utiliser ?
Pourquoi ne pas l'utiliser ?
Le poids de jQuery, un problème ?
Pas d'autres impacts. Namespace limité à "jQuery" et "$"
La vitesse de jQuery un problème ?
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
Boostrap, Wikipédia ou Worpdress utilisent jQuery ? Parce que ça marche ?
Mon point de vue et celui des autres...
jQuery, c'est avant tout du JavaScript...
The problem : The JavaScript problem is two-fold and can be described thus:
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
Second problème du dévéloppeur JS et jQuery : savoir faire la part des choses !
Just a reminder that jQuery 2.x still fixes 88 bugs in modern browsers
to give you a consistent dev experience...
$("#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.
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…
});
});
Bon exemple "Vanilla"
$("a").click(function() {
document.location.hash = this.id; // ok !
});
$("a").click(function() {
document.location.hash = $(this).attr('id');
});
"Mauvais" exemple "Vanilla"
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 :
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, ...
Les "bons" usages de jQuery
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');
});
});
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(); });
};
La gestion des appels AJAX et JSONP
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');
});
});
Les selectors + $.filter()
$('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").
Les events et events customs
// 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');
Les promesses ("Promises")
// 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"); });
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
Les bons plugins de jQuery !
Il existe des plugins de qualité ! Petits exemples...
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...
Au dela du "build modulaire", jQuery continue d'évoluer, pour le bien !
Mes supports de cours pour "débutants", en open source "Copyleft"
GPL/MIT/COPYLEF - @molokoloco 2015 - http://b2bweb.fr
Pour aller plus loin (pour les experts ?)...
GPL/MIT/COPYLEF - @molokoloco 2015 - http://b2bweb.fr