javascript, jquery, plugins

Ahah & unobtrusive javascript

07.27.09 | 8 Comments

DEMO

IL PROBLEMA

Javascript non intrusivo ha un problema di fondo.
Lavorando sull’evento window.onload, i componenti javascript non intrusivi, NON si attivano nel momento in cui viene fatta un’ iniezione html nel DOM (AHAH).

Qui ho scritto un banale esempio.

Il plugin aggiunge le funzionalità draggable e resizable (jQuery UI) agli elementi HTML selezionati.

Il plugin come “comportamento di default” agisce su tutti gli elementi con classe “box” :


$(document).ready(function(){
	$(".box").myplugin();
});

Quindi nella demo tutti gli elementi con classe “box” saranno draggabili e ridimensionabili.

Il link ADD, nella DEMO, aggiunge elementi box via ajax (AHAH):


$("#add").click(function(){
    $.ajax({
        url     : 'box.html',
        success : function(html){
            $("body").append($(html).myplugin());
        }
    });
});

Questi elementi arrivando da un iniezione HTML non vengono “arricchiti” dal plugin (non sono quindi ridimensionabili ne trascinabili) perchè non sono presenti al window.onload (al document.ready per chi usa jquery).

Questo problema non si avvertirebbe se il componente fosse intrusivo (js cablato nell’html):


<div id="box1"></div>
<script type="text/javascript">
//<![CDATA[
$("#box1").myplugin();
//]]>
</script>

Se io iniettassi il codice qui sopra il box si attiverebbe perchè verrebbe valutato il js (almeno se iniettato via jquery) immediatamente senza eventi onload di mezzo.

Spesso i framework server-side sono intrusivi proprio per essere più flessibili.
Il codice html generato sarà sporco (richfaces ad esempio) ma i componenti saranno sempre “funzionanti” sia al primo caricamento sia a seguito di iniezioni html.

SOLUZIONE MANUALE

La soluzione “manuale” (a carico dello sviluppatore che implementa il comportamento e USA il plugin) è attivare le funzionalità js prima di appendere il nuovo pezzo di html. DEMO


$("#add").click(function(){
    $.ajax({
        url     : 'box.html',
        success : function(html){
            $("body").append($(html).myplugin());
        }
    });
});

Questa è la soluzione che più di frequente utilizzo.

Se invece avessi scritto il plugin ecco come potrei fare (quindi spostando la responsabilità al plugin stesso).

SOLUZIONE AUTOMATICA

L’idea è : quello che attivo al document.ready lo attivo anche all’evento ajaxStop (quindi inizializzo il plugin sia al window.onload sia alla chiusura di ogni chiamata ajax).


$(document)
.ready(function(){
    $(".box").myplugin();
})
.ajaxStop(function(){
    $(".box").myplugin();
});

Il problema di questa soluzione è che applicando 2 volte ad un elemento lo stesso plugin si “rischia” spesso di ottenere degli effetti indesiderati (doppi eventi al click, interfaccia che si moltiplica a mo’ di matrioska, ecc…).

La soluzione quindi è realizzare un qualche sistema di singleton all’elemento che si arrichisce.
Nel plugin finale ho scritto un banale if che aggiunge una certa classe (flag) all’elemento arricchito.
Se un evento (ajaxStop) invoca il costruttore del plugin più volte il codice non verrà eseguito.


//singleton
if ($(that).is(".myplugin")) {
   return;
} else {
   $(that).addClass(".myplugin");
}
//plugin code...

Popularity: 2% [?]

8 Comments

  1. Gennaro
    Posted 27 July 2009 at 23:13 | Permalink

    2° soluzione automatica (responsabilità affidata alla libreria): utilizzare il metodo live se si utilizza jQuery 1.3, oppure il plugin livequery per le versioni precedenti.
    Entrambi fanno uso della Event Delegation.

    Ciao!

  2. Posted 28 July 2009 at 09:29 | Permalink

    grazie, ci provo subito :)

  3. Marcel
    Posted 28 July 2009 at 10:00 | Permalink

    Hello Massimiliano, first of all, congratulations for your jquery google chart, it helps me a lot doing my job. But I have to tell you that I have a little problem with that. I created a php class that creates an structure like this:

    and I insert that into my php page.

    In firefox I have no problems with that, but in explorer I cannot do it, in ie, if I want to see it, I need to write the code manually, and obviously, that is not what I want to do.

    Any idea that could help me to solve my problem???

    Thank you by advance,

    cheers, Marcel

  4. Marcel
    Posted 28 July 2009 at 10:03 | Permalink

    Well I don’t know why I can’t show my code, maybe for internal restrictions that you have. It contents a div with a rand id, and a jquery appended to it,

    Thanks!!

  5. Posted 28 July 2009 at 10:27 | Permalink

    @Marcel

    1) could you send me a link in which I can see the problem?
    2) the typical error in IE is this (in my experience)

    $(“.something”).plugin({
    option1 : “value”,
    option2 : “value”, //THIS COMMA CRASH IE JAVASCRIPT INTERPRETER
    });

  6. Posted 31 July 2009 at 12:31 | Permalink

    @Gennaro

    grazie per lo spunto. mi ero sempre ripromesso di guardare i metodi live/die e questa è stata l’occasione.
    però non risolve il problema delle iniezioni ajax.

    Quello che ci vorrebbe secondo me… è una sorta di “aggancio” a tutte le istruzioni presenti nel “document.ready” (o nel bind(“load”)) per poterle applicare ai SOLI contenuti iniettati.

    In pratica sarebbe molto comodo e utile applicare all’html iniettato l’evento “ready/load”
    e se tutto questo fosse trasparente allo sviluppatore sarebbe PERFETTO.

    success : function(html){
    $(html).trigger(“ready”);
    }

  7. Gennaro
    Posted 31 July 2009 at 14:48 | Permalink

    Ciao Massimiliano,
    non mi è chiaro cosa intendi quando affermi che “non risolve il problema delle iniezioni ajax”. In generale infatti, i metodi live() e die() funzionano senza problemi con elementi del DOM iniettati con i metodi ajax, in tutte le declinazioni ($.get(), $.post(), $.ajax() e load()).
    Se invece ti riferisci alla necessità di bindare agli eventi solo ed unicamente gli elementi del DOM iniettati via ajax, allora hai due strade:
    a) utilizzi per gli elementi iniettati dei selettori (classi/id) differenti da quelli già caricati al document.ready, ed utilizzi live()/die()
    b) la tua soluzione del singleton tramite flag.

    Ciao,
    Gennaro

  8. Posted 31 July 2009 at 15:20 | Permalink

    @gennaro

    Ciao Gennaro,
    live() devo studiarlo meglio. qui ho fatto una prova. non ha senso e non funziona [te lo dico subito]. oggi stacco tutto. ci ripenserò a settembre.

    http://maxb.net/scripts/ahah_issue/live.html

    qui invece ho provato a realizzare un aggancio all’evento ready con “contesto dinamico” (rileggendolo manca solo supercazzola e antani… lo so :) ).

    http://maxb.net/scripts/ahah_issue/ready.html

    spero che da codice sia più chiaro.

Post a Comment

Your email is never shared. Required fields are marked *

*
*