New Plugin: Live Query
August 19th, 2007
Live Query, previously called Behavior, utilizes the power of jQuery selectors by binding events or firing callbacks for matched elements auto-magically, even after the page has been loaded and the DOM updated.
For example you could use the following code to bind a click event to all A tags, even any A tags you might add via AJAX or a script.
-
$('a')
-
.livequery('click', function(event) {
-
alert('clicked');
-
return false;
-
});
Once you add new A tags to the document, Live Query will bind the click event and there is nothing else that needs to be called or done.
When an element no longer matches a selector the events Live Query bound to it are unbound. Also, the Live Query can be expired by calling expire which will no longer bind anymore events and unbind all the events it previously bound.
Live Query can even be used with the more powerful jQuery selectors. The following Live Query will match and bind a click event to all A tags that have a rel attribute with the word "friend" in it. If one of the A tags is modified by removing the word "friend" from the rel attribute, the click event will be unbound since it is no longer matched by the Live Query.
-
$('a[@rel*=friend]')
-
.livequery('click', function(event) {
-
doSomething();
-
});
Live Query also has the ability to fire a callback function when it matches a new element and another callback function for when an element is no longer matched. This provides ultimate flexibility and untold use-cases. For example the following code uses a function based Live Query to implement the jQuery hover helper method and remove it when the element is no longer matched.
-
$('li')
-
.livequery(function(){
-
$(this)
-
.hover(function() {
-
$(this).addClass('hover');
-
}, function() {
-
$(this).removeClass('hover');
-
});
-
}, function() {
-
$(this)
-
.unbind('mouseover')
-
.unbind('mouseout');
-
});
See It In Action
I've put together two simple demos that utilize both an event based and function based Live Query on unordered lists.
Get Live Query
Live Query can be downloaded from the Live Query project page. The Live Query documentation is a work in progress but the API is fully documented.

August 20th, 2007 at 8:30 am
Thanks Brandon!
Not only for this, but for all the good work you do and share.
One small thing you might like to now, when I click on any link in your blog in IE 6, an error is thrown.
Line: 18
Error: 'this.href' is null or not an object
The Microsoft Script Deugger shows:
/****** GA urchin.trackLinks 0.2
line 18:
addHandler(a[i], 'click', function() { urchinTracker('/outgoing/'+this.href.replace(/^http:\/\/|https:\/\//i, '').split('/').join('--')); }, false);
I'm looking forward to using this great plugin! TWB
August 20th, 2007 at 8:36 am
Thanks! That error in IE6 should be resolved now.
August 22nd, 2007 at 9:30 am
So glad to see this, I'd been working on something similar in ExtJS. This really is Behavior 2.0 and belongs in every JS library!
I had lots of trouble with DOM nodes coming and going, and will take a look to see how you solved that... Related note, how's perf? That was the killer for me, having to evaluate multiple selectors per DOM addition (and for that matter, being able to always identify DOM addition). I'm guessing you're putting the events on a higher node and counting on event bubbling to catch it?
August 22nd, 2007 at 9:45 am
Brandon, Looking quickly in the code I see you have a setInterval with a delay of 20. Is that how frequently you are reexamining the DOM for added and removed elements?
August 22nd, 2007 at 9:53 am
Thanks Steve.
Performance is a major concern and (after several iterations) with reasonable usage everything is good. The typical use-case should see no immediate performance hit at all. Although, in my testing I was surprised to see that Opera tends to stand out with performance issues when using lots of function based live queries that match a lot of elements on a very large (200k) DOM.
Of course users should always try to be as specific as possible with their selectors and not use it for the sake of using it. :)
I seriously can't wait to see what solution you guys and the other libs come up with.
August 22nd, 2007 at 10:00 am
@Peter: No way! :p That is for checking a very conservative queue. The selectors are not run until they have been queued up by a specific action. An action would be using one of the DOM manipulation methods found within jQuery or a registered plugin ($.livequery.registerPlugin).
August 22nd, 2007 at 10:09 am
So this the DOM reexamination is triggered by other jQuery calls in an aspect-oriented sort of way? That would be great as there is no delay where the element is visible but not yet had handler's attached.
It sounds like you are doing very little work at the end of each 20ms interval; however, using setTimeout is safer in general. If the action taken when each interval ends is longer than 20ms then the single threadedness of JavaScript means you could lock up the JavaScript with your code as there are more things waiting in line to be run. If you use setTimeout after each time you check the queue then there is a guarantee that other JavaScript can get in line and be executed.
August 22nd, 2007 at 10:13 am
@Peter: Exactly! Thanks for the tip on using setTimeout instead of setInterval ... I'm going to look into that.
August 22nd, 2007 at 11:03 am
As for Opera: Depending where the slowdown is, reacting to DOM Mutation Events could help.
August 22nd, 2007 at 11:08 am
It should also be noted that various tests have shown that 20 ms is the maximum resolution you can get out of current generation browsers. In other words, if you want to play it safe you should increase the interval slightly to 25 or even 30 ms.
August 22nd, 2007 at 2:32 pm
It looks like you've made some nice optimizations with the queue so DOM traversal only needs to occur after all DOM modifications are finished: for example, multiple appends in the same block of code. Very nice.
August 22nd, 2007 at 2:49 pm
@Peter: That is correct! Thanks. :)
August 22nd, 2007 at 10:47 pm
Brandon, why is it you poll the queue? You know exactly when something is added to the queue. When the first item is added to the queue do a setTimeout. When the callback runs the queue will be emptied. Then when the next first item is added to the queue the cycle automatically repeats. Seems like a lot of polling for something you completely control.
What is the purpose of $.livequery.running? JavaScript is single threaded and you are only using that running flag inside one function. When that function is running, another "thread" will never try to run it at the same time and cause some sort of race condition on the queue array.
Apologies for the bother if I'm simply not reading your code with due care. I've never read jQuery code before and am half blurry eyed from a day of programming.
I've been thinking about this plug in on and off all day. The general problems surrounding unobtrusive JavaScript have been percolating in my mind for months. The indirect access to DOM manipulation provided by jQuery gives you the necessary hook to pull off the persistence part of unobtrusive JavaScript. That's great. Most browser scripting solutions involve one level of indirection. It is like you are providing IE CSS expressions in multi-browser way. The window.onload problem is still the big hurdle. The Dean Edwards hack just isn't enough for me. The problem is solved legitimately for events that bubble by using event delegation but not for non-bubbling events like focus. It sure would be nice to have the grand unification for unobtrusive JavaScript with the persistance of your plugin and a real window.onload solution.
August 22nd, 2007 at 10:51 pm
Nikola: do you have data that confirms this? The only information I ever read on the subject indicated 10ms as the minimum [1].
1. http://webkit.org/blog/?p=96
August 23rd, 2007 at 6:05 am
@Peter: Thanks for the great feedback! You bring up some really good points. As soon as I get the chance I'm going to test out your ideas (and try to remember what lead me to do some of the things you asked :). I suspect you are right on and these will be nice improvements to Live Query.
August 24th, 2007 at 8:15 am
What about using Event Delegation instead? Look for events on the BODY element and find where they've bubbled up from. This would work fine for clicks, though I don't know about hovers.
August 24th, 2007 at 8:24 am
Live Query is not just about events. It also supports function based Live Queries which provides a lot of flexibility. BTW, I've removed the setInterval based on Peter's suggestions and plan to do another release right after I finish the test suite.
August 25th, 2007 at 11:34 am
Brandon, I've been thinking (NB not testing) about the performance of your plugin. Each time the DOM is modified you remake all live queries. This means running each query over the whole DOM. I know this is old data now http://dojotoolkit.org/node/336 but there are many queries that take ~400 seconds or more. If just 10 of these are live queries then with your plugin won't the browser freeze up for 4 seconds? That is a long time. Have you done any testing on big pages that use live queries extensively?
August 25th, 2007 at 4:44 pm
Peter, There would be performance issues if 10 Live Queries are running and matching the same elements that were being matched in the speed tests. I've been testing on a what I would consider a large DOM (200k) with 3 Live Queries that match a lot of elements. I've seen no performance hit what-so-ever. Of course, once additional Live Queries are added and more elements are matched, the performance starts to decrease accordingly. However, the plugin works absolutely wonderfully for the typical real-world scenario where a couple of Live Queries are running against a particular piece of the DOM. The same rules apply to this plugin as they do to any framework, library and/or helper. If performance becomes an issue, start optimizing. Optimization can happen on many different levels. For example, just providing a context to a selector can really boost performance. If someone really is using 10 Live Queries on a large DOM, there is probably a better solution. Live Query is not a replacement for bind but rather an extension for when future elements need to have events bound as well.
November 28th, 2007 at 4:16 pm
Thanks Brandon. I had been trying unsuccessfuly to get jQuery to bind event handlers to divs I had created on-the-fly using jQuery's 'append', with no success. Eventually I found a reference to your plugin on the jQuery FAQs, and it fixed the probem straight away. Thank you!!
December 28th, 2007 at 11:01 am
just stumbled onto this. Could of used this thing several weeks ago. Look forward to giving it a try. Great Work
May 1st, 2008 at 12:25 pm
Any chance of getting this to work with IE 5.5? I have tried some simple click functions from jquery and they work fine with 5.5 but break when I try and use them with livequery.
Thank you.