I'm writing a single page ws++ site, and I'd like to keep my code grouped first by "page" (I think I need a new word since it never posts back) then by section then by concept etc.
I'd like to split up WebSocket.onmessage
across my code much in the same way that $('#someElement')
can constantly have an event like click(function(){})
added to it.
Can this be done with WebSocket.onmessage(function(){})
? If so, how?
As some jQuery programmers happily know, an event can be initially set then added to in multiple places across the js. That's my favorite thing about js, the "put it anywhere as long as it's in order" ability. This makes code organization so much easier for me at least.
With WebSockets, really, the action client side for me so far is with the WebSocket.onmessage()
handler since WebSocket.send()
can be used anywhere and really just ports js data to the server.
onmessage()
now owns my page, as whatever's in it initiates most major actions such as fading out the login screen to the first content screen upon a "login successful" type message.
According to my limited understanding of js, the onmessage()
handler must be set all in one place. It's a pain to keep scrolling back/tabbing to another file to make a change to it after I've changed the js around it, far, far, away.
How can I add to the WebSocket.onmessage()
handler in multiple places across the js?
I'm writing a single page ws++ site, and I'd like to keep my code grouped first by "page" (I think I need a new word since it never posts back) then by section then by concept etc.
I'd like to split up WebSocket.onmessage
across my code much in the same way that $('#someElement')
can constantly have an event like click(function(){})
added to it.
Can this be done with WebSocket.onmessage(function(){})
? If so, how?
As some jQuery programmers happily know, an event can be initially set then added to in multiple places across the js. That's my favorite thing about js, the "put it anywhere as long as it's in order" ability. This makes code organization so much easier for me at least.
With WebSockets, really, the action client side for me so far is with the WebSocket.onmessage()
handler since WebSocket.send()
can be used anywhere and really just ports js data to the server.
onmessage()
now owns my page, as whatever's in it initiates most major actions such as fading out the login screen to the first content screen upon a "login successful" type message.
According to my limited understanding of js, the onmessage()
handler must be set all in one place. It's a pain to keep scrolling back/tabbing to another file to make a change to it after I've changed the js around it, far, far, away.
How can I add to the WebSocket.onmessage()
handler in multiple places across the js?
- As someone who is interested to answer your question, I have no idea what are you talking about! :) Perhaps it's better if you expand it a little more. – Mehran Commented Jul 6, 2013 at 14:22
- I'll give you an answer then you can say if I'm understood correctly. – Mehran Commented Jul 6, 2013 at 15:31
4 Answers
Reset to default 4To answer your last question;
how can I add to onmessage handler in multiple places across the js?
You can define your own personal (global) event handler in which you accept arbitrary number of handler functions. Here's an example:
window.bind: function(name, func, context) {
if (typeof this.eventHandlers[name] == "undefined") {
this.eventHandlers[name] = [func];
this.eventContexts[name] = [context];
}
else {
var found = false;
for (var index in this.eventHandlers[name]) {
if (this.eventHandlers[name][index] == func && this.eventContexts[name][index] == context) {
found = true;
break;
}
}
if (!found) {
this.eventHandlers[name].push(func);
this.eventContexts[name].push(context);
}
}
}
window.trigger: function(name, args) {
if (typeof this.eventHandlers[name] != "undefined") {
for (var index in this.eventHandlers[name]) {
var obj = this.eventContexts[name][index];
this.eventHandlers[name][index].apply(obj, [args]);
}
}
}
// === Usage ===
//First you will bind an event handler some where in your code (it could be anywhere since `.bind` method is global).
window.bind("on new email", function(params) { ... });
//Then you need to trigger "on new email" in `onmessage` once appropriate events happen.
WebSocket.onmessage(function(data) {
//Work on data and trigger based on that
window.trigger("on new email", { subject: data.subject, email: data.email });
})
This code is a part of an open source project I worked on before. It gives events names and let you set context for your handler (for methods instead of functions). Then you can call trigger in your onmessage handler of your socket. I hope this is what you are looking for.
You can create a wrapper which will handle WS events on itself. See this example CoffeeScript:
class WebSocketConnection
constructor: (@url) ->
@ws = new WebSocket(@url)
@ws.onmessage = @onMessage
@callbacks = []
addCallback: (callback) ->
@callbacks.push callback
onMessage: (event) =>
for callback in @callbacks
callback.call @, event
# and now use it
conn = new WebSocketConnection(url)
conn.addCallback (event) =>
console.log event
You can do it with addEventListener
:
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
I've constructed a CoffeeScript class to solve this problem. It's similar to @Valent's but a bit more full-featured, so I figured I'd share it. It provides "on"
, "off"
, and "clear"
methods for web socket events and also provides forwarding functions for "send"
and "close"
so that you pretty much don't have to touch the socket directly. If you do need access to the actual WebSocket object, you can get there by superWebSocket.ws
.
edit: I added a getConnection
static method to produce url-dependent singletons. This way there's only one connection per url and if you attempt to create a 2nd, it just gives you the existing one. It also protects against anyone calling the constructor directly.
edit: I municate across the socket in JSON. I added some code that will run JSON.stringify
on any non-string passed into send
and also will attempt to run JSON.parse
on any message received via a handler.
superSockets = {}
class SuperWebSocket
@getConnection: (url)->
superSockets[url] ?= new SuperWebSocket url
superSockets[url]
constructor: (url)->
if arguments.callee.caller != SuperWebSocket.getConnection
throw new Error "Calling the SuperWebSocket constructor directly is not allowed. Use SuperWebSocket.getConnection(url)"
@ws = new WebSocket url
events = ['open', 'close', 'message', 'error']
@handlers = {}
events.forEach (event)=>
@handlers[event] = []
@ws["on#{event}"] = (message)=>
if message?
try
message = JSON.parse message.data
catch error
for handler in @handlers[event]
handler message
null
on: (event, handler)=>
@handlers[event] ?= []
@handlers[event].push handler
this
off: (event, handler)=>
handlerIndex = @handlers[event].indexOf handler
if handlerIndex != -1
@handlers[event].splice handlerIndex, 1
this
clear: (event)=>
@handlers[event] = []
this
send: (message)=>
if typeof(message) != 'string'
message = JSON.stringify message
@ws.send message
close: => @ws.close()