How to refresh listeners in Prototype
Lets say, you are making a site, were everything is AJAX, and you rely on event handlers to know when people double click on this element, drag the image across the site, etc. Normally you would start listening to things once the DOM fires (when the page loads), but problem is, it only fires once. Here is an example of a normal “onload” listener in Prototype.js:
Event.observe(window, 'load', function () {
$$('tr[rel="file"]').each(function(element) {
element.observe('click', function(event) {
new Ajax.Request('file_info.php', {
asynchronous: true,
method: 'get',
parameters: 'ajax=true',
onComplete: function(t) {
$('files').update(t.responseText);
}
});
event.stop();
});
});
});
So, once the window loads, tell every table row with the rel attribute named “file” to some things once its clicked (in this case, gets the file info). Problem is, with all the new table rows coming in, Prototype wasn’t told to listen to the new table rows, only the previous ones that are gone.
I spent about an hour trying to figure out how to re-load the observe above. With tension building, I finally found something in the Prototype API documentation. You can establish your own psuedo type observation, which you would call when the window loads! Here is an example:
document.observe('start:files', function() {
$$('tr[rel="file"]').each(function(element) {
element.observe('click', function(event) {
new Ajax.Request('file_info.php', {
asynchronous: true,
method: 'get',
parameters: 'ajax=true',
onComplete: function(t) {
$('files').update(t.responseText);
document.fire('start:files');
}
});
event.stop();
});
});
});
Event.observe(window, 'load', function (event) {
document.fire('start:files');
});
So when the window loads, it will load another observer. Although this seems redundant, it’s the only way to re-fire the listener when you need to. Let me note, that the start:files you can name it anything you want really, although it should have a : somewhere in-between it.


This is actually an overkill.
Event delegation seems more appropriate here.
Try this:
document.observe(’click’, function(e) {
var target = e.findElement(’tr[rel=file]’);
if(!target) return;
e.stop();
// send request, etc.
})
Best,
kangax
Comment by kangax — June 15, 2008 @ 12:17 pm
Won’t there be a memory leak over time, listening to every click and doing if statements all over? Instead of adding the listener to the actual element?
Comment by Garrett — June 15, 2008 @ 8:34 pm
I don’t see why there would be a leak. There seems to be no circular references created (if you are referring to IE’ s bug). I’ve been using event delegation for quite some time lately and performance gains are usually pretty noticeable.
- kangax
Comment by kangax — June 16, 2008 @ 1:52 pm
Maybe not a memory leak, but just the overall viewpoint of it…listening to every click where its not needed, over listening to double clicks on elements that are needed should be targeted that way. That is were I think the if statements are bloated and not needed.
Let me mention that I changed my double click listener to a simple single for this post.
Comment by Garrett — June 16, 2008 @ 2:30 pm
I see your point.
Event delegation is obviously not a silver bullet.
It could be very well not worth it, but performing a tiny operation on every click usually leads to a better performance (vs. having N event handlers in memory)
Comment by kangax — June 17, 2008 @ 11:32 am