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 commentUnderstanding Javascript self executing functions
A colleague of mine asked yesterday what the point of self-executing functions (SEF) in Javascript was. This is a common question; surely a function, by definition, contains code that you wish to execute at some point later, not right now, no?
Yes, generally. But SEFs do have their uses - if you can get over the theoretical heressy that is a function executing itself.
The most common use of these in my experience is to 'capture' current values when assigning event handlers. Consider the following problem:
1var animal = 'dog';
2$('#someLink').click(function() { alert(animal); });
3animal = 'cat';
When you click the link the alert will say 'cat', not 'dog'. This is because the event callback is evaluated when the event fires - not when it is bound. In other words, our alert will say the current value of animal - not the value as it was when the event was bound.
You could get round this by using a SEF. First a demo, then the science:
1var animal = 'dog';
2var evtCallback = (function(animal) {
3 return function() {
4 alert(animal);
5 };
6})(animal);
7$('#someLink').click(evtCallback);
8animal = 'cat';
The crucial difference here is that our event callback is generated by a SEF. We do this to take advantage of the local scope it provides. When we pass it our animal variable, it takes its own, local copy of it - entirely unconnected with the variable of the same name in the outer scope.
So even though the outer scope animal changes value a few lines later, the copy of this variable in our SEF is fixed to the value it had at the time it was captured: 'dog'.
Obviously if real-world code was as simple as the above, we could just hard-code the word 'dog' in our alert, but you get the idea.
Incidentally, jQuery provides its own shortcut to achieve the above effect, using the bind() method. (bind() is the daddy of event methods; all others, such as click(), effectively get re-routed to this behind the scenes).
It allows you to pass in event data that, like our second example above, will be used in the event callback - protected from anything that happens in the outer scope between now and the time the event fires. Event data is passed as the second argument:
1$('#someLink').bind('click', {animal: 'dog'}, function() {
2 alert(animal);
3});
MSThis - simulate W3C events in IE
Just posted a script I wrote which aims to tackle the age-old problem of IE registering events on Window rather than on the event trigger element.
This has long caused Javascript developers headaches since it means that, in event callbacks, the 'this' keyword does not point to the trigger element like it does in the W3C standard.
MSThis rectifies that, simulating W3C event-handling in IE. It works by storing an event handler on an element, then firing them as methods of it. Frameworks like jQuery probably do something similar to harmonise W3C vs. MS event implementations.
Head over here to download, get usage info or view a demo.
post a commentHello, world
Phew, so here we are. I finally launched. Welcome to my site, primarily a vehicle to promote and share the growing array of Javascript work and other scripts I write in my capacity as web developer in Hampshire.
Although I've historically been a back-end developer, my interest is more with JS/jQuery/AJAX. So that's what I'll be posting, though expect some sprinklings of PHP/MySQL too.
echo "hello, world!";
All my stuff is written with integration and customisation in mind right from the off; how often do you find a script you think solves a problem you have, only to find it doesn't quite work how you want - or at all?
I'll shortly be loading up the site with recent scripts, including a cross-browser curvey corners script (the existing curvey corners jQuery plugin has some quirks...), a fix for the age-old issue of IE running events on Window, not on the trigger element, and more...
post a comment