2010-02-01 13 views
29

Una vez más quiero cargar una página que contiene su propio script en un div usando $ ("divid"). Load (...). El problema que enfrento está relacionado con los eventos. Digamos que disparamos ("mono") desde la página principal y en la página cargada vinculamos ("mono") y hacemos una alerta ("mono obligado"). Si se llama al mismo método de carga varias veces, el enlace se llama varias veces. Ahora podría desvincularlo antes de vincularlo, o verificar el número de manejadores antes del enlace y luego no vincularlo para evitar esto. Ninguna de las opciones es escalable, como qué pasa si más adelante quiero vincularme a ese desencadenador en otra "página secundaria" (una página cargada en un div).Modelo de evento JQuery y prevención de manipuladores duplicados

Lo que idealmente quiero hacer es verificar si el controlador que voy a agregar ya existe, pero aún así QUIERO usar manejadores anónimos ... (preguntando un poco sobre esa última solicitud, creo). Actualmente tengo una solución mediante el uso de métodos predefinidos/nombrados y luego verificar esto antes del enlace.

// 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; 
    } 

Esta es una forma bastante burda de hacerlo, pero parece funcionar. Acabo de hacer lo siguiente antes del enlace de la siguiente manera:

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

¿Alguien tiene una solución más elegante? por ejemplo, ¿hay alguna forma de verificar que un script en particular esté cargado? así que podría usar getScript() para cargar el js desde la página secundaria en la primera carga, y luego simplemente no cargarlo en cargas posteriores (y solo disparar un disparador que sería manejado por el js preexistente) ..

+0

posible duplicado de [jQuery impedir el funcionamiento duplicado asignado] (http://stackoverflow.com/questions/1558377/jquery-prevent-duplicate-function-assigned) –

Respuesta

7

Esto no es una respuesta completa, pero rápidamente encontré cómo arreglar todo mi código con cambios mínimos. En mi "base" clase js (esto es un objeto que se utiliza en todas las páginas de conexiones de botones estándar por el nombre de la clase, etc.) he añadido el siguiente método que realiza la verificación de los manipuladores de duplicados:

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); 
    } 
}, 

Entonces sólo invocarlo en lugar del método bind!

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

Tenga en cuenta que no puede utilizar métodos anon aquí, ya que sólo serían llamados 1, 2, 3, etc, y la única manera de comprobar si hay duplicados estarían con un método toString() en el método, que haría ser bastante lento en una aplicación compleja

+0

$ (document) .data ('eventos') no funcionaría para enumerar los eventos asociados a un elemento a partir de jquery <= 1.8 – Agalo

8

Ummm ¿qué hay de usar one()? http://api.jquery.com/one/

¿O estoy completamente mal entendido?

+2

También se me pasó por la mente, pero 'uno' solo ocurrirá una vez por cada elemento vinculado y controlador inyectado en la página, y soy bastante Asegúrese de que esos manejadores deben * permanecer * atado mientras dichos elementos están a la vista. – karim79

+0

¡No tenía conocimiento de este método! muy útil. Aunque no será útil para muchos ejemplos en mi proyecto. El js ya está cargado (y es grande), por lo que cargarlo y quitarlo cada vez puede causar más problemas de memoria. También como karim79 sugiere que muchos de los manejadores necesitan permanecer atados. Sin embargo, gracias por apuntarme en esta dirección, voy a intentar usar eso para los manipuladores cargados sub formulario y ver si sufrimos fugas. –

1

unos cien años demasiado tarde, pero si usted no está utilizando funciones anónimas se puede hacer esto mucho más simplemente mediante el uso de desvinculación primera:

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

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

Esta aplicación funcionará en Firefox 4, pero no en muchos otros navegadores, porque la variable controlador se crea nueva cada vez, por lo que la desvinculación no puede encontrar un controlador coincidente para desvincular. Al mover el controlador en el ámbito padre corrige el problema: es decir,

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

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

Es de suponer que Firefox está haciendo una optimización inteligente que significa que el controlador se lleva a cabo como una variable constante en alguna parte, ya que nunca cambia.

48

Evitar duplicar unión using namespace caso de jQuery

En realidad, hay un par de maneras diferentes de prevenir duplicado.Uno solo está pasando el manejador original al desvincularlo, PERO si es una copia y no está en el mismo espacio en la memoria no se desvinculará, la otra forma popular (usar espacios de nombres) es una manera más segura de lograr esto.

Este es un problema común con los eventos. Así que explicaré un poco sobre los eventos de jQuery y el uso del espacio de nombres para evitar enlaces duplicados.



RESPUESTA: (Short y directamente 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! 



EJEMPLO DE RESPUESTA: (explicación completa detallada)


Primero creemos un elemento de ejemplo para enlazar. Usaremos un botón con el id de #button. Luego haga 3 funciones que pueden y serán usadas como manejadores para vincularse al evento:

function example One() uniremos con un clic. function exampleTwo() nos uniremos a un espacio de nombre del clic. function exampleThree() enlazaremos a una instancia del clic, pero desvincularemos y enlazaremos varias veces sin quitar los otros enlaces, lo que impide duplicar el enlace mientras no se elimina ningún otro de los métodos enlazados.

Ejemplo de inicio: (Crear elemento de unirse y algunos métodos para que sean nuestros manipuladores)

<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!'); } 

Enlazar exampleOne hacer clic en:

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

Ahora bien, si el usuario hace clic en el botón o llame a $ ('# button'). trigger ('click') recibirá la alerta "One Fired!";

Enlazar exampleTwo a un espacio de nombres de clic: "nombre es arbitrario, utilizaremos myNamespace2"

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

Lo bueno de esto es, podemos desencadenar el "clic", que se disparará exampleOne () Y exampleTwo(), o podemos desencadenar "click.myNamespace2", que sólo se disparará exampleTwo()

Enlazar exampleThree a un espacio de nombres de clic: "de nuevo, el nombre es arbitrario, siempre y cuando sea diferente del espacio de nombres de exampleTwo , usaremos myNamespace3 "

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

Ahora bien, si un 'clic' de obtener desencadenó los tres métodos de ejemplo será despedida, o pueden dirigirse a un espacio de nombres específico.

ponerlo todo en conjunto para prevenir DUPLICADO

Si tuviéramos que siga siendo obligatorio para exampleThree(), así:

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

que obtendrían disparó tres veces, porque cada vez que llame lo obligarán agréguelo a la matriz de eventos. Entonces, realmente simple. Sólo desatan durante ese espacio de nombres antes de la unión, así:

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

Si se activa la función de clic, exampleOne(), exampleTwo(), y exampleThree() sólo una vez despedidos.

Para envolver todo junto en una función simple:

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

Resumen:

evento jQuery espacios de nombres permiten la unión al evento principal, pero también permiten espacios de nombres del niño a ser creados y despejaron sin afectar espacios de nombres hermanos o padres que con un pensamiento creativo muy mínimo permite la prevención de enlaces duplicados.

Para una explicación más detallada: http://api.jquery.com/event.namespace/

+0

¡Gran respuesta! Thx –

+3

Creo que hay un error. Desvincula y vincula 'click.myNamespace3' en una línea. Luego, en la siguiente línea, vuelve a enlazar 'click.myNamespace3'. Entonces, cuando el visitante hace clic en el botón, se debe llamar a exampleThree() dos veces, no una vez. Aquí está la prueba: http://jsfiddle.net/mpr79qm8/ – hswner

+0

¡El espaciado de nombres funciona muy bien! Mucho más fácil que programar tu propio bus de eventos personalizado cuando no necesitas o quieres algo pesado o estás trabajando con eventos externos que se activan en el nivel de la ventana, como con Flash y Actionscript. Realmente deberían hacer esto más fácil de encontrar en los documentos. – Throttlehead

18

Una excelente respuesta de

bind event only once

copyPastedInfo:

si se puede aplicar, probablemente desee echar un vistazo a event.preventDefaultt y event.stopPropagation O desvincular y enlazar cada vez, dentro del método como

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

Esto es lógico y un alivio, gracias –

+0

Funciona como un encanto. ¡Gracias! –

Cuestiones relacionadas