2010-02-01 16 views
29

Ancora una volta voglio caricare una pagina che contiene il proprio script in un div utilizzando $ ("divid"). Load (...). Il problema che affronto è legato agli eventi. Diciamo che attiviamo ("scimmia") dalla pagina madre e sulla pagina caricata leghiamo ("scimmia") e facciamo solo un avviso ("vincolato alla scimmia"). Se lo stesso metodo di caricamento viene chiamato più volte, il binding viene chiamato più volte. Ora potrei semplicemente smembrarlo prima di legarlo, o controllare il numero di handler prima del binding e poi non legarlo per impedirlo. Nessuna delle due opzioni è scalabile come se in seguito desiderassi associare a quel trigger in un'altra "sottopagina" (una pagina caricata in un div).Modello eventi JQuery e prevenzione di gestori duplicati

Quello che voglio idealmente fare è controllare se il gestore che sto per aggiungere esiste già, ma voglio ancora usare gestori anonimi ... (chiedendo un po 'più di quell'ultima richiesta che penso). Attualmente ho una soluzione alternativa usando i metodi predefiniti/denominati e quindi controllando questo prima del bind.

// Found this on StackOverflow 
function getFunctionName(fn) 
{ 
var rgx = /^function\s+([^\(\s]+)/ 
var matches = rgx.exec(fn.toString()); 
return matches ? matches[1] : "(anonymous)" 
} 

function HandlerExists(triggerName, handlerName) { 
     exists = false; 
     if ($(document).data('events') !== undefined) { 
      var event = $(document).data('events')[triggerName]; 
      if(event !== undefined) 
      { 
       $.each(event, function(i, handler) { 
        alert(handlerName); 
        if (getFunctionName(handler) == handlerName) { 
         exists = true; 
        } 
       }); 
      } 
     } 
     return exists; 
    } 

Questo è un modo piuttosto crudo di andare avanti mi sembra, ma sembra funzionare. Faccio quanto segue prima del bind come segue:

if (!HandlerExists("test", "theMethod")) { 
    $(document).bind("test", theMethod); 
} 

Qualcuno ha una soluzione più elegante? per esempio, c'è un modo per controllare che uno script particolare sia caricato? quindi potrei usare getScript() per caricare il js dalla pagina figlio al primo caricamento, e poi semplicemente non caricarlo sui carichi successivi (e lanciare un trigger che sarebbe gestito da lui preesistente js) ..

+0

possibile duplicato del [jquery funzione di prevenzione di duplicato assegnato] (http://stackoverflow.com/questions/1558377/jquery-prevent-duplicate-function-assigned) –

risposta

7

Questo non è una risposta completa, ma ho trovato rapidamente come risolvere tutto il mio codice con modifiche minime. Nella mia "base" classe js (si tratta di un oggetto che viene utilizzato in ogni pagina per allacciamenti dei pulsanti standard, dal nome della classe, ecc) ho aggiunto il seguente metodo che fa l'assegno per i gestori babbeo:

BindOnce: function(triggerName, fn) { 
    function handlerExists(triggerName, theHandler) { 
     function getFunctionName(fn) { 
      var rgx = /^function\s+([^\(\s]+)/ 
      var matches = rgx.exec(fn.toString()); 
      return matches ? matches[1] : "(anonymous)" 
     } 
     exists = false; 
     var handlerName = getFunctionName(theHandler); 
     if ($(document).data('events') !== undefined) { 
      var event = $(document).data('events')[triggerName]; 
      if (event !== undefined) { 
       $.each(event, function(i, handler) { 
        if (getFunctionName(handler) == handlerName) { 
         exists = true; 
        } 
       }); 
      } 
     } 
     return exists; 
    } 
    if (!handlerExists(triggerName, fn)) { 
     $(document).bind(triggerName, fn); 
    } 
}, 

Poi ho solo invocalo invece del metodo bind!

$.mystuff.BindOnce("TheTrigger", OnTriggered) 

Notare che non è possibile utilizzare i metodi anon qui come sarebbero solo essere chiamati 1, 2, 3, ecc e l'unico modo per verificare la presenza di gonzi sarebbe con un toString() sul metodo, che avrebbe essere piuttosto lento in un'applicazione complessa

+0

$ (document) .data ('eventi') non avrebbe funzionato per enumerare gli eventi collegati a un elemento a partire da jquery <= 1.8 – Agalo

8

Ummm che ne dici di utilizzare one()? http://api.jquery.com/one/

O ti sto completamente fraintendendo?

+2

Mi è passato per la testa, ma "uno" succederà solo una volta per ogni elemento associato e gestore iniettato nella pagina, e sono carina certo che quei conduttori dovrebbero * rimanere * legati mentre i suddetti elementi sono in vista. – karim79

+0

Non ero a conoscenza di questo metodo! molto utile. Anche se non sarà utile per molti esempi nel mio progetto. Il js è già caricato (e grande) e quindi caricarlo e slegarlo ogni volta potrebbe causare ulteriori problemi di memoria. Anche come karim79 suggerisce che molti degli handler hanno bisogno di rimanere legati. Grazie per avermi indicato in questa direzione, tuttavia, cercherò di utilizzarlo per i gestori di moduli sottoforma e vedere se subiamo perdite. –

1

Circa un centinaio di anni di ritardo, ma se non si sta usando funzioni anonime che si può fare questo molto più semplicemente utilizzando unbind prima:

$.fn.eventWillOnlySubscribeOnce = function() { 
    return this.each(function() { 
     var oneHandler = (function() { 
      HANDLER CODE 
     }); 

    $(this).unbind("submit", oneHandler); 
    $(this).bind("submit", oneHandler); 
}); 
}; 

Questa implementazione funzionerà in Firefox 4, ma non in molti altri browser - perché la variabile del gestore viene creata ogni volta di nuovo in modo tale che lo unbind non riesca a trovare un gestore corrispondente da non vincolare. Lo spostamento del gestore nel campo di applicazione genitore risolve il problema: vale a dire

var oneHandler = (function() { 
    HANDLER CODE 
}); 

$.fn.eventWillOnlySubscribeOnce = function() { 
    return this.each(function() { 
    $(this).unbind("submit", oneHandler); 
    $(this).bind("submit", oneHandler); 
}); 
}; 

Presumibilmente Firefox sta facendo un po 'di ottimizzazione intelligente che significa che il gestore è tenuto come una variabile costante da qualche parte in quanto non cambia mai.

48

Prevenire duplicare rilegatura utilizzando namespace evento di jQuery

realtà ci sono un paio di modi diversi di prevenire duplicato.Uno sta semplicemente passando il gestore originale nel gruppo unbind, MA se è una copia e non nello stesso spazio in memoria non si slegherà, l'altro modo popolare (usando i namespace) è un modo più sicuro per ottenere questo.

Questo è un problema comune con gli eventi. Quindi spiegherò un po 'sugli eventi di jQuery e usando lo spazio dei nomi per evitare collegamenti duplicati.



RISPOSTA: (Short e dritto al punto)


// bind handler normally 
$('#myElement').bind('myEvent', myMainHandler); 

// bind another using namespace 
$('#myElement').bind('myEvent.myNamespace', myUniqueHandler); 

// unbind the unique and rebind the unique 
$('#myElement').unbind('myEvent.myNamespace').bind('myEvent.myNamespace', myUniqueHandler); 
$('#myElement').bind('myEvent.myNamespace', myUniqueHandler); 

// trigger event 
$('#myElement').trigger('myEvent'); 

// output 
myMainHandler() // fires once! 
myUniqueHandler() // fires once! 



ESEMPIO DI RISPOSTA: (spiegazione dettagliata completa)


Per prima cosa creiamo un elemento di esempio da associare a. Useremo un pulsante con l'id di #button. Quindi crea 3 funzioni che possono essere utilizzate come handler per essere associate all'evento:

function exampleOne() ci legheremo con un clic. function exampleTwo() ci collegheremo a uno spazio dei nomi del clic. function exampleThree() ci collegheremo ad un namepsace del click, ma non ci impegneremo e ci legheremo più volte senza mai rimuovere gli altri binding che impediscono la duplicazione del binding mentre non rimuoviamo nessun altro dei metodi associati.

esempio Avviare: (Crea elemento di legarsi e alcuni metodi per essere i nostri gestori)

<button id="button">click me!</button> 


// create the example methods for our handlers 
function exampleOne(){ alert('One fired!'); } 
function exampleTwo(){ alert('Two fired!'); } 
function exampleThree(){ alert('Three fired!'); } 

Bind exampleOne di cliccare:

$('#button').bind('click', exampleOne); // bind example one to "click" 

Ora, se l'utente fa clic sul pulsante o chiama $ ('# button'). trigger ('click') riceverai l'avviso "One Fired!";

Bind exampleTwo ad uno spazio dei nomi di clic: "nome è arbitrario, useremo myNamespace2"

$('#button').bind('click.myNamespace2', exampleTwo); 

La cosa interessante di questo è, siamo in grado di innescare il "click" che il fuoco exampleOne () e exampleTwo(), oppure possiamo innescare "click.myNamespace2", che sarà solo il fuoco exampleTwo()

Bind exampleThree ad uno spazio dei nomi di clic: "ancora una volta, il nome è arbitrario finché è diverso dallo spazio dei nomi di exampleTwo , useremo myNamespace3 "

$('#button').bind('click.myNamespace3', exampleThree); 

Ora, se 'click' get del innescato tutti e tre i metodi di esempio avranno licenziato, o siamo in grado di indirizzare uno spazio dei nomi specifici.

mettere tutto insieme per prevenire DUPLICATO

Se dovessimo continuare a legare exampleThree() in questo modo:

$('#button').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree); 

che avrebbero ottenuto licenziato tre volte perché ogni volta che si chiama bind voi aggiungilo all'array degli eventi. Quindi, davvero semplice. Basta slegano per questo spazio dei nomi prima del legame, in questo modo:

$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree); 
$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree); 
$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree); 

Se viene attivata la funzione di scatto, exampleOne(), exampleTwo(), e exampleThree() solo una volta vengono licenziati.

Per avvolgere il tutto insieme in una semplice funzione:

var myClickBinding = function(jqEle, handler, namespace){ 
    if(namespace == undefined){ 
     jqEle.bind('click', handler); 
    }else{ 
     jqEle.unbind('click.'+namespace).bind('click.'+namespace, handler); 
    } 
} 

Sommario:

evento jQuery namespace consentono per il legame alla manifestazione principale, ma anche permettono namespace bambino per essere creati e cancellati senza creare spazi dei nomi fratelli o parentali che con un pensiero creativo molto minimale consente la prevenzione di associazioni duplicate.

Per ulteriori spiegazioni: http://api.jquery.com/event.namespace/

+0

Ottima risposta! Thx –

+3

Penso che ci sia un errore. Non associ e lega "click.myNamespace3" in una riga. Quindi nella riga successiva rileghi nuovamente "click.myNamespace3". Quindi, quando il visitatore fa clic sul pulsante, exampleThree() dovrebbe essere chiamato due volte, non una sola volta. Ecco la prova: http://jsfiddle.net/mpr79qm8/ – hswner

+0

Il namespace degli eventi funziona alla grande! Molto più semplice della codifica del proprio bus eventi personalizzato quando non hai bisogno o desideri qualcosa di così pesante o stai lavorando con eventi esterni che vengono attivati ​​a livello di finestra, come ad esempio con Flash e Actionscript. Dovrebbero davvero renderlo più facile da trovare nei documenti. – Throttlehead

18

Una risposta eccellente da

bind event only once

copyPastedInfo:

se è possibile applicarlo, probabilmente vuole dare un'occhiata a event.preventDefaultt e event.stopPropagation O separare e associare ogni volta, con il metodo

function someMethod() 
{ 
    $(obj).off('click').on('click', function {}); 
} 
+0

Questo è logico e un sollievo, grazie –

+0

funziona come un fascino. Grazie! –

Problemi correlati