jQuery Spaghetti

Le plus gros problème avec jQuery

Aucune organisation de code n'est proposée
Comment sommes-nous introduit a jQuery?

ce n'est pas que jQuery fait quelque chose de mauvais mais plus - pas de direction de comment utiliser l"api

Le fameux custom.js, une nouvelle aventure vous attends ...

image

"Acceptable" pour de petits sites web

image

Plus le temps avance..

Plus cette façon de travailler devient inefficace

<script language="JavaScript">
<!-- 
// This is the function that will open the
// new window when the mouse is moved over the link

function selectlink(currentDell,linkdesc){
    currentDell.style.background='yellow';

    if (document.getElementById)
      document.getElementById("selectdesc").innerHTML=linkdesc;
    else
      selectdesc.innerHTML=html;
}
function leavelink(currentDell){
    currentDell.style.background='blue';

    if (document.getElementById)
      document.getElementById("selectdesc").innerHTML='&nbsp;';
    else
      selectdesc.innerHTML='&nbsp;';
}

function open_new_window(a,i) 
{


 info=document.getElementById('div'+a.id);

if(info.className=='on') {
   info.className='off';
   pic.src='plus.GIF'; 
 }
else {
   info.className='on';
   pic.src='minus.GIF';                     
  }

}

// This is the function that will close the
// new window when the mouse is moved off the link
function close_window(a,i) 
{
info=document.getElementById('div'+a.id);

if(info.className=='on') {
   info.className='off';
   pic.src='plus.GIF'; 
 }
else {
   info.className='off';
   pic.src='minus.GIF';                     
  }

}

// -->
 <script type="text/javascript">
                           if (screen.width == 800 )
{
     document.write("<img height='100' src='../images/800.jpg' width='100%'>");

}else if (screen.width == 1024)
{

 document.write("<img height='110' src='../images/1024.jpg' width='100%'>");

}else if (screen.width == 1152 )
{

 document.write("<img height='100' src='../images/1152.jpg' width='100%'>");

}else if (screen.width == 1280 )
{

 document.write("<img height='80' src='../images/1280.jpg' width='100%'>");

}else if (screen.width == 1400 )
{

 document.write("<img height='125' src='../images/1400.jpg' width='100%'>");

}
else if (screen.width == 1600 )
{

 document.write("<img height='145' src='../images/1600.jpg' width='100%'>");

}

          

                                        </script>
                    
                     <script type="text/javascript">
  
  else if (document.test.tclass.value==4)// this condition checks which class you have selected and you can add subject according to class 4
  {
    document.test.tsubject.options[0].text="..Select Subject..";
    document.test.tsubject.options[1].text="Mathematics";
    document.test.tsubject.options[2].text="Hindi";
    document.test.tsubject.options[3].text="Environmental Studies"
    document.test.tsubject.options[4].text="English"
    document.test.tsubject.options[5].text="Urdu"
  } 
  
else if (document.test.tclass.value==5)// this condition checks which class you have selected and you can add subject according to class 5
  {
    document.test.tsubject.options[0].text="..Select Subject..";
    document.test.tsubject.options[1].text="Mathematics";
    document.test.tsubject.options[2].text="Hindi";
    document.test.tsubject.options[3].text="English";
    document.test.tsubject.options[4].text="Environmental Studies";
    document.test.tsubject.options[5].text="Urdu";
    //document.test.tsubject.options[6].text="";
    
  } 
  else if (document.test.tclass.value==6)// this condition checks which class u have select and you can add subject according to class  6
  {
    document.test.tsubject.options[0].text="..Select Subject..";
    document.test.tsubject.options[1].text="Hindi";
    document.test.tsubject.options[2].text="English";
    document.test.tsubject.options[3].text="Mathematics";
    document.test.tsubject.options[4].text="Social Studies";
    document.test.tsubject.options[5].text="Sanskrit";
    document.test.tsubject.options[6].text="Science";
    document.test.tsubject.options[7].text="Urdu";
    //document.test.tsubject.options[8].text="Environmental Education";
    //document.test.tsubject.options[9].text="";
    
  } 
  
else if (document.test.tclass.value==7)// this condition checks which class u have select and you can add subject according to class 7 
  {
    document.test.tsubject.options[0].text="..Select Subject..";
    document.test.tsubject.options[1].text="Mathematics";
    document.test.tsubject.options[2].text="Science";
    document.test.tsubject.options[3].text="English"
    document.test.tsubject.options[4].text="Sanskrit"
    document.test.tsubject.options[5].text="Social Science"
    document.test.tsubject.options[6].text="Hindi"
    document.test.tsubject.options[7].text="Urdu"
    //document.test.tsubject.options[8].text="Environmental Education"
  } 
else if (document.test.tclass.value==8)// this condition checks which class u have select and you can add subject according to class 8  
  {
    document.test.tsubject.options[0].text="..Select Subject..";
    document.test.tsubject.options[1].text="English";
    document.test.tsubject.options[2].text="Mathematics";
    document.test.tsubject.options[3].text="Hindi";
    document.test.tsubject.options[4].text="Science";
    document.test.tsubject.options[5].text="Social Science";
    document.test.tsubject.options[6].text="Sanskrit";
    document.test.tsubject.options[7].text="Urdu";
    //document.test.tsubject.options[7].text="Environmental Education";
    //document.test.tsubject.options[8].text="";
    //document.test.tsubject.options[9].text="";
    
  } 
  else if (document.test.tclass.value==9)// this condition checks which class u have select and you can add subject according to class 9 
  {
    document.test.tsubject.options[0].text="..Select Subject..";
    document.test.tsubject.options[1].text="English";
    document.test.tsubject.options[2].text="Hindi";
    document.test.tsubject.options[3].text="Sanskrit";
    document.test.tsubject.options[4].text="Mathematics";
    document.test.tsubject.options[5].text="Science";
    document.test.tsubject.options[6].text="Social Science";  
    document.test.tsubject.options[7].text="Urdu";  
    //document.test.tsubject.options[8].text="Environmental Education";    
    //document.test.tsubject.options[9].text="";
    
  } 

    
    document.test.tbook.options[5].value=""         
  }
  
  else if((document.test.tclass.value==13) && (document.test.tsubject.options[sind].text=="Information Technology"))
  {
    document.test.tbook.options[0].text="..Select Book Title..";
    document.test.tbook.options[1].text="Information Technology in Schools";
    document.test.tbook.options[1].value="textbook.htm?syit1=0-1"
    
              
  }
  
  
  else if(document.test.tsubject.options[sind].text=="..Select Subject..")
  {
    document.test.tbook.options[0].text="..Select Book Title..";
    document.test.tbook.options[1].text="";
  }
}
}

custom.js devient trop souvent une poubelle

image

Changeons ca.

Quand Backbone, javascriptMVC, etc ne sont pas nécessaires?

En gros, quand votre framework back-end fait tout le "heavy lifting"

Ce qu'il reste, c'est la gestion d'événements, pourquoi se compliquer la vie?

Prenons un exemple

Events

Delegate / Custom events

3 types d'event handler

Le bubbling (Event delegation), version courte

Quand un événement se produit, une réaction en chaine se produit dans le DOM et il est possible de "catcher" l'événement sur tous les parents du node.

Oups!

Depuis la création de ces slides delegate, live et bind on été mis au rencart pour on().
Ex: $("#container").on("click", ".btn", function(){ // do stuff })

Pourquoi utiliser delegate()?

Avoir un contexte c'est important

Delegate est très utile lorsqu'on l'utilise associé à un module ou un bloc html précis.

Retournons à l'example

jQuery(document).ready(function($) {
  $("#usersModule").delegate("click", ".btnDelete", function(event) {
    $(this).parent().parent().remove();
  });
  $("#usersModule").delegate("click", ".btnModify", function(event) {
    $(this).parent().parent().find("input").css("display"," block");
    $(this).parent().parent().find(".name").css("display"," none");
  });
  $("#usersModule").delegate("click", ".btnSave", function(event) {
    $(this).parent().parent().find(".printNom").html($(this).parent().find("input").val());
    $(this).parent().parent().find("input").css("display"," none");
    $(this).parent().parent().find(".printNom").css("display"," bloxk");
  });
  $("#usersModule").delegate("click", ".btnAdd", function(event) {
    $("ul#listInvite").find("li:last").after("<li class="item"><input name='nom'><div class="printNom"></div> <div class="tools"><a class='btnModify' href='#'>Modifier</a><a class='btnDelete' href='#'>Detruire</a></li>");
  });
})(jQuery);

Custom Events, c'est quoi? ça sert à quoi?

Custom Events

// Pour ouvrir un lightbox en script en utilisant le plugin colorbox
// Ce script n'est la qu'une fois au travers de notre application
$(document).bind('lightbox.open', function(event, config) {
  $.colorbox(config)
});


// Dans notre script, on ouvre un lightbox
  $(document).trigger('lightbox.open', [{"href":"/popup", "height":400}]);

Découpler vos fonctionnalités

image

$(document).........

Les custom events de jQuery sont malheureusement associés au DOM, la plupart du temps ça ne sert absolument à rien. Heureusement d'autres solutions existent..

Dites bonjour à PUB/SUB

// On veut un effet "growl" qui averti notre utlisateur quand certaines actions sont déclenchées.
$.subscribe('alert.show', function(customConfigs) {
    var configs = {
    title: '',
    text: '',
    image: 'http://a0.twimg.com/profile_images/59268975/jquery_avatar_bigger.png',
    sticky: false, 
    time: 8000
  }
  $.extend(configs, customConfigs, true);

    $.gritter.add(defaults);
});

// Dans ce cas on alert l'utilisateur qu'un utilisateur a été supprimé
 $.publish('alert.show', [{"title":"One user has been deleted!"}]);

Petite note sur les collections jQuery..

Chaque fois que vous utilisé $() une nouvelle collection jQuery est créée, ce pourquoi il est préférable de la sauvegarder quand il y a plusieurs utilisations

Les events

Abusez de delegate!

Utilisez live avec parcimonie

Réduisez votre dépendance aux plugins et decouplez mieux votre code avec les custom events

Selector Traversing

.parent().parent().parent()

Ou comment être certain que le prochain developeur à modifier le html casse le fonctionnement du script

Direction l'enfer

Bonjour closest("#myParentID")

closest vous permet de traverser les sélecteurs en toute securité.

closest("#myParentID")

jQuery(document).ready(function($) {
  var jQuser = $("#usersModule");
  jQuser.delegate(".btnDelete", "click", function() {
    $(this).closest('.item').remove();
    return false;
  });
  jQuser.delegate(".btnModify", "click", function() {
    var jQitem = $(this).closest('.item');
    jQitem.find("input").css("display"," block");
    jQitem.find(".name").css("display"," none");
    return false;
  });
  jQuser.delegate(".btnSave", "click", function() {
    var jQitem = $(this).closest('.item');
    jQitem.find(".printNom").html($(this).parent().find("input").val());
    jQitem.find("input").css("display"," none");
    jQitem.find(".printNom").css("display"," block");
    return false;
  });
})(jQuery);

Request Ajax

Un "petit nouveau" depuis 1.5: Deferred

is a chainable utility object that can register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.

Tellement 2007...

Soyez hipster

image

Exemple avec deferred

Deferred ajoute beaucoup de fonctionnalités aussi

$.when

Structure

Backbone views

La meme structure sans backbone

var mailapp.events.users = {
  initialize: function(){
    this.$usersWrapper = $("#usersWrapper");
      if(!this.$usersWrapper.length) this.loadEvents();
  },
  loadEvents: function(){
    var _this = this;
    // launch our modal events
    $.subscribe("modal.complete", function(){ _this.loadPopupEvents(); });
  
      // Delegate give a context to our events
      this.$usersWrapper
        .delegate("#btnDelete","click", function() { _this.delete(); return false; });
        .delegate("#btnsave","click",   function() { _this.save(); return false;));
        .delegate("#btnModify","click", function() { _this.modify(); return false;));
  },
  loadPopupEvents : function() {
    $(".myPopupForm").validationEngine();
  },
  delete: function(){
    $(this).closest('.item').remove();
  },
  modify: function(){
    var jQitem = $(this).closest('.item');
    jQitem.find("input").css("display"," block");
    jQitem.find(".name").css("display"," none");
  },
  save : function(){
    var jQitem = $(this).closest('.item');
    jQitem.find(".printNom").html($(this).parent().find("input").val());
    jQitem.find("input").css("display"," none");
    jQitem.find(".printNom").css("display"," block");
  }
}

Initialize

On regarde si la page contient le module

 initialize: function(){
    this.$usersWrapper = $("#usersWrapper");
      if(!this.$usersWrapper.length) this.loadEvents();
  },

loadEvents

Si on est dans le module on load les events

 loadEvents: function(){
    var _this = this;
    // launch our modal events
    $.subscribe("modal.complete", function(){ _this.loadPopupEvents(); });
  
      // Delegate gives a context to our events
      this.$usersWrapper
        .delegate("#btnDelete","click", function() { _this.delete(); return false; });
        .delegate("#btnsave","click",   function() { _this.save(); return false;));
        .delegate("#btnModify","click", function() { _this.modify(); return false;));
  },

loadPopupEvents

Init quand le popup ouvre

 loadPopupEvents : function() {
    $(".myPopupForm").validationEngine();
  },
var mailapp.events.users = {
  initialize: function(){
    this.$usersWrapper = $("#usersWrapper");
      if(!this.$usersWrapper.length) this.loadEvents();
  },
  loadEvents: function(){
    var _this = this;
    // launch our modal events
    $.subscribe("modal.complete", function(){ _this.loadPopupEvents(); });
  
      // Delegate give a context to our events
      this.$usersWrapper
        .delegate("#btnDelete","click", function() { _this.delete(); return false; });
        .delegate("#btnsave","click",   function() { _this.save(); return false;));
        .delegate("#btnModify","click", function() { _this.modify(); return false;));
  },
  loadPopupEvents : function() {
    $(".myPopupForm").validationEngine();
  },
  delete: function(){
    $(this).closest('.item').remove();
  },
  modify: function(){
    var jQitem = $(this).closest('.item');
    jQitem.find("input").css("display"," block");
    jQitem.find(".name").css("display"," none");
  },
  save : function(){
    var jQitem = $(this).closest('.item');
    jQitem.find(".printNom").html($(this).parent().find("input").val());
    jQitem.find("input").css("display"," none");
    jQitem.find(".printNom").css("display"," block");
  }
}

Revealing Pattern

Plugin Pattern

(function($)
{
    var defaultSettings = {
        "position":"top"
    };

    $.fn.companyTooltip = function(settings)
    {
        settings = $.extend({}, defaultSettings, settings || {});

        return this.each(function()
        {
            var elem = $(this);

            //run some code here
        }
    }

})(jQuery);

Wrapper dans un closure

(function($)
{
})(jQuery);

Creer et extender les settings

     var defaultSettings = {
        "position":"top"
    };

    $.fn.companyTooltip = function(settings)
    {
        settings = $.extend({}, defaultSettings, settings || {});
    

N'oublier pas de retourner l'élément!

        return this.each(function()
        {
            var elem = $(this);

            //run some code here
        }

Plugin Pattern

(function($)
{
    var defaultSettings = {
        "position":"top"
    };

    $.fn.companyTooltip = function(settings)
    {
        settings = $.extend({}, defaultSettings, settings || {});

        return this.each(function()
        {
            var elem = $(this);

            //run some code here
        }
    }

})(jQuery);

image

Questions?