¿Eventos personalizados en jQuery?

180

Estoy buscando información sobre cómo implementar el manejo de eventos personalizado en jquery de la mejor manera. Sé cómo conectar eventos de los elementos dom como 'hacer clic', etc., pero estoy construyendo una pequeña biblioteca / complemento javascript para manejar algunas funciones de vista previa.

Tengo un script en ejecución para actualizar parte del texto en un elemento dom de un conjunto de reglas y datos / entrada del usuario que obtuve, pero ahora necesito ese mismo texto que se muestra en otros elementos que este script no puede conocer. Lo que necesito es un buen patrón para observar de alguna manera este script que produce el texto necesario.

Entonces, ¿cómo hago esto? ¿Pasé por alto alguna funcionalidad incorporada en jquery para generar / manejar eventos de usuario o necesito algún complemento de jquery para hacerlo? ¿Cuál crees que es la mejor forma / complemento para manejar esto?

Per Hornshøj-Schierbeck
fuente
3
Existe otro marco, knockoutjs.com , que realmente puede resolver mi antiguo problema, si alguien que mira esta pregunta lo necesita.
Per Hornshøj-Schierbeck
2
Al ver que sigo obteniendo reputación de esta pregunta, la gente todavía debe estar leyéndola. Solo quiero actualizar mi elección actual del marco mvc del
Per Hornshøj-Schierbeck
Aunque es completamente diferente de angular v1, Angular2 ( angular.io ) es muy prometedor y será mi primera elección una vez que esté fuera de beta / rc.
Per Hornshøj-Schierbeck

Respuestas:

105

Mira esto:

(reimpreso de la página caducada del blog http://jamiethompson.co.uk/web/2008/06/17/publish-subscribe-with-jquery/ basado en la versión archivada en http://web.archive.org/web /20130120010146/http://jamiethompson.co.uk/web/2008/06/17/publish-subscribe-with-jquery/ )


Publicar / Suscribirse con jQuery

17 de junio de 2008

Con miras a escribir una interfaz de usuario jQuery integrada con la funcionalidad fuera de línea de Google Gears, he estado jugando con algún código para sondear el estado de la conexión de red usando jQuery.

El objeto de detección de red

La premisa básica es muy simple. Creamos una instancia de un objeto de detección de red que sondeará una URL a intervalos regulares. Si estas solicitudes HTTP fallan, podemos suponer que se ha perdido la conectividad de la red o que el servidor simplemente no está disponible en este momento.

$.networkDetection = function(url,interval){
    var url = url;
    var interval = interval;
    online = false;
    this.StartPolling = function(){
        this.StopPolling();
        this.timer = setInterval(poll, interval);
    };
    this.StopPolling = function(){
        clearInterval(this.timer);
    };
    this.setPollInterval= function(i) {
        interval = i;
    };
    this.getOnlineStatus = function(){
        return online;
    };
    function poll() {
        $.ajax({
            type: "POST",
            url: url,
            dataType: "text",
            error: function(){
                online = false;
                $(document).trigger('status.networkDetection',[false]);
            },
            success: function(){
                online = true;
                $(document).trigger('status.networkDetection',[true]);
            }
        });
    };
};

Puedes ver la demostración aquí. Configure su navegador para que funcione sin conexión y vea qué sucede ... No, no es muy emocionante.

Disparador y enlace

Sin embargo, lo que es emocionante (o al menos lo que me emociona) es el método por el cual el estado se transmite a través de la aplicación. Me topé con un método en gran parte no discutido para implementar un sistema pub / sub utilizando los métodos de activación y vinculación de jQuery.

El código de demostración es más obtuso de lo necesario. El objeto de detección de red publica eventos de 'estado' en el documento que los escucha activamente y, a su vez, publica eventos de 'notificación' a todos los suscriptores (más sobre eso más adelante). El razonamiento detrás de esto es que en una aplicación del mundo real probablemente habría algo más de control lógico cuándo y cómo se publican los eventos de 'notificación'.

$(document).bind("status.networkDetection", function(e, status){
    // subscribers can be namespaced with multiple classes
    subscribers = $('.subscriber.networkDetection');
    // publish notify.networkDetection even to subscribers
    subscribers.trigger("notify.networkDetection", [status])
    /*
    other logic based on network connectivity could go here
    use google gears offline storage etc
    maybe trigger some other events
    */
});

Debido al enfoque centrado en DOM de jQuery, los eventos se publican en elementos DOM (activados). Este puede ser el objeto de ventana u documento para eventos generales o puede generar un objeto jQuery usando un selector. El enfoque que he tomado con la demostración es crear un enfoque casi espaciado de nombres para definir suscriptores.

Los elementos DOM que serán suscriptores se clasifican simplemente con "suscriptor" y "networkDetection". Entonces podemos publicar eventos solo en estos elementos (de los cuales solo hay uno en la demostración) activando un evento de notificación en$(“.subscriber.networkDetection”)

El #notifierdiv, que forma parte del .subscriber.networkDetectiongrupo de suscriptores, tiene una función anónima vinculada a él, actuando efectivamente como un oyente.

$('#notifier').bind("notify.networkDetection",function(e, online){
    // the following simply demonstrates
    notifier = $(this);
    if(online){
        if (!notifier.hasClass("online")){
            $(this)
                .addClass("online")
                .removeClass("offline")
                .text("ONLINE");
        }
    }else{
        if (!notifier.hasClass("offline")){
            $(this)
                .addClass("offline")
                .removeClass("online")
                .text("OFFLINE");
        }
    };
});

Ahí vas. Todo es bastante detallado y mi ejemplo no es nada emocionante. Tampoco muestra nada interesante que pueda hacer con estos métodos, pero si alguien está interesado en profundizar en la fuente, siéntase libre. Todo el código está en línea en la cabecera de la página de demostración.

Vitor Silva
fuente
Excatly lo que estaba buscando. Probablemente tenga razón en que es el camino a seguir, pero no puedo evitar pensar que jquery necesita soporte directo o un complemento para manejarlo con más gracia. Esperaré un poco y veré si hay otras opiniones sobre el problema antes de marcar una respuesta :)
Per Hornshøj-Schierbeck
66
¿Es esta una cita de alguna parte? Si es así, cite su fuente y proporcione un enlace, si aún existe.
Aryeh Leib Taurog
1
Sí, es una cita, en el historial de edición está este enlace , actualmente es inaccesible. La página archivada está aquí .
Stano
1
Ben Alman escribió un pequeño plugin jQuery pub sub pequeño . Es extremadamente elegante
Barney
127

El enlace proporcionado en la respuesta aceptada muestra una buena manera de implementar el sistema pub / sub usando jQuery, pero encontré el código algo difícil de leer, así que aquí está mi versión simplificada del código:

http://jsfiddle.net/tFw89/5/

$(document).on('testEvent', function(e, eventInfo) { 
  subscribers = $('.subscribers-testEvent');
  subscribers.trigger('testEventHandler', [eventInfo]);
});

$('#myButton').on('click', function() {
  $(document).trigger('testEvent', [1011]);
});

$('#notifier1').on('testEventHandler', function(e, eventInfo) { 
  alert('(notifier1)The value of eventInfo is: ' + eventInfo);
});

$('#notifier2').on('testEventHandler', function(e, eventInfo) { 
  alert('(notifier2)The value of eventInfo is: ' + eventInfo);
});
Manuel Navarro
fuente
10
Realmente me gusta esta respuesta. Cualquiera puede leer los documentos en trigger () bind () (y on ()), pero esta respuesta proporciona un ejemplo concreto de crear un bus de eventos (pub / subsistema) usando jquery.
lostdorje
1
Bravo. Todo lo que necesitaba saber.
Patrick Fisher
He descubierto que si pasa una matriz (donde longitud> 1) cuando activa un evento personalizado, el oyente obtendrá todos los elementos de la matriz en sus argumentos, por lo que terminará con una ncantidad de argumentos.
vsync
solución a los múltiples argumentos de manipulación en el oyente:var eventData = [].slice.call(arguments).splice(1)
VSYNC
2
¿Hay alguna manera de hacer esto sin tener los elementos ficticios #notifier? Si tuviera una biblioteca de JavaScript y quisiera un objeto para exponer un evento, no puedo estar seguro de qué elementos existen en la página.
AaronLS
21

Creo que sí ... es posible 'vincular' eventos personalizados, como (de: http://docs.jquery.com/Events/bind#typedatafn ):

 $("p").bind("myCustomEvent", function(e, myName, myValue){
      $(this).text(myName + ", hi there!");
      $("span").stop().css("opacity", 1)
               .text("myName = " + myName)
               .fadeIn(30).fadeOut(1000);
    });
    $("button").click(function () {
      $("p").trigger("myCustomEvent", [ "John" ]);
    });
Tuxified
fuente
1
Estoy buscando una forma de generar un evento y que los suscriptores / oyentes se activen. No activarlo manualmente ya que el objeto que levanta no sabe quién está escuchando ...
Per Hornshøj-Schierbeck
2
Este código hace esto, ¿no? myCustomEventse levanta, el enlace en la parte superior agrega un oyente.
Sprintstar
15

Tenía una pregunta similar, pero en realidad estaba buscando una respuesta diferente; Estoy buscando crear un evento personalizado. Por ejemplo, en lugar de decir siempre esto:

$('#myInput').keydown(function(ev) {
    if (ev.which == 13) {
        ev.preventDefault();
        // Do some stuff that handles the enter key
    }
});

Quiero abreviarlo a esto:

$('#myInput').enterKey(function() {
    // Do some stuff that handles the enter key
});

desencadenar y vincular no cuentan toda la historia: este es un complemento de JQuery. http://docs.jquery.com/Plugins/Authoring

La función "enterKey" se adjunta como una propiedad a jQuery.fn - este es el código requerido:

(function($){
    $('body').on('keydown', 'input', function(ev) {
        if (ev.which == 13) {
            var enterEv = $.extend({}, ev, { type: 'enterKey' });
            return $(ev.target).trigger(enterEv);
        }
    });

    $.fn.enterKey = function(selector, data, fn) {
        return this.on('enterKey', selector, data, fn);
    };
})(jQuery);

http://jsfiddle.net/b9chris/CkvuJ/4/

Una característica de lo anterior es que puede manejar la entrada del teclado con gracia en escuchas de enlaces como:

$('a.button').on('click enterKey', function(ev) {
    ev.preventDefault();
    ...
});

Ediciones: actualizado para pasar correctamente el thiscontexto correcto al controlador y para devolver cualquier valor de retorno del controlador a jQuery (por ejemplo, en caso de que estuviera buscando cancelar el evento y el burbujeo). Actualizado para pasar un objeto de evento jQuery adecuado a los controladores, incluido el código clave y la capacidad de cancelar el evento.

Viejo jsfiddle: http://jsfiddle.net/b9chris/VwEb9/24/

Chris Moschini
fuente
Chris: la función funcionó perfectamente ... lo único que no funciona es acceder a esta variable ... puedo acceder a ella usando e.currentTarget ... pero me preguntaba si hay una manera más eficiente.
Addy
Buena captura de ese error. Sí, si cambia la línea 5 del controlador (ev); a: handler.call (esto, ev); entonces el argumento this será como normalmente es en los controladores de eventos jquery estándar. jsfiddle.net/b9chris/VwEb9
Chris Moschini
¿Conoces también una forma de implementar esto al revés? Estoy tratando de usar este ejemplo pero con scroll, que no se propaga. Creo que en lugar de tener a los oyentes codificados en el cuerpo, necesito los elementos que tienen el onevento vinculado a ellos para disparar el evento ellos mismos.
Gust van de Wal
No estoy seguro del escenario sobre el que preguntas; podría hacerse mejor como una pregunta con código de muestra?
Chris Moschini
9

Es una publicación antigua, pero intentaré actualizarla con nueva información.

Para usar eventos personalizados, debe vincularlo a algún elemento DOM y activarlo. Entonces necesitas usar

El método .on () toma un tipo de evento y una función de manejo de eventos como argumentos. Opcionalmente, también puede recibir datos relacionados con eventos como su segundo argumento, empujando la función de manejo de eventos al tercer argumento. Cualquier dato que se pase estará disponible para la función de manejo de eventos en la propiedad de datos del objeto de evento. La función de manejo de eventos siempre recibe el objeto de evento como su primer argumento.

y

El método .trigger () toma un tipo de evento como argumento. Opcionalmente, también puede tomar una matriz de valores. Estos valores se pasarán a la función de manejo de eventos como argumentos después del objeto de evento.

El código se ve así:

$(document).on("getMsg", {
    msg: "Hello to everyone",
    time: new Date()
}, function(e, param) {
    console.log( e.data.msg );
    console.log( e.data.time );
    console.log( param );
});

$( document ).trigger("getMsg", [ "Hello guys"] );

Una buena explicación se puede encontrar aquí y aquí . ¿Por qué exactamente esto puede ser útil? Encontré cómo usarlo en esta excelente explicación del ingeniero de Twitter .

PD: en javascript simple puedes hacer esto con el nuevo CustomEvent , pero ten cuidado con los problemas de IE y Safari.

Salvador Dalí
fuente
3

Así es como autorizo ​​eventos personalizados:

var event = jQuery.Event('customEventName');
$(element).trigger(event);

De acuerdo, simplemente podrías hacer

$(element).trigger('eventname');

Pero la forma en que escribí le permite detectar si el usuario ha evitado el incumplimiento o no al hacer

var prevented = event.isDefaultPrevented();

Esto le permite escuchar la solicitud de su usuario final para detener el procesamiento de un evento en particular, como si hace clic en un elemento de botón en un formulario pero no desea que el formulario se publique si hay un error.


Entonces suelo escuchar eventos como ese

$(element).off('eventname.namespace').on('eventname.namespace', function () {
    ...
});

Una vez más, podrías simplemente hacer

$(element).on('eventname', function () {
    ...
});

Pero siempre he encontrado esto algo inseguro, especialmente si estás trabajando en un equipo.

No hay nada malo con lo siguiente:

$(element).on('eventname', function () {});

Sin embargo, suponga que necesito desvincular este evento por cualquier razón (imagine un botón deshabilitado). Entonces tendría que hacer

$(element).off('eventname', function () {});

Esto eliminará todos los eventnameeventos de $(element). No puede saber si alguien en el futuro también vinculará un evento a ese elemento, y también desunirá involuntariamente ese evento

La forma segura de evitar esto es crear espacios de nombres en sus eventos haciendo

$(element).on('eventname.namespace', function () {});

Por último, es posible que haya notado que la primera línea era

$(element).off('eventname.namespace').on('eventname.namespace', ...)

Yo personalmente siempre desatar un evento antes de que la unión sólo para asegurarse de que el mismo controlador de eventos nunca se llama varias veces (imaginar que este era el botón de enviar en un formulario de pago y el evento había sido atado 5 veces)

Luke Madhanga
fuente
Buen enfoque. +1 para espacio de nombres.
Aurovrata