Je souhaite implémenter un événement personnalisé pouvant être "diffusé" plutôt que d'être envoyé à des cibles spécifiques. Seuls les éléments qui se sont inscrits en tant qu'auditeurs de tels événements les recevront.
Ce que j'ai en tête ressemblerait à ce qui suit.
Premièrement, à divers endroits du code, il y aurait des déclarations de la forme
some_subscriber.on_signal( 'some_signal', some_handler );
J'utilise le terme signal
comme raccourci pour "événement de diffusion". Dans l'expression ci-dessus, some_subscriber
s'enregistre en tant qu'auditeur d'un type (appelé "un_signal") de ces signaux, en fournissant un gestionnaire pour celui-ci.
Ailleurs dans le code, il y aurait des déclarations de la forme
publisher.signal_types[ 'some_signal' ].broadcast( event_data );
Lorsque des instructions telles que celles-ci sont exécutées, un nouvel événement est généré et "broadcast". J'entends par là que le code qui appelle la méthode broadcast
ne dispose d'aucune information directe sur les écouteurs pour le signal émis.
J'ai implémenté un croquis de cette idée dans this jsFiddle , principalement pour illustrer ce que j'ai décrit avec les mots ci-dessus1. (Ce n'est certainement pas une qualité de production, et je ne suis pas particulièrement sûr que cela puisse être fait ainsi.)
Les éléments clés de cette implémentation sont les suivants. Tout d'abord, les objets éditeur ne gardent pas la trace de leurs abonnés}, comme on peut le voir dans l'implémentation d'une méthode fabrique pour un tel éditeur, comme indiqué ci-dessous:
function make_publisher ( signal_types ) {
// ...
var _
, signal = {}
, ping = function ( type ) {
signal[ type ].broadcast( ... );
}
;
signal_types.forEach( function ( type ) {
signal[ type ] = $.register_signal_type( type );
} );
return { signal_types: signal_types, ping: ping };
}
Cet objet éditeur n'expose que deux éléments: les types de signaux qu'il diffuse (en signal_types
) et une méthode ping
. Lorsque sa méthode ping
est invoquée, l'éditeur répond par broadcast un signal:
signal[ type ].broadcast( ... )
Les destinataires ultimes de cette émission n'apparaissent nulle part dans ce code.
Deuxièmement, ailleurs dans le code, les abonnés s'enregistrent comme auditeurs de ces signaux de diffusion, comme
$( some_selector ).on_signal( signal_type, some_handler );
Remarque: Il est fondamentalement impossible d’illustrer la logique de ce schéma à l’aide d’un exemple à la fois petit et réaliste. La raison en est que ce système présente l'avantage de prendre en charge un couplage très lâche entre le code de l'éditeur et le code de l'abonné. Il s'agit d'une fonctionnalité qui n'est jamais nécessaire dans un petit exemple. Au contraire, dans un petit exemple, le code qui implémente un tel couplage lâche apparaît invariablement comme étant inutilement complexe. Il est donc important de garder à l'esprit que cette apparente complexité excessive est un artefact du contexte. Le couplage lâche est très utile dans les grands projets. En particulier, le couplage lâche via un modèle de type éditeur/abonné est l’une des caractéristiques essentielles de MVC.
Ma question est la suivante: existe-t-il un moyen meilleur (ou du moins plus standard) d'obtenir cet effet de "diffusion" d'événements personnalisés?
(Je suis intéressé à la fois par les réponses basées sur jQuery et par les réponses "JS pur".)
1Une version antérieure et malheureuse de ce poste a été accueillie avec une incompréhension quasi universelle et (bien entendu) avec le vote à la baisse par trop typique. À une exception près, tous les commentaires que j'ai reçus remettaient en cause les prémisses mêmes de la publication et l'un d'eux remettait directement en question ma compréhension des bases de la programmation événementielle, etc. cela ne semblera pas aussi inconcevable que lorsque je l'ai décrit avec des mots seulement. Heureusement, le commentaire utile que j'ai eu sur ce message précédent m'a informé de la fonction jQuery.Callbacks
. C'était en effet un conseil utile; l'implémentation de l'esquisse mentionnée dans l'article est basée sur jQuery.Callbacks
.
D'accord.
Donc, je pense que ce que vous pouvez faire est d’utiliser les méthodes nativesdispatchEvent
etaddEventListener
et d’utiliserdocument
comme seul élément pour les deux {publication} _ et s'abonner à ces événements. Quelque chose comme:
var myCustomEvent = new Event('someEvent');
document.dispatchEvent(myCustomEvent);
...
document.addEventListener('someEvent', doSomething, false);
Et pour faire du multi-navigateur, vous pouvez:
var myCustomEvent = new Event('someEvent');
document.dispatchEvent(myCustomEvent);
...
if (document.addEventListener) {
document.addEventListener('someEvent', doSomething, false);
} else {
document.attachEvent('someEvent', doSomething);
}
Vous pouvez en savoir plus sur le sujet ici et ici . J'espère que cela t'aides.
Ma question est la suivante: existe-t-il un moyen meilleur (ou du moins plus standard) d’atteindre cet effet de "diffusion" d’événements personnalisés?
Non, il n’existe pas de méthode plus classique de publication/abonnement en Javascript. À ma connaissance, il n’est pas directement intégré au langage ni au navigateur. Il n’existe aucune norme de plate-forme pour cela.
Vous avez plusieurs options (la plupart dont vous semblez être conscient) pour assembler votre propre système.
Vous pouvez choisir un objet spécifique, tel que l'objet document
ou window
, ou un nouvel objet que vous créez, et utiliser les fonctions .on()
et .trigger()
de jQuery avec cet objet en tant que chambre de compensation centrale pour bricoler ensemble un modèle de type publication/abonnement. Vous pouvez même cacher l’existence de cet objet à votre utilisation réelle en le codant dans quelques fonctions d’utilité si vous le souhaitez.
Ou, comme vous semblez le savoir déjà, vous pouvez utiliser la fonctionnalité jQuery.Callbacks
. Il existe même un exemple de code de publication/abonnement dans jQuery doc .
Vous pouvez également trouver une bibliothèque tierce offrant un modèle de publication/abonnement quelque peu traditionnel.
Vous pouvez également créer votre propre système à partir de rien, ce qui implique simplement de conserver une liste des fonctions de rappel associées à un événement spécifique. Ainsi, lorsque cet événement est déclenché, vous pouvez appeler chaque fonction de rappel.
Si vous êtes venu ici à la recherche de la manière jQuery
de le faire, voici:
Ajouter la diffusion de l'événement/le code d'envoi:
Syntaxe:$(<element-name>).trigger(<event-name>);
.
Exemple:
$.ajax({
...
complete: function () {
// signal to registered listeners that event has occured
$(document).trigger("build_complete");
...
}
});
Enregistrer un auditeur pour l'événement:
Syntaxe:$(<element-name>).on(<event-name>, function() {...});
Exemple:
$(document).on("build_complete", function () {
NextTask.Init();
});
Remarque: si vous procédez ainsi: $(document).build_complete(function() {...});
conduit à une erreur: Uncaught TypeError: $(...).build_complete is not a function
.