I want to implement a custom event that can be "broadcast", rather than sent to specific targets. Only those elements that have registered themselves as listeners for such events will receive them.
What I have in mind would look as follows.
First, in various places of the code, there would be statements of the form
some_subscriber.on_signal( 'some_signal', some_handler );
I'm using the term signal as shorthand for "broadcast event". In the expression above, some_subscriber registers itself as a listener of one type (called 'some_signal') of such signals, by providing a handler for it.
Elsewhere in the code, there would be statements of the form
publisher.signal_types[ 'some_signal' ].broadcast( event_data );
When statements like these get executed, a new event is generated and "broadcast". By this I mean that the code that calls the broadcast method has no direct information about the listeners for the signal it is issuing.
I have implemented a sketch of this idea in this jsFiddle, mostly in order to illustrate what I described in words above1. (It's certainly not production-grade, and I'm not particularly confident that it could be made so.)
The key elements of this implementation are the following. First, publisher objects do not keep track of their subscribers, as can be seen in the implementation of a factory method for such a publisher, shown below:
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 };
}
This publisher object exposes only two items: the types of signals it broadcasts (in signal_types), and a ping method. When its ping method is invoked, the publisher responds by broadcasting a signal:
signal[ type ].broadcast( ... )
The ultimate recipients of this broadcast are nowhere to be seen in this code.
Second, elsewhere in the code, subscribers register themselves as listeners of these broadcast signals, like so
$( some_selector ).on_signal( signal_type, some_handler );
Note: It is basically impossible to illustrate the rationale for this scheme using an example that is both small and realistic. The reason for this is that the strength of this scheme is that it supports very loose coupling between the publisher code and subscriber code, and this is a feature that is never necessary in a small example. On the contrary, in a small example, code that implements such loose coupling invariably comes across as unnecessarily complex. It is therefore important to keep in mind that this apparent excess complexity is an artifact of the context. Loose coupling is very useful in larger projects. In particular, loose coupling via a publisher/subscriber-type pattern is one of the essential features of MVC.
My question is: is there a better (or at least more standard) way to achieve this effect of "broadcasting" custom events?
(I'm interested in both jQuery-based answers as well as "pure JS" ones.)
1An earlier, ill-fated version of this post was met with almost universal incomprehension, and (of course) the all-too-typical down-voting. With one exception, all the comments I got challenged the very premises of the post, and one directly questioned my grasp of the basics of event-driven programming, etc. I'm hoping that by presenting a working example of what I mean at least it won't come across as utterly inconceivable as it did when I described it in words alone. Luckily, the one helpful comment I did get on that earlier post informed me of the function jQuery.Callbacks. This was indeed a useful tip; the sketch implementation mentioned in the post is based on jQuery.Callbacks.
All right.
So I think what you can do is use the native dispatchEvent and addEventListener methods and use document as the only element for both publishing and subscribing to those events. Something like:
var myCustomEvent = new Event('someEvent');
document.dispatchEvent(myCustomEvent);
...
document.addEventListener('someEvent', doSomething, false);
And to make cross-browser, you could:
var myCustomEvent = new Event('someEvent');
document.dispatchEvent(myCustomEvent);
...
if (document.addEventListener) {
document.addEventListener('someEvent', doSomething, false);
} else {
document.attachEvent('someEvent', doSomething);
}
You can read more on the subject here and here. Hope this helps.
My question is: is there a better (or at least more standard) way to
achieve this effect of "broadcasting" custom events?
No, there is not a more standard way of doing publish/subscribe in Javascript. It is not directly built into the language or the browser and there are no platform standards for it that I'm aware of.
You have several options (most of which you seem aware of) to put your own system together.
You could pick a specific object such as the document object or the window object or a new object you create and use jQuery's .on() and .trigger() with that object as a central clearing house to cobble together a publish/subscribe-like model. You could even hide the existence of that object from your actual use by just coding it into a few utility functions if you want.
Or, as you seem to already know, you could use the jQuery.Callbacks functionality. There's even publish/subscribe sample code in the jQuery doc.
Or, you can find a third party library that offers a somewhat traditional publish/subscribe model.
Or, you can build your own from scratch which really just involves keeping a list of callback functions that are associated with a specific event so when that event is triggered, you can call each callback function.
If you came here looking for the jQuery way of doing this, here you go:
Add the event broadcast/dispatch code:
Syntax:
$(<element-name>).trigger(<event-name>);.
Example:
$.ajax({
...
complete: function () {
// signal to registered listeners that event has occured
$(document).trigger("build_complete");
...
}
});
Register a listener for the event:
Syntax:
$(<element-name>).on(<event-name>, function() {...});
Example:
$(document).on("build_complete", function () {
NextTask.Init();
});
Note:
Doing it this way: $(document).build_complete(function() {...}); leads to an error: Uncaught TypeError: $(...).build_complete is not a function.
I know this has been marked as answered back in 2015 -- but a solution that is also elegant and simple could be to use Redux
Related
I have been following the Modular Design Pattern for quite some time now and find it extremely useful as it helps in the well maintenance of code & separation of blocks into modules.
Regular usage of the module structure with jQuery has led to most of my applications/code following the below structure:
(function() {
var chat = {
websocket: new WebSocket("ws://echo.websocket.org/"),
that: this,
init: function() {
this.scrollToBottom();
this.bindEvents();
this.webSocketHandlers();
},
bindEvents: function() {
this.toggleChat();
this.filterPeople();
this.compose();
},
elements: {
indicator: $(".indicator"),
statusText: $(".status-text"),
chatHeadNames: $(".people li .name"),
filterInput: $("#filter-input"),
msgInput: $("#msg-input"),
sendBtn: $(".send")
},
...
...
...
filterPeople: function() {
var that = this;
this.elements.chatHeadNames.each(function() {
$(this).attr('data-search-term', $(this).text().toLowerCase());
});
},
...
...
};
chat.init();
})();
What I would like to know is whether referencing all my elements via jQuery as part of a single variable chat.elements is a good practice?
One part of me tells that it indeed is a good way to reference all your selectors at once and cache them in variables so that multiple usages of the same element can be done with the cached variables (instead of multiple DOM selections).
Another part of me tells that this might be an anti-pattern and specific elements should be selected and cached locally when required.
I have used similar structures throughout and have got mixed responses about the code, but nothing solid. Any help would be appreciated. Thanks!
Caching the selectors is a good thing. Hanging on to them is a good idea. It improves performance over repeatedly querying the DOM for the same thing. The code you have above looks very similar to BackboneJS and MarionetteJS code.
I do have some warnings for you though:
This pattern could cause memory leaks. Consider the case where you destory a subview, but you keep a reference to something that selected it. This is called a dangling pointer. The view won't really disappear. All bindings will remain. Events will continue to fire behind the scenes.
You will eventually run into a bug where you decided to re-render part of your screen. Cleaning up all your bindings is then required and you need to remember to delete and selectors. If you don't do this you will almost certainly run into issues where you wonder why an event is indeed firing but nothing appears to happen on screen.... (this will be because its happening off screen, to the element that you tried to delete, that still exists... sorta).
The current way you are querying for elements causes searches against the entire page. Take a look at https://api.jquery.com/find/. If you cache one selector and then perform searches within that selector it may gain you a little performance bump.
I think, If the chat module has selectors only for its children, then it's a good pattern. Like:
<div id="chat-module">
<div class="indicator">...</div>
<div class="status-text">...<div>
...
</div>
<script src="and your chat module.js"></script>
// your chat module selecting .indicator:
// $('#chat-module.indicator')
Also, add a shut-down function to your module. So, when you remove it from the view (as in a single-page-app), you can nullify your selectors and detaching event handlers, like: delete this.elements.indicator and event detaching code.
There are also other/better patterns for this, like, when a user types something, you fire an event, and catch that event in your module. To separate UI and the code.
I've learnt development by looking at other people's codes, so I'm not very good with terminologies. Lately I've been writting my JS/Jquery this way:
$(document).ready(function() {
testingFunc.init();
});
var testingFunc = {
$object: $('#object'),
init: function() {
var _that = this;
console.log($object);
}
}
Can someone please tell me if this a pattern of some sort? Or can someone please tell me how to describe the code I've done above?
This particular style represented in your code is an "object literal" pattern. It differs only slightly from a "module" pattern when you find yourself not requiring specific properties or methods to be private.
Before getting into a trap of terminologies, you may want to understand (in principle) what Javascript patterns are, and then identify those which may be architecturally best-fit for your project.
You may get an in-depth understanding from this mini-book from Addy Osmani:
http://addyosmani.com/resources/essentialjsdesignpatterns/book/
And a high-level article from him:
http://addyosmani.com/largescalejavascript/
The first part is using a jQuery selector with the listener "ready". What this means is that the callback function attached to the selector and listener will run once the document (in this case the browser window) is ready (in web terms, this means when the page finishes loading).
The second part of your code is following a standard called object literal, which is a JavaScript methodology that follows the principles of key->value
Perhaps you can name it the Object Literal pattern like used by Rebecca Murphey in her article. However I do not think that it's widely adopted as an official name for this kind of code structure, but it seems appropriate.
I guess you are wondering about the ready function. In order to understand how it works, you have to know that when you load an HTML page into you browser, the HTML structure is turned into a javascript tree called "DOM" (Document Object Model). In your sample, the DOM is referenced through the variable named document. To populate this tree, each markup has to be initialized as a javascript object. Once this job is done, the "ready" event is raised, invoking every function which is bound to it. To summarize :
$(document).ready(function () { testingFunc.init(); });
// translation : Once the DOM has been initialized, call "init".
Regarding your code, $('#object') attempts to query the DOM tree to find a node with an id set to "object" (e.g. <div id="object">). However, the document is probably not yet fully initialized. As a result, this query might fail. To avoid this risk you should rather do this :
var testingFunc = {
$object: null,
init: function() {
this.$object = $('#object');
console.log(this.$object);
}
}
You can think of the DOM as a folder structure, where each folder and file is an HTML markup. jQuery browses the DOM tree the same way that you browse your files explorer.
i've been playing with MVC for a while now, but since the project i'm on is starting to get wind in its sails more and more people are added to it. Since i'm in charge of hacking around to find out some "best practice", i'm especially wary about the possible misuses of javascript and would like to find out what would be the best way to have our views and partial views play nicely with javascript.
For the moment, we're having code that looks like this (only simplified for example's sake)
<script type="text/javascript">
function DisableInputsForSubmit() {
if ($('#IsDisabled').is(':checked')) {
$('#Parameters :input').attr('disabled', true);
} else {
$('#Parameters :input').removeAttr('disabled');
}
}
</script>
<%=Html.SubmitButton("submit", Html.ResourceText("submit"), New With {.class = "button", .onclick = "DisableInputsForSubmit(); if ($('#EditParameters').validate().form()) {SetContentArea(GetHtmlDisplay('SaveParameters', 'Area', 'Controller'), $('#Parameters').serialize());} return false;"})%><%=Html.ResourceIcon("Save")%>
Here, we're saving a form and posting it to the server, but we disable inputs we don't want to validate if a checkbox is checked.
a bit of context
Please ignore the Html.Resource* bits, it's the resource management
helpers
The SetContentArea method wraps ajax calls, and GetHtmlDisplay
resolves url regarding an area,
controller and action
We've got combres installed that takes care of compressing, minifying
and serving third-parties libraries and what i've clearly identified as reusable javascript
My problem is that if somebody else defines a function DisableInputsForSubmit at another level (let's say the master page, or in another javascript file), problems may arise.
Lots of videos on the web (Resig on the design of jQuery, or Douglas Crockford for his talk at Google about the good parts of javascript) talk about using the namespaces in your libraries/frameworks.
So far so good, but in this case, it looks a bit overkill. What is the recommended way to go? Should i:
Create a whole framework inside a namespace, and reference it globally in the application? Looks like a lot of work for something so tiny as this method
Create a skeleton framework, and use local javascript in my views/partials, eventually promoting parts of the inline javascript to framework status, depending on the usage we have? In this case, how can i cleanly isolate the inline javascript from other views/partials?
Don't worry and rely on UI testing to catch the problem if it ever happens?
As a matter of fact, i think that even the JS code i've written that is in a separate file will benefit from your answers :)
As a matter of safety/best practice, you should always use the module pattern. If you also use event handlers rather than shoving javascript into the onclick attribute, you don't have to worry about naming conflicts and your js is easier to read:
<script type="text/javascript">
(function() {
// your button selector may be different
$("input[type='submit'].button").click(function(ev) {
DisableInputsForSubmit();
if ($('#EditParameters').validate().form()) {
SetContentArea(GetHtmlDisplay('SaveParameters', 'Area','Controller'), $('#Parameters').serialize());
}
ev.preventDefault();
});
function DisableInputsForSubmit() {
if ($('#IsDisabled').is(':checked')) {
$('#Parameters :input').attr('disabled', true);
} else {
$('#Parameters :input').removeAttr('disabled');
}
}
})();
</script>
This is trivially easy to extract into an external file if you decide to.
Edit in response to comment:
To make a function re-usable, I would just use a namespace, yes. Something like this:
(function() {
MyNS = MyNS || {};
MyNS.DisableInputsForSubmit = function() {
//yada yada
}
})();
I would like to ask if there is a liveconnect equivalent for ActionScript 3. I understand that there is the ExternalInterface class inside AS3 but it only supports calling a method by name. The really cool thing about Java and LiveConnect is that you can do something like
function jsFunc(name) = {
this.name = name;
this.talk = function(){
alert('hello world my name is ' + this.name);
}
}
javaapplet.function(new jsFunc("bob"));
The above approaches pseudo code since I never tested it but I've seen it in action. In AS3, while I am able to pass in an instance of JavaScript "object" into AS, it is often converted into an ActionScript Object instance which does away with all the functions as far as I'm aware.
I saw an implementation of JSInterface but I don't think it does specifically that. Is there any way to make OO like javascript work with ActionScript 3?
Try this library on Google code:
http://code.google.com/p/jsobject/
ExternalInterface.call("f = function() { alert('Is this like live connect?'); }");
Actually the main usage scenario is to have JS "objects" interacting with the Flex SWF application. Therefore when the JS "object" wants to say wait for something happening in the SWF object, it will put in a "this" with a callback.
After researching, the way I used to accomplish this is via the Flex Ajax bridge. It may not be a direct answer to the way I phrased the question but it was sufficient for my needs.
Basically what I do is via FABridge, after initializing, I'll attach event listeners to the object.
// JS
FlexApp.addEventListeners('flexDidSomething', this.doSomething().bind(this)); //using mootools;
and in Flex, the main application itself
// AS
dispatchEvent(new CustomCreatedEvent(param1, param2));
And inside the JS function I'll access the get methods of the event object to retrieve the params.
There's tight coupling in that sense but it works at least for what I need.
Hope this is helpful!
JSInterface designed exactly for such things.
Taking Jeff Atwood's advice, I decided to use a JavaScript library for the very basic to-do list application I'm writing. I picked the Dojo toolkit, version 1.1.1. At first, all was fine: the drag-and-drop code I wrote worked first time, you can drag tasks on-screen to change their order of precedence, and each drag-and-drop operation calls an event handler that sends an AJAX call to the server to let it know that order has been changed.
Then I went to add in the email tracking functionality. Standard stuff: new incoming emails have a unique ID number attached to their subject line, all subsequent emails about that problem can be tracked by simply leaving that ID number in the subject when you reply. So, we have a list of open tasks, each with their own ID number, and each of those tasks has a time-ordered list of associated emails. I wanted the text of those emails to be available to the user as they were looking at their list of tasks, so I made each task box a Dijit "Tree" control - top level contains the task description, branches contain email dates, and a single "leaf" off of each of those branches contains the email text.
First problem: I wanted the tree view to be fully-collapsed by default. After searching Google quite extensively, I found a number of solutions, all of which seemed to be valid for previous versions of Dojo but not the one I was using. I eventually figured out that the best solution would seem to be to have a event handler called when the Tree control had loaded that simply collapsed each branch/leaf. Unfortunately, even though the Tree control had been instantiated and its "startup" event handler called, the branches and leaves still hadn't loaded (the data was still being loaded via an AJAX call). So, I modified the system so that all email text and Tree structure is added server-side. This means the whole fully-populated Tree control is available when its startup event handler is called.
So, the startup event handler fully collapses the tree. Next, I couldn't find a "proper" way to have nice formatted text for the email leaves. I can put the email text in the leaf just fine, but any HTML gets escaped out and shows up in the web page. Cue more rummaging around Dojo's documentation (tends to be out of date, with code and examples for pre-1.0 versions) and Google. I eventually came up with the solution of getting JavaScript to go and read the SPAN element that's inside each leaf node and un-escape the escaped HTML code in it's innerHTML. I figured I'd put code to do this in with the fully-collapse-the-tree code, in the Tree control's startup event handler.
However... it turns out that the SPAN element isn't actually created until the user clicks on the expando (the little "+" symbol in a tree view you click to expand a node). Okay, fair enough - I'll add the re-formatting code to the onExpand() event handler, or whatever it's called. Which doesn't seem to exist. I've searched to documentation, I've searched Google... I'm quite possibly mis-understanding Dojo's "publish/subscribe" event handling system, but I think that mainly because there doesn't seem to be any comprehensive documentation for it anywhere (like, where do I find out what events I can subscribe to?).
So, in the end, the best solution I can come up with is to add an onClick event handler (not a "Dojo" event, but a plain JavaScript event that Dojo knows nothing about) to the expando node of each Tree branch that re-formats the HTML inside the SPAN element of each leaf. Except... when that is called, the SPAN element still doesn't exist (sometimes - other times it's been cached, just to further confuse you). Therefore, I have the event handler set up a timer that periodically calls a function that checks to see if the relevant SPAN element has turned up yet before then re-formatting it.
// An event handler called whenever a "email title" tree node is expanded.
function formatTreeNode(nodeID) {
if (dijit.byId(nodeID).getChildren().length != 0) {
clearInterval(nodeUpdateIntervalID);
messageBody = dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML
if (messageBody.indexOf("<b>Message text:</b>") == -1) {
messageBody = messageBody.replace(/>/g, ">");
messageBody = messageBody.replace(/</g, "<");
messageBody = messageBody.replace(/&/g, "&");
dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML = "<b>Message text:</b><div style=\"font-family:courier\">"+messageBody+"</div>";
}
}
}
// An event handler called when a tree node has been set up - we changed the default fully-expanded to fully-collapsed.
function setupTree(theTree) {
dijit.byId("tree-"+theTree).rootNode.collapse();
messageNode = dijit.byId("tree-"+theTree).rootNode.getChildren();
for (pl = 0; pl < messageNode.length; pl++) {
messageNode[pl].collapse();
messageNode[pl].expandoNode.onclick = eval("nodeUpdateIntervalID = setInterval(\"formatTreeNode('"+messageNode[pl].id+"')\",200); formatTreeNode('"+messageNode[pl].id+"');");
}
}
The above has the feel of a truly horrible hack, and I feel sure I must have taken a wrong turn somewhere early on in my thought process. Can someone please tell me:
The correct way to go about putting nicely-formatted text inside a Dojo/Dijit Tree control.
The correct way to handle Dojo events, like where I can figure out what events are available for me to subscribe to.
A better JavaScript library to use (can I do what I want to with JQuery and avoid the all-around-the-houses approach seen above?).
PS: If you're naming a software project, give thought to its name's uniqueness in Google - I'm sure searching for "Dojo" documentation in Google would be easier without all the martial arts results getting in the way.
PPS: Firefox spellchecker knows how to spell "Atwood", correcting me when I put two 'T's instead of one. Is Jeff just that famous now?
I assume that you followed the dijit.Tree and dojo.data in Dojo 1.1 tutorial which directed you to pass the data to the tree control using a data store. That had me banging my head of a brick wall for a while.
Its not really a great approach and the alternative is not really well documented. You need to create a use model instead. I have included an example below of a tree model that I created for displaying the structure of an LDAP directory.
You will find the default implementation of the model in your dojo distribution at ./dijit/_tree/model.js. The comments should help you understand the functions supported by the model.
The IDirectoryService class the code below are stubs for server-side Java POJOs generated by Direct Web Remoting (DWR). I highly recommend DWR if you going to be doing a lot of client-server interaction.
dojo.declare("LDAPDirectoryTreeModel", [ dijit.tree.model ], {
getRoot : function(onItem) {
IDirectoryService.getRoots( function(roots) {
onItem(roots[0])
});
},
mayHaveChildren : function(item) {
return true;
},
getChildren : function(parentItem, onComplete) {
IDirectoryService.getChildrenImpl(parentItem, onComplete);
},
getIdentity : function(item) {
return item.dn;
},
getLabel : function(item) {
return item.rdn;
}
});
And here is an extract from the my JSP page where I created the model and used it to populate the tree control.
<div
dojoType="LDAPDirectoryTreeModel"
jsid="treeModel"
id="treeModel">
</div>
<div
jsid="tree"
id="tree"
dojoType="dijit.Tree" model="treeModel"
labelAttr="name"
label="${directory.host}:${directory.port}">
</div>