Distinguishing real events from simulated events
Simulated events are one of the real joys of jQuery. Geniusly, to simulate an event in jQuery you simply call the method relating to the event in question (or use the trigger() method) without any arguments.
1//set up the event
2$('#tabs').on(''click', 'li', function() {
3 alert('Hello!');
4});
5
6//simulate the event immediately on the first LI
7$('#tabs li:first').click();
This is a common situation. Here, I have some tabs which, when clicked, show a corresponding tab content area, which are all hidden to start with. By simulating the event on the first tab, it is automatically turned on as though we had clicked it ourselves.
(Yes, I could avoid this by having the first tab area showing by default via CSS, but let's imagine our event handler does more than simply show and hide DIVs, but also some more complex code).
So far, so easy, and so common. But how do you differentiate a simulated event from a real one?
I had a situation the other day where I had a carousel-like widget on the page, and a bigger version of the same carousel, hidden, to appear in a lightbox should the user wish. Traversing one of the carousels (i.e. changing slide) should update the other one, too.
Consider the following.
1var carousels = $('.carousel');
2
3//listen for clicks to carousel left & right buttons
4carousels.on('click', 'button', function(evt) {
5
6 //get this carousel, other carousel and click direction
7 var this_carousel = $(this).closest('.carousel'),
8 other_carousel = carousels[$(this).is('#carousel1') ? 'first' : 'last'](),
9 direction = $(this).is('.left') ? 'left' : 'right';
10
11 //simulate the same click on the other carousel
12 other_carousel.find('button.'+click_direction).click();
13
14 //more code here...
15
16});
There, we listen for clicks to the left/right buttons of each carousel and then simulate the same click on the other carousel.
But there's an obvious problem; the simulated click uses the same event handler, so it too will invoke a simulated click on the first carousel, and so on and so on, without end.
What we actually want, of course, is for a simulated click to occur only in response to a real click. In other words, how do we spot the difference between a real and simulated event with jQuery?
Simple: with a simulated event, the originalEvent property of the event object will be undefined, whereas with a real event it will be the native event object.
1$('#tabs').on('click', 'li', function(evt) {
2 if (evt.originalEvent) {
3 //real event
4 } else {
5 //simulated event
6 }
7});
Obviously you don't have to simulate events - you could assign their callbacks to a named function and call the function manually. But then you'd have no event object passed, so this often means filling your code with lots of conditions - to work both as an event callback and as a direct function call. Simulated event calls save the need for this reengineering.
post a commentNon-AJAX use for jQuery's deferred objects
I'm currently writing another article for .NET magazine on the newer features of jQuery, for developers who perhaps got comfy with jQuery 1.3 or 1.4 and didn't keep up.
One of the obvious candidates for the article is deferred objects, which landed in jQuery 1.5 as part of the overhaul to jQuery's AJAX module.
AJAX is the obvious use-case for deferred objects, and it's simple to come up with examples for that. But I was also trying to show a non-AJAX example.
I'm talking about cases where you would manually make your own deferred objects and apply subjective logic as to whether, when and how it is resolved or rejected. So other forms of asynchronous code execution.
This is quite a different beast from using deferreds with AJAX, since, at least usually, jQuery's AJAX methods automatically create, return and resolve/reject deferred objects for you. In other words, you can use deferreds in an AJAX context without ever going near methods like $.Deferred(), $.when() or deferred.resolve().
Click the vowels
I eventually came up with a slightly contrived game for children where they have to identify and click the vowels in a sequence of words. Each vowel would constitute a deferred object. When clicked, the vowel fades out and its deferred object is set to resolved. When all deferreds are resolved (i.e. all vowels have been clicked), we give feedback and move on. I think it's quite a nice pattern.
You can see a demo of the game here.
First, some simple HTML:
1<h1>Click all the vowel letters</h1>
2<div id='words'></div>
And CSS:
1#words { height: 100px; }
2#words div { display: inline-block; width: 100px; height: 100%; text-align: center; font-size: 50px; line-height: 90px; margin-right: 10px; background: #e50; color: #fff; cursor: default; }
3#words div:last-child { margin: 0; }
Now on to the JS (all inside a DRH, of course, as we're dealing with the DOM).
1//prep
2var
3words = ['square', 'hosepipe', 'canine', 'flower'],
4container = $('#words'),
5vowels = 'aeiou',
6next_word_index = 0;
All rather self-explanatory. Now for the bulk of the code:
1function build_word(word) {
2
3 //increment the next-word index
4 next_word_index++;
5
6 //remove the previous word, if any
7 container.empty();
8
9 //an array to store our deferreds (one for each vowel)
10 var deferreds = [];
11
12 //loop over the word's letters
13 for (var i=0, len=word.length; i<len; i++) {
14
15 var
16 letter = word.substr(i, 1),
17 isVowel = vowels.indexOf(letter) != -1,
18 letter_div = $('<div />', {text: letter}).appendTo(container);
19
20 //if this letter is a vowel...
21 if (isVowel) {
22
23 //set up a deferred object for it and log it in the array
24 var deferred = $.Deferred();
25 deferreds.push(deferred);
26
27 //on click, fade it out then resolve its deferred
28 letter_div.click({deferred: deferred}, function(evt) {
29 $(this).animate({opacity: .2}, evt.data.deferred.resolve).unbind('click');
30 });
31 }
32 }
33
34 //when all deferreds are resolved, do feedback and move on
35 $.when.apply(null, deferreds).done(function() {
36 var msg = 'Well done - you got all the vowels! ';
37 if (words[next_word_index]) {
38 alert(msg+"Let's try the next word...");
39 build_word(words[next_word_index]);
40 } else
41 alert(msg+"That's the end of the game!");
42 });
43}
44
45//on page entry, do the first word
46build_word(words[next_word_index]);
A few points
Hopefully the comments make it possible to follow what's going on there, but here's some points of particular note.
Firstly, I invoke $.when not directly but via the native apply(). This is because $.when() does not presently allow you to pass multiple objects as an array, which is necessary for my example. apply(), as you may know, allows you to stipulate arguments to a function as an array, so problem solved.
(If you're new to $.when(), I'll be covering that in a separate post, as it has a lot to offer your patterns.)
Secondly, in a production environment it would be prudent to expose not the deferreds themselves but their inherant promise objects (via the promise() method) instead. This allows environmental code to bind callbacks to them but not interfere with their state or progress. See the jQuery API page on promise objects for more detail.
Summary
I reiterate that this is a slightly contrived example, to highlight the use of deferreds independently of AJAX, but I think it's quite a nice pattern.
Of course, the same effect could be achieved several other ways without deferreds; one could continually check, in the fadeout callback, whether there were any vowels still remaining at full opacity. If no, the user has clicked all the vowels. That would require a more complex fadeout callback, but it would of course work.
5 comments | post newJSON-P: what it is, what it's not and how it works
I thought I'd take time out from discussing the brave new world of ECMA5 (to be continued) and do a post on JSON-P, since it's occured to me lately that it's often misunderstood by intermediate developers.
This post is aimed at developers who use JSON-P but have been been too sure about what it does under the bonnet. We'll pin down what it is, isn't, how it works, and some common misconceptions.
JSON-P in two sentences
JSON-P is means of loading data from a remote server, provided the server in question is expecting the request. It is not AJAX, and does not necessarily involve JSON.
How JSON-P works
JSON-P gets around the fact that JavaScript's Same Origin Policy prevents cross-domain AJAX by exploiting the fact that the src attribute of the script tag is not subject to this limitation, and can load in content (i.e. JavaScript) from remote servers.
Therefore, the steps of a JSON-P request are as follows:
- a new script tag is DOM-scripted into the page (or an old one is re-used)
- the request URL is applied to the script tag's src attribute
- the requested server loads our request and outputs a response
- the response is evaluated as JS, since we requested via a script tag
1var req = 'http://www.someserver.com/web_service';
2var script = document.createElement('script');
3script.src = req;
4document.head.appendChild(script);
If the server's response is...
alert('hello from someserver.com!');
...the alert will fire in our page once the request completes. Likewise, if the response is...
var something = 'hello from someserver.com!';
...a global variable, something, will be set in our page.
The key to understanding JSON-P is to remember that the server's response ultimately ends being evaluated as JavaScript, since it is called by a script tag.
These are simple examples. Usually, of course, you're going to want the server to give you some data.
Accessing the server response: assignment and callback
Normally we want our JSON-P request to result in one of two things happening:
- the data is passed to a globally-accessible callback function that we've prepared
- the data is assigned to a global variable
It must do one or the other, otherwise the response will not be accessible to our page's JavaScript. So if the server simply returned:
{name: 'Fido', type: 'Labrador', age: 3}
...that data would be unreachable - even though it's valid JavaScript. This is not a trait of JSON-P but of JavaScript itself, but it is a common stumbling block for developers new to JSON-P. Think about it; if a linked script in your page contains the above, without assigning it to a variable or passing it to a function, it is inaccessible.
Assignment
We saw variable assignment in the example further up. This is useful when, say, loading jQuery from CDN; we don't need the request to call a callback - we simply want jQuery to load. In other words, our JSON-P response should simply assign the variable jQuery (the jQuery namespace on which the library's API lives).
Callback
More common is for the response to call a callback function that we have prepared. For this, the server will need to know the name of the callback function; large, public JSON-P web services allows you to specify this in your request structure.
For example, the following is a request to Twitter's JSON-P web service to retrieve five Tweets that mention Paddington Bear:
'http://search.twitter.com/search.json?q=Paddington%20Bear&rrp=5&callback=my_callback_func'
If you run that in your browser, you'll see the web service outputs JS that passes the returned data, as JSON, to the callback I requested, my_callback_func().
Imagining the Twitter web service in simple terms, if it was built in PHP, it would look something like this: (I include this only for context; if you're a front-end-only developer, don't worry too much about this).
1//get JSON-encoded Tweets
2$tweetsJSON = get_tweets_json();
3
4//output
5if (isset($_GET['callback'])) echo $_GET['callback']."(";
6echo $tweetsJSON;
7if (isset($_GET['callback'])) echo ");";
The web service fetches and JSON-encodes the tweets, then builds an output string consisting of a call to our callback function, passing it the JSON as its only argument, i.e.
my_callback_func({ /* Twitter JSON here */ });
If we hadn't specified a callback, only the JSON would be output - useless for JSON-P requests but usable by server-side cross-domain requests (not relevant to this article).
As I touched on above, it is not always certain that you'll need to tell the web service the name of your callback function. If you control the web service, you may choose to hard-code the name of the callback on the server, so our PHP would look like:
1$data = get_some_data(); //get data and format as JSON
2echo "jsonp_callback(".$data.");";
There, the server assumes jsonp_callback - so your callback will have to be called that. This is a less common scenario, but illustrates the point that, whilst the ability to stipulate the name of your callback to the web service is a convenience, it is not a fundamental component of the JSON-P concept.
Callbacks with jQuery
We've established that our callback needs to be globally accessible. Given that, you might wonder how this, a typical JSON-P request with jQuery, works:
1$.ajax({
2 url: 'http://search.twitter.com/search.json?q=Paddington%20Bear&rrp=5&callback=?',
3 dataType: 'jsonp',
4 success: function(json) { console.log(json); }
5});
There, our callback is an anonymous function, clearly not globally accessible. What we don't see, though, is that jQuery redefines it globally, assigning it a randomly generated function name (to minimise name clashes with other global entities).
This is why we stipulated the name of our callback function as ? in the request URL rather than choosing it ourselves (though we could have done). This permits jQuery to handle this whole issue; sure enough, Opera's Dragonfly tools shows the actual request URL that was sent, and the global function that was created:
Misconceptions
Now we've taken a tour of what JSON-P is and how it works, some of the common misconceptions about should now seem obvious when you read them.
Misconception 1: JSON-P is a form of AJAX request
[UPDATE: granted, this depends on your definition of AJAX. I would hold that it's not a form of AJAX, but see the comments below for a discussion on this...]
This misconception is understandable for two reasons. Firstly, JSON-P involves the silent loading of data, just like AJAX. Secondly, jQuery implements JSON-P through its AJAX module, something that is understandable (i.e. to have a single section of the API concerned with data retrieval/submission) but can and does lead to this misconception.
As we've seen, though, JSON-P works by exploiting the src attribute of script tags, whereas AJAX requests are done over XMLHTTPRequest.
Misconception 2: JSON-P always involves JSON
The original proposal for JSON centred on the idea that servers would, on receipt of a JSON-P request, go off to the database (where applicable), get some rows of data, and serialise and output that data as JSON.
This is often the case - but it's not to say it has to be. The server response could equally be a string or a number, say.
Misconception 3: JSON-P requests must involve a callback
Again, most JSON-P web services do involve callbacks, but it does not have to be the case. As we saw early on, there's no need for callbacks when, say, loading jQuery over JSON-P - we simply want jQuery's jQuery global variable to be set.
Misconception 4: callbacks must be global functions
It's true that the callback function (or assigned variable) must be globally accessible, but this does not necessarily mean global in sense of being in the outermost scope.
1<script>
2function func() { } //global function
3var namespace = {};
4namespace.func = function() {}; //globally accessible function
5</script>
If you're using a namespace pattern like above, i.e. where your entire code exists in a single namespace to avoid global pollution, there's no reason your callback function can't be a method of that namespace. So our Twitter request from above might become:
'http://search.twitter.com/search.json?q=Paddington%20Bear&rrp=5&callback=namespace.func2'
Likewise if the web service sets a variable rather than calling a callback. You could have the server do this:
1$data = get_some_data(); //get data and format as JSON
2echo "namespace.jsonp_data = ".$data.";";
So there you have it
So there you have it. If JSON-P was hazy for you before this, I hope I've helped clear up the issue and shown you how it works under the scenes. It's certainly one of the lesser understood elements of every-day web development, but it pays to understand precisely what's going on.
For more on JSON-P, be sure to check out Kyle Simpson's JSON-P.org.
10 comments | post newJavaScript getters and setters: varying approaches
Last week I posted an introductory article on ECMAScript 5 object properties, and the mini-revolution that I think they constitute. (The post made the coveted JavaScript Weekly - thanks, guys.)
One of the key features of them is the ability to define getter/setter callbacks on them.
Getters and setters are a means of providing an arm's-length way of getting or setting certain data, whilst keeping private other data, and are common of most languages. In JavasScript, setters are also a good way of ensuring your UI stays up to date as your data changes, which I'll show you an example implementation further down.
A new approach to getters and setters
The new approach looks like this, and can be used only on properties created via the new Object.create() and Object.definePropert[y/ies]() methods.
1var dog = {}, name;
2var name;
3Object.defineProperty(dog, 'name', {
4 get: function() { return name; },
5 set: function(newName) { name = newName; }
6});
7dog.name = 'Fido';
8alert(dog.name); //Fido
You'll note that this approach requires the help of a 'tracker variable' (in our case name) via which the getter/setter reference the property's value. This is to avoid maximum recursion errors that the following would cause:
1...
2 get: function() { return this.name; }, //MR error
3...
That happens because we set a getter, via which any attempt to read the property is routed. Therefore, having the getter reference this.name is effectively asking the getter to call itself - endlessly. Likewise for a setter, if it tried to assign to this.name.
Since each property needs its own tracker, and you don't want lots of variables flying around, it's a good idea to use a closure when declaring several properties.
1var dog = {}, props = {name: 'Fido', type: 'spaniel', age: 4};
2for (var prop in props)
3 (function() {
4 var propVal = props[prop];
5 Object.defineProperty(dog, prop, {
6 get: function() { return propVal; },
7 set: function(newVal) { propVal = newVal; }
8 });
9 })()
10alert(dog.name+' is a '+dog.type); //Fido is a spaniel
11dog.name = 'Rex';
12alert(dog.name+' is age '+dog.age); //Rex is age 4
There, we declare what properties we want on our object, and some start values. The loop sets each property, and tracks its value via a private propVal variable in its closure.
One of the things I like about this new approach is you no longer have to call the getters/setters explicitly (as you did with previous implementations - see below) - they fire simply by talking to the property.
Admittedly this has its proponents and its opponents; those in favour say getters/setters should fire simply by calling/assigning to the property - not calling some special methods to do that. Those against normally point out that someone new to the code might be surprised to find that talking to a property in fact fires a function.
My take is that, as long as this is part of the spec, and your code is well documented, there can be few complaints with using the new implementation.
Other ways of doing getters/setters
In any case, I much prefer them to the implementation we got in JavaScript 1.5.
1var dog = {
2 type: 'Labrador',
3 get foo() { return this.type; },
4 set foo(newType) { this.type = newType; }
5};
6alert(dog.type); //Labrador
7dog.foo = 'Rotweiller';
8alert(dog.foo); //Rotweiller
I've never been in love with this approach, chiefly because you don't deal directly with the property but with a proxy that represents its getter/setter callbacks - in the above example foo. The new approach does away with this; you call/assign to the property just as you would if there were no getters/setters in play, and the getter/setter callbacks kick in automatically - they are not referenced explicitly.
That said, one good point about this separation of property value from getter/setter is that the getter/setter can safely reference the property via this without the risk of recursion error, as befalls the new approach.
The older way
There's also the depracated __defineGetter__() and __defineSetter__() technique.
1var dog = {
2 type: 'Labrador'
3};
4dog.__defineGetter__('get', function() { return this.type; });
5alert(dog.get); //Labrador
Once again you have to name your setters/getters. By far the most notable point about this approach, though, is you can assign getters/setters after assigning the property - not a super common desire, but useful any time you don't want to or can't alter the prototype. The other two implementations don't allow you to do this, at least without a lot of reworking.
A final point about these latter implementations is that they don't hijack control of your property like the new implementation does. That is, if a developer ignores them and manipulates the property directly, they can. This is not good news; if you defined getters/setters, you probably want them to run, not be bypassed.
1var dog = {
2 name: 'Henry',
3 set foo(newName) { alert('Hi from the setter!'); this.name = newName; }
4};
5dog.name = 'Rex'; //setter bypassed; its alert doesn't fire
Setters and a responsive UI
As I mentioned in the intro, another role of getters in JavaScript can be to keep your UI up to date as your data changes. Frameworks like Backbone JS sell themselves heavily on this concept.
As the intro to the Backbone documentation points out, medium-large JavaScript applications can easily get bogged down with jQuery selectors and other means trying to keep your views in-sync with your data.
A getter can help here. Here's something I cooked up:
1Object.UIify = function(obj) {
2 for(var property in obj) {
3 var orig = obj[property];
4 (function() {
5 var propVal;
6 Object.defineProperty(obj, property, {
7 get: function() { return propVal; },
8 set: (function(target) {
9 return function(newVal) {
10 propVal = newVal;
11 $(target).text(propVal);
12 };
13 })(orig.target)
14 });
15 })()
16 obj[property] = orig.val;
17 }
18 return obj;
19};
20
21$(function() {
22 var dog = Object.UIify({name: {val: 'Fido', target: '#name'}, type: {val: 'Labrador', target: '#type'}});
23 dog.name = 'Bert';
24 dog.type = 'Rotweiller';
25});
And here's some example HTML:
I'll go into the details of what my method does in a further post. Essentially, though, what's happening is we pass an object to the UIify() method where each property is a sub-object containing its starting value (val) and a CSS/jQuery selector pointing to to the UI element that should be updated as and when the value changes (target.)
UIify() then returns an object using the new ECMA5 getters/setters. Whenever a property of the object is overwritten, the corresponding UI element denoted by the target we specified is updated. In my case, the targets were simply elements with IDs, but it could of course be more complex targets - it's just CSS/jQuery selector syntax.
---------
So there you have it, three approaches through the ages. Next time up I'll be looking more at the new Object funcionality in ECMA5.
(p.s. for further reading, be sure to check out the extensive MDN article on working with objects, which talks a lot about getter/setter techniques.)
8 comments | post new