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:

  1. 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.
  2. 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.

 

https://wiki.haskell.org/The_JavaScript_Problem​

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

Exemple de code optimisé : ici

 

 

 

jQuery : Mauvaise utilisation n°2

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...

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

"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

/*

    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

$('#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...

 

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 ! 

@jQuerySF : We are excited to announce that @linclark will be talking about using @npmjs + @jquery + @browserify + @babeljs to build a modular widget! 

jQuery : Mes supports de cours...

GPL/MIT/COPYLEF - @molokoloco 2015 - http://b2bweb.fr

jQuery : Aller plus loin...

Pour aller plus loin (pour les experts ?)...

 

Merci !

Des questions ?

 

GPL/MIT/COPYLEF - @molokoloco 2015 - http://b2bweb.fr

jQuery "The good parts"

By Julien G

jQuery "The good parts"

Is jQuery always relevant today ? TL;DR Yep !

  • 7,288