Événements personnalisés dans jQuery?

180

Je recherche des informations sur la manière d'implémenter la gestion d'événements personnalisée dans jquery de la meilleure façon. Je sais comment connecter des événements à partir des éléments dom comme 'click', etc., mais je construis une minuscule bibliothèque / plugin javascript pour gérer certaines fonctionnalités de prévisualisation.

J'ai un script en cours d'exécution pour mettre à jour du texte dans un élément dom à partir d'un ensemble de règles et d'entrées de données / utilisateur que j'ai, mais maintenant j'ai besoin du même texte affiché dans d'autres éléments que ce script ne peut pas connaître. Ce dont j'ai besoin, c'est d'un bon modèle pour observer d'une manière ou d'une autre ce script produisant le texte nécessaire.

Alors comment faire ça? Ai-je négligé certaines fonctionnalités intégrées de jquery pour déclencher / gérer les événements utilisateur ou ai-je besoin d'un plugin jquery pour le faire? Selon vous, quel est le meilleur moyen / plugin pour gérer cela?

Pour Hornshøj-Schierbeck
la source
3
Il existe un autre cadre, knockoutjs.com qui peut en fait résoudre mon ancien problème, si quelqu'un qui regarde cette question en a besoin.
Per Hornshøj-Schierbeck
2
Voyant que je continue à me faire une réputation grâce à cette question, les gens doivent encore la lire. Je veux juste mettre à jour mon choix actuel de framework mvc côté client: angularjs.org
Per Hornshøj-Schierbeck
Même s'il est complètement différent de angular v1, Angular2 ( angular.io ) est tellement prometteur et sera mon premier choix une fois qu'il sera sorti de beta / rc.
Per Hornshøj-Schierbeck

Réponses:

105

Regarde ça:

(réimprimé de la page du blog expiré http://jamiethompson.co.uk/web/2008/06/17/publish-subscribe-with-jquery/ sur la base de la version archivée à http://web.archive.org/web /20130120010146/http://jamiethompson.co.uk/web/2008/06/17/publish-subscribe-with-jquery/ )


Publier / S'abonner avec jQuery

17 juin 2008

En vue d'écrire une interface utilisateur jQuery intégrée à la fonctionnalité hors ligne de Google Gears, j'ai joué avec du code pour interroger l'état de la connexion réseau à l'aide de jQuery.

L'objet de détection de réseau

Le principe de base est très simple. Nous créons une instance d'un objet de détection de réseau qui interrogera une URL à intervalles réguliers. Si ces requêtes HTTP échouent, nous pouvons supposer que la connectivité réseau a été perdue ou que le serveur est tout simplement inaccessible à l'heure actuelle.

$.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]);
            }
        });
    };
};

Vous pouvez voir la démo ici. Configurez votre navigateur pour qu'il fonctionne hors ligne et voyez ce qui se passe…. non, ce n'est pas très excitant.

Déclenchement et liaison

Ce qui est passionnant (ou du moins ce qui m'excite), c'est la méthode par laquelle le statut est relayé via l'application. Je suis tombé sur une méthode largement non discutée d'implémentation d'un système pub / sous en utilisant les méthodes de déclenchement et de liaison de jQuery.

Le code de démonstration est plus obtus qu'il ne devrait l'être. L'objet de détection de réseau publie les événements de «statut» dans le document qui les écoute activement et publie à son tour les événements de «notification» à tous les abonnés (nous en parlerons plus tard). Le raisonnement derrière cela est que dans une application du monde réel, il y aurait probablement plus de logique contrôlant quand et comment les événements de «notification» sont publiés.

$(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
    */
});

En raison de l'approche centrée DOM de jQuery, les événements sont publiés dans (déclenchés sur) les éléments DOM. Cela peut être la fenêtre ou l'objet de document pour les événements généraux ou vous pouvez générer un objet jQuery à l'aide d'un sélecteur. L'approche que j'ai adoptée avec la démo est de créer une approche presque en espace de noms pour définir les abonnés.

Les éléments DOM qui doivent être abonnés sont simplement classés par «abonné» et «détection de réseau». On peut alors publier des événements uniquement sur ces éléments (dont il n'y en a qu'un dans la démo) en déclenchant un événement de notification sur$(“.subscriber.networkDetection”)

Le #notifierdiv qui fait partie du .subscriber.networkDetectiongroupe d'abonnés a alors une fonction anonyme qui lui est liée, agissant effectivement en tant qu'auditeur.

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

Alors, voilà. Tout est assez verbeux et mon exemple n'est pas du tout passionnant. Cela ne présente pas non plus quelque chose d'intéressant que vous puissiez faire avec ces méthodes, mais si quelqu'un est intéressé à fouiller dans la source, n'hésitez pas. Tout le code est en ligne dans la tête de la page de démonstration

Vitor Silva
la source
Excatly ce que je cherchais. Il a probablement raison de dire que c'est la voie à suivre, mais je ne peux m'empêcher de penser que jquery a besoin d'un support direct ou d'un plugin pour le gérer plus gracieusement. J'attendrai un peu et voir s'il y a d'autres prises sur le problème avant de
signaler
6
Est-ce une citation de quelque part? Si tel est le cas, veuillez citer votre source et fournir un lien, s'il existe toujours.
Aryeh Leib Taurog
1
Oui, c'est une citation, dans l'historique des modifications se trouve ce lien , actuellement il est inaccessible. La page archivée est ici .
Stano
1
Ben Alman a écrit un minuscule (minuscule minuscule petit) sous-plugin jQuery pub . C'est extrêmement élégant.
Barney
128

Le lien fourni dans la réponse acceptée montre une bonne façon d'implémenter le système pub / sous en utilisant jQuery, mais j'ai trouvé le code un peu difficile à lire, voici donc ma version simplifiée du code:

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
la source
10
J'aime vraiment cette réponse. Tout le monde peut lire la documentation sur trigger () bind () (et on ()), mais cette réponse fournit un exemple concret de création d'un bus d'événements (pub / sous-système) en utilisant jquery.
lostdorje
1
Bravo. Tout ce que j'avais besoin de savoir.
Patrick Fisher
J'ai trouvé que si vous passez un tableau (où la longueur> 1) lorsque vous déclenchez un événement personnalisé, l'auditeur obtiendra tous les éléments du tableau dans ses arguments, vous vous retrouverez donc avec un nnombre d'arguments.
vsync
solution à la gestion des arguments multiples dans l'auditeur:var eventData = [].slice.call(arguments).splice(1)
vsync
2
Existe-t-il un moyen de faire cela sans avoir les éléments factices #notifier? Si j'avais une bibliothèque javascript et que je voulais un objet pour exposer un événement, je ne peux pas être sûr des éléments qui existent sur la page.
AaronLS
21

Je pense que oui ... il est possible de «lier» des événements personnalisés, comme (à partir 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" ]);
    });
Tuxifié
la source
1
Je cherche un moyen de déclencher un événement et de déclencher les abonnés / auditeurs. Ne le déclenchez pas manuellement car l'objet qui se lève ne sait pas qui écoute ...
Per Hornshøj-Schierbeck
2
Ce code fait cela, n'est-ce pas? myCustomEventest levée, la liaison en haut ajoute un auditeur.
Sprintstar
15

J'avais une question similaire, mais je cherchais en fait une réponse différente; Je cherche à créer un événement personnalisé. Par exemple au lieu de toujours dire ceci:

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

Je veux l'abréger en ceci:

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

trigger et bind ne racontent pas toute l'histoire - c'est un plugin JQuery. http://docs.jquery.com/Plugins/Authoring

La fonction "enterKey" est attachée en tant que propriété à jQuery.fn - c'est le code requis:

(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/

Une des particularités ci-dessus est que vous pouvez gérer la saisie au clavier avec élégance sur les écouteurs de lien comme:

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

Modifications: mise à jour pour transmettre correctement le bon thiscontexte au gestionnaire et pour renvoyer toute valeur de retour du gestionnaire à jQuery (par exemple, au cas où vous cherchiez à annuler l'événement et à faire des bulles). Mis à jour pour transmettre un objet d'événement jQuery approprié aux gestionnaires, y compris le code de clé et la possibilité d'annuler l'événement.

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

Chris Moschini
la source
Chris - La fonction a parfaitement fonctionné .. la seule chose qui ne fonctionne pas est d'accéder à cette variable .. je suis capable d'y accéder en utilisant e.currentTarget .. mais je me demandais s'il existe un moyen plus efficace.
Addy le
Bonne prise sur ce bug. Oui, si vous changez la ligne 5 du gestionnaire (ev); à: handler.call (this, ev); alors cet argument sera comme il est normalement dans les gestionnaires d'événements jquery standard. jsfiddle.net/b9chris/VwEb9
Chris Moschini
Connaissez-vous également un moyen de mettre en œuvre cela dans l'autre sens? J'essaye d'utiliser cet exemple mais avec scroll, qui ne se propage pas. Je pense qu'au lieu d'avoir les auditeurs codés en dur sur le corps, j'ai besoin des éléments qui ont le on-event lié à eux pour déclencher l'événement eux-mêmes.
Gust van de Wal
Gust Je ne suis pas sûr du scénario dont vous parlez; serait-il préférable de poser une question avec un exemple de code?
Chris Moschini
9

C'est un ancien message, mais je vais essayer de le mettre à jour avec une nouvelle information.

Pour utiliser des événements personnalisés, vous devez le lier à un élément DOM et le déclencher. Vous devez donc utiliser

La méthode .on () prend un type d'événement et une fonction de gestion d'événement comme arguments. En option, il peut également recevoir des données liées aux événements comme deuxième argument, poussant la fonction de gestion des événements vers le troisième argument. Toutes les données transmises seront disponibles pour la fonction de gestion des événements dans la propriété data de l'objet événement. La fonction de gestion d'événements reçoit toujours l'objet événement comme premier argument.

et

La méthode .trigger () prend un type d'événement comme argument. En option, il peut également prendre un tableau de valeurs. Ces valeurs seront transmises à la fonction de gestion d'événements en tant qu'arguments après l'objet événement.

Le code ressemble à ceci:

$(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"] );

Une belle explication peut être trouvée ici et ici . Pourquoi exactement cela peut-il être utile? J'ai trouvé comment l'utiliser dans cette excellente explication de l' ingénieur Twitter .

PS En javascript simple, vous pouvez le faire avec le nouveau CustomEvent , mais méfiez-vous des problèmes d'IE et de Safari.

Salvador Dali
la source
5

Voici comment je crée des événements personnalisés:

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

Certes, vous pouvez simplement faire

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

Mais la façon dont j'ai écrit vous permet de détecter si l'utilisateur a empêché le défaut ou non en faisant

var prevented = event.isDefaultPrevented();

Cela vous permet d'écouter la demande de votre utilisateur final d'arrêter de traiter un événement particulier, par exemple si vous cliquez sur un élément de bouton dans un formulaire mais que vous ne souhaitez pas que le formulaire soit publié en cas d'erreur.


J'écoute généralement les événements comme ça

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

Encore une fois, tu pourrais juste faire

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

Mais j'ai toujours trouvé cela un peu dangereux, surtout si vous travaillez en équipe.

Il n'y a rien de mal à ce qui suit:

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

Cependant, supposons que je doive dissocier cet événement pour une raison quelconque (imaginez un bouton désactivé). Je devrais alors faire

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

Cela supprimera tous les eventnameévénements de $(element). Vous ne pouvez pas savoir si quelqu'un dans le futur liera également un événement à cet élément, et vous dissocieriez également cet événement par inadvertance.

Le moyen le plus sûr d'éviter cela est d'espacer les noms de vos événements en faisant

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

Enfin, vous avez peut-être remarqué que la première ligne était

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

Je personnellement toujours unbind un événement avant de se lier juste pour vous assurer que le même gestionnaire d'événements ne fait jamais appelé plusieurs fois (imaginez ce fut le bouton soumettre un formulaire de paiement et l'événement avait été lié 5 fois)

Luke Madhanga
la source
belle approche. +1 pour l'espace de noms.
Aurovrata