A Look at Sun.COM's New Event Delegation Library

(A followup to my last post.) We're on the brink of releasing a new JavaScript mini-library to sun.com, which we call the reg library. It provides an object named, naturally enough, reg. It stands for register. With it, you can register behaviors, like this:

reg.click('ul.foo > li > a.bar', myFunction);

Once that bit of code runs, regardless of whether the entire DOM has finished loading into the browser, click events on elements matching ul.foo > li > a.bar will be handled by myFunction, which is passed a reference to the active element and the event object. This happens without any DOM traversal, and without any events ever being attached to any <a> elements. Even if the entire contents of document.body are subsequently overwritten by innerHTML, all those new element nodes implicitly inherit the correct behavior. No re-walking of the new DOM subtree ever occurs. No re-attachment of events ever occurs.

How is this even possible?

Two facts conspire to make this feasible. 1) The <body> (document.body) is available almost immediately. 2) Most events bubble. All you need to do is to stand on document.body, and you're privy to almost every event that occurs on the page, right out of the gate. No need to go seeking out the elements you need, they literally bubble their way to you. All you do is grab the event's target and ask the question, does this element, or one of its ancestors, match 'ul.foo > li > a.bar'? If so, run the associated function, if not, ignore it. This is really just event delegation, and it's nothing new, but we've made little use of it on Sun.COM before now.

Limitations, caveats, dangers

I harbor no illusions this is a perfect solution. There are limitations. Besides the fact that not all events bubble, much of time, behavior depends exclusively on preprocessing, especially if you're doing progressive enhancement. You don't want non-JS users to see useless expand/collapse signs or widgets laying around, so you build the widgets only if scripting is enabled. And the only time to build the widgets is onload. And the only way to build the widgets in the right place is... \*sigh\* traversal. Some of this can fortunately be avoided by relying as much as possible on CSS and making your widget styles depend on a dynamically-added class name, such as body.jsenabled, so there's some workaround potential at least.

There's also an inherent performance limitation. Sitting in the driver's seat on document.body isn't always a relaxing cruise through the countryside. It can easily turn into rush-hour gridlock, with a flood of events each demanding to be checked. For that reason, I dare not use this technique to handle mousemove events. That would cause a veritable torrent of events. Even mouseover events are iffy. We have the capability and it appears to work reasonably well, but time will tell whether it's really viable. Click events, on the other hand, because of their relative infrequency, are a good candidate. Of course, as we develop this thing further, we'll be looking for ways to mitigate performance risks.

So there you go

It isn't live yet, but hopefully will be soon. After this code has been chugging away out there in the wild and woolly world of production Sun.COM for a while, I may post more about this, what works, what doesn't, unexpected wins and losses, etc. Stay tuned.

Comments:

You got any code to show us? Otherwise I don't believe you. ;-)

Posted by Dean Edwards on February 12, 2008 at 09:52 PM MST #

I'll make another posting when it goes into production.

Posted by Greg on February 13, 2008 at 02:54 AM MST #

I recently wrote about this myself, it's pretty easy to achieve using jQuery:

(function ($) {
$.fn.behaviour = function (selector, fn) {
return this.click(function (ev) {
var el = ev.target;
if ($(el).is(selector)) {
return fn.call(el);
}
});
};
})(jQuery);

$(function () {
$('body').behaviour('ul.foo > li > a.bar', function () {
console.log('Clicked link: ' + this.href);
return false;
});
});

(From: http://remysharp.com/2008/02/03/poor-mans-javascript-behaviours/)

Obviously the function can/should be upgraded to handle more than just a click event.

Posted by Remy Sharp on February 13, 2008 at 05:45 PM MST #

When will it be in production?

Posted by Justin Meyer on February 13, 2008 at 06:18 PM MST #

Here's the prototype version:
var reg = {};
reg.click = function(selector, func){
Event.observe(document.body, 'click', function(ev){
var el = ev.target;
if(el.match(selector))
return func();
})
};
reg.click('ul.foo > li > a.bar', myFunction);

Does anyone have match/is functionality outside a framework? I want to add this to JavaScriptMVC's controllers.

Posted by Justin Meyer on February 13, 2008 at 06:53 PM MST #

Greg,
why have you chosen to use event bubbling on the body instead of using the documentElement or the document itself ?

The "documentElement" is always present (for what concerns javascript), it is capable of accepting the same methodology and since nobody uses it, it is the perfect choice to be able to write a generic cross-browser and frameworks agnostic solution that could be applied even out of jQuery.

Why should we check the body exists, why wait for the page "onload" event, or why do we need wrap this in $(function(){}) or $(document).ready() blocks ?

Posted by guest on February 13, 2008 at 09:23 PM MST #

Sorry, forgot to name the post above...

This sounds like there are new objectives for Javascript and the Web...

@Remy (and believe the same is for Justin/Prototype/match)
jQuery is() method works perfectly for the test, but is unsuitable for the task described here, it is too slow, we want to speed things up not down.

Don't misunderstand me here. The is() method was just designed to solve other tasks and now tentatively adapted to another kind of task requiring much more power from the primitives. There is no need at all to loop on a set of elements to just match the properties of ONE element through CSS.

The is() method calls multiFilter(), which in turn, inside a while, calls filter() and merge() going through more parsing and looping and more conditionals.

Just open Firebug and do some profiling to see what I mean. Particularly look at the merge() method which is one of the last of the above chain.

To achieve what is described above a really professional jQuery plugin already existed since mid 2007. It's name is LiveQuery from Brandon Aaron which is the best that could be done for jQuery, even in term of speed and features.

Posted by Diego Perini on February 13, 2008 at 09:31 PM MST #

@82.54.117.18 - Thanks, that's a good idea. Assuming it works, that's what I'll do.

Posted by Greg on February 14, 2008 at 04:50 AM MST #

Diego,
Excuse my ignorance in jQuery, but what is LiveQuery's trick for getting the speedup? I looked in livequery but could not instantly understand it.

Posted by Justin Meyer on February 14, 2008 at 05:19 AM MST #

LiveQuery doesn't work like reg. It relies on having to use jQuery's DOM manipulation methods. After you use something like

$('#thing').append('<p>asdf<p>');

It sets a callback that will update all your event handlers.

So if you use something like document.getElementById('thing').innerHTML = ' ... ', it will not work. Boo.

Btw, is it possible, using some VERY rarely used css property to set css like:

ul.foo > li > a.bar {someRareProperty: someRareValue}

then check if the target's value is someRareValue. And then set back the css to:

ul.foo > li > a.bar {someRareProperty: default}

?

Posted by Justin Meyer on February 14, 2008 at 06:02 AM MST #

@82.54.117.18 - Think I hit a snag with attaching the events directly to document instead of document.body, something subtle, and only in FF2/Mac. Weird.

Posted by Greg on February 14, 2008 at 07:01 AM MST #

@Justin - We're testing the hell out of it right now, seeing if it can handle all of our legacy stuff. Squashed a couple bugs over the last 24h, and they're getting fewer and farther between, which is good, we're aiming for the end of the month. I'll make a post when it's in production.

Posted by Greg on February 14, 2008 at 07:05 AM MST #

Here is that function using computed style (works in FF only)
reg = function(event, selector, func){
Event.observe(document.body, event, function(ev){
var el = ev.target;
var option = {js :'outlineWidth', css: 'outline', value: 'solid 1px'};

var before = window.getComputedStyle(el, null)[option.js]
document.styleSheets[0].insertRule(selector+' {'+option.css+': '+option.value+';}}',1)
var after = window.getComputedStyle(el, null)[option.js]
document.styleSheets[0].deleteRule(1)
if(before != after)
return func.call(el);
})
};

Posted by Justin Meyer on February 14, 2008 at 07:45 AM MST #

Greg,
what I suggested was attaching the events to "document.documentElement" which ought to be the HTML element, I use that one in my current code and I stressed the tests on various browsers and platforms.

@82.54.117.18 was actually me again forgetting to type my name/info.

I haven't seen your code yet but since you are at it an important feature is being able to handle the bubbling phase of those event that normally do not bubble like "focus" and "blur" for example.

Could you please write more details about the behavior or error you found on FF2/Mac so I can also check if that is a concern or happens in my code using "document.documentElement" ?

Do you have code to wait for the body to be present in this scenario ?

Posted by Diego Perini on February 14, 2008 at 11:37 AM MST #

@Deigo - Attaching the events to just to document actually worked perfectly, except for some errors that seemed to be appearing onunload, of all things. I wasn't even able to replicate them, it was someone else on my team who was using ff2/mac. I couldn't get the error on either ff3/mac or ff2/win. The errors stopped when I switched back to document.body. My guess is almost certainly wrong, but it almost seemed like functions were being called after they were garbage collected or something. I'll try documentElement as you suggest. Currently we're doing something like:

function setupDelegates() {
if (!document.body) {
window.setTimeout(setupDelegates,100);
} else {
// attach events here
}
}
setupDelegates();

(sorry for the loss of indentation on the code)

Posted by Greg on February 14, 2008 at 01:53 PM MST #

@Deigo - we have focus and blur working, we just flip it to 'activate' and 'deactivate' for IE, and in all other browsers we set the focus/blur events for the capturing phase. So far it seems to be working.

Posted by Greg on February 14, 2008 at 01:57 PM MST #

@Diego - Sorry for spelling your name wrong :)

Posted by Greg on February 14, 2008 at 02:00 PM MST #

Nice Greg,
you are on the right track. I am eager to have a look at your code, and really hope the "document.documentElement" can work in your code, this will let you avoid that "YUI like" kludge (setTimeout) and your code can start even before body is available. yuk !!!

I would like your comments on the code I pointed out in your previous blog thread, when you have time, no hurry, you can contact me OT if you prefer.

Thank you for sharing your thoughts, cooperation as always is the winner.

Posted by Diego Perini on February 14, 2008 at 07:59 PM MST #

Sorry for all the posts. I spent the last day implementing it with the css trick across IE, Opera, Safari, and FF. It's working really nice in everything but Opera.

But, I realized that listening on body/document won't work the same as addEventListener. AddEventListener calls back on events on the element it's attached to and all elements inside that element's events.

This means if you have html like &lt;div&gt; &lt;p&gt; &lt;/p&gt; &lt;/div&gt;

If you click on the p tag, AddEventListener will call the div's event handlers with 'this' as the div element.

I hopefully am wrong, but I don't think it is possible to call the appropriate 'this' in that case (w/o searching the document which would be too expensive).

My css method can call the event handler with events on the P tag; but I can't see a quick way to extract the correct element for 'this'.

Any ideas?

Posted by Justin on February 15, 2008 at 02:21 PM MST #

@Diego - So yeah doing this stuff there's a bottleneck matching elements to selectors. Not because matching is slow but because it occurs so often. 'a.foo' has to fire when a span inside a.foo is clicked, so it can't just match the target, it also has to match up the chain of ancestors. If the target isn't contained in any selected elements, it searches all the way to the top for nothing.

So as you've already discovered this is where trying to implement powerful CSS3 or xpath-like selection gets cumbersome. Our selectors err on the simple and (hopefully) fast side and don't pretend to be true CSS3, especially in how they select attributes. And in any case we haven't badly needed fancy selectors... yet.

Posted by Greg on February 15, 2008 at 04:07 PM MST #

@Justin,
in W3C terms you have an "event.target" and an "event.currentTarget", in this scenario the "this" keyword may be a reference to "event.currentTarget" or to "event.target" depending on the PHASE you are handling. I don't want to even try to say by memory which are the equivalences and in which phases but you may find references easily. Does this sound tricky enough ?

Hope to have correctly understand/answered your main question though.

Posted by Diego Perini on February 15, 2008 at 08:15 PM MST #

Greg,
my turn to say I am sorry to abuse your blog...really.

> Not because matching is slow but because it occurs so often.

The truth is that both of them are real bottlenecks, matching being the worst for each framework that I know because as you noticed it is done recursively many times, on many elements and testing many properties,
in case of "mousemove" hundreds times a second.

I am still not able to say this "event delegation" methodology is suited for events like "mousemove", but after looking at how my "match()" method work we can also talk about that being feasible, and I will show you how.

You are not forced to use a full CSS3 selector, in my code I have a fall back to just check and match element properties so instead of passing a selector string like "div#test" you just pass an object like this:

{
id: 'test',
tagName: 'div'
}

Which is exactly the same, without needing the "NWMatcher" part of my project, in this way one can choose to include the CSS3 part only if really needed (lazyload approach).

The above is the old NWEvents Manager method I have been using since 2006, until in Auguts 2007 Peter Michaux helped introducing me to much faster "compiled selectors" which is why I then added the CSS3 extension.

Since the two pieces of code are really as framework agnostic as I could write, it should be easy for you to quickly setup some aliasing of your current event methods and just give it a try. It works on Konqueror ;-)

Posted by Diego Perini on February 15, 2008 at 08:59 PM MST #

@Diego - Ah, then I think we're already on a similar trajectory. (If I understand correctly.) Our selector objects are "compiled" and cached for subsequent use, similar to what you described. So:

var sel=new Selector('div.foo > a, div#bar + a@href\^=".html"')

parses approximately into the internal form:

[[{tag:'div',class:'foo'},{name:'child'},{tag:'a'}],[{tag:'div',id:'bar'},{name:'following-sibling'},{tag:'a',attName:'href',attVal:'.html',matchType:'\^='}]]

And matching is done like:

sel.matchesElement(elmt)

Posted by Greg on February 16, 2008 at 02:53 AM MST #

The exact internal form is probably a bit different, this is just from memory on a bleary Saturday morning :)

Posted by Greg on February 16, 2008 at 02:56 AM MST #

Actually that example would make more sense with '$=' instead of '\^='.

Posted by Greg on February 16, 2008 at 03:03 AM MST #

Without using the CSS3 selector engine I can only resolve properties matching. So you pass a list of properties you want the element to match and if that resolve to true the user handling function is called:

{
className: 'foo',
checked: true
}

so no relationship between elements. No selectors no party.-

In case the CSS3 engine is added the selector you mentioned:

div.foo > a, div#bar + a@href\^=".html"

is converted directly to a compiled Javascript function (element as arg):

function(element){
...see if element matches the selector
...return true/false
}

This function is then cached for subsequent calls, it will never change.

If this event happen several time in the life of the page, as for example "mouseover,mouseout" there will only be one CSS parsing of the selector, also events will be fired hundreds of times.

This is the biggest saving in using "compiled selectors", especially those functions does not need to go through all elements in the page but just compare the list of passed properties with those of the element currently scrutinized.

You see, methods may scale differently on differently sized pages.

Well if I have to tell all the truth, I haven't get strong support about this idea, most comments have been hostile, but that have just helped me shape this idea better.

Posted by Diego Perini on February 16, 2008 at 05:02 AM MST #

Hmm, not sure I follow. Do you mean that the \*results\* of the function are cached? And if so, what if the structure of the DOM changes in the meantime?

Posted by Greg on February 16, 2008 at 05:35 AM MST #

>Hmm, not sure I follow. Do you mean that the \*results\* of the function are cached?

No not the result of the function, only the function itself is cached for reuse.

>And if so, what if the structure of the DOM changes in the meantime?

That is a good question for the "select()" method that produces a collection of elements as result, in that case I have a configurable caching system but that part have nothing to do with the "event delegation" we are talking about.

For event delegation I use the "match()" method which does not cache results in any way.

Posted by Diego Perini on February 16, 2008 at 06:21 AM MST #

Greg,
You should consider my CSS technique. It handles mousemoves matching 30 different CSS selectors perfectly fine. And, you can use any CSS selector that the browser supports. I'm rewriting JavaScriptMVC's controller with this technique.

On my question, I'm not sure if you answered it, or if I'm misunderstanding your answer (I'm probably misunderstanding). CurrentTarget returns a reference to the node that processes the event. With your method, I believe this would be body or documentElement. Target returns the element that was actually clicked.

If I wanted a function to be called when a hyperlink 'A' tag or any of its nested children tags are clicked on, I would expect to register a function like:

reg.click("a", function(){ alert(this.nodeName == 'A')}

and, whenever an "A" tag or any of its children are clicked on, it would alert "true".

With your method, it's still not clear to me how clicking a nested 'IMG' tag for example would even be alerted, let alone alert "true". This is because currentTarget would be body, and target would be IMG. This event wouldn't be matched by "a", so no event would be called.

My CSS method does manage to call the alert, but with target the IMG element.

Am I missing something? Thanks for your help and for letting people know about this clever technique.

Posted by Justin Meyer on February 17, 2008 at 05:35 PM MST #

@Justin - Okay I think I understand the question. Here's how it would look:

reg.click("a", function(a,e){ alert(a.nodeName=='A'); });

In other words you wouldn't use the 'this' keyword at all, it just passes the element as an arg. If you click on an image within an A tag, it searches the ancestor chain until it finds the A element. So it will still alert true. I've got a post in the works explaining how that works, plus some optimization we've put in. Sorry for saying "Real Soon Now" so much, but that post should be live soon :)

Posted by Greg on February 18, 2008 at 02:22 AM MST #

@Justin - When I saw "works in FF only" I admit I didn't pay much attention. But I gave it another look. So, wow, that's either a highly irresponsible piece of code or an extremely brilliant piece of code :) Have you profiled it on real world web pages? I'd be curious to see what the average runtime is. Sounds like it works well, though, if you're using it for mousemove. One question, what if there are hundreds of elements that match the same selector, wouldn't the browser struggle to recompute all their styles for each match? Currently, my matching function takes on average about 0.041 ms according to Firebug, so my main concern at this point is how often it runs.

Posted by Greg on February 18, 2008 at 02:38 AM MST #

Haha, I was thinking the same thing about my CSS trick. I've got it working in the other browsers. However, I haven't found the 'perfect' CSS attribute for Safari and Opera like I have for FF and IE.

I'm in the process of adding it to our FTP client -> http://fit.jupiterit.com.

It probably wont get to hundreds of elements, but it's a really good real world test.

Is your matching function O(1) with the number of elements? I would imagine it would have problems too as the number of elements gets very large.

If speed isn't a problem with mine. It does have a big Gotcha however. It relies on being able to change the font-family for specific elements.

For IE and FF, I append JAVASCRIPTMVC to the font list for the selector.

I grab or create a body id in an attempt at creating the highest precedent possible and insert the rule like this:

body#body_id .todos a {font-family: Arial, JAVASCRIPTMVC}

But, this method won't match if someone uses a higher precedent CSS selector. But, in my opinion, this is unlikely. I've also thought of checking the styles before hand to make sure this is safe.

I'll keep you posted on how this goes.

Posted by Justin Meyer on February 18, 2008 at 03:47 AM MST #

0.041 ms! That's really fast. On human time scales (40 ms), you can run your match function about 1000 times? That seems fast enough for mousemove to me.

How many elements are you matching? Here's how fast my CSS method is:

10 elements: .502ms
100 elements: 3.430ms
1000 elements: 30.540 ms

So, around 600 elements, you will start to get occasional matches that are slower than 40ms and it will start to look bad.

And, matching 2 things:

10 elements: .602ms
100 elements: 4.982ms
1000 elements: 42.969ms

I'm measuring the total time it is in the callback function that the documentElement calls on mousemove.

I've commented out the part of the function that calls the actual event handler.

Posted by Justin Meyer on February 18, 2008 at 05:31 AM MST #

Actually, thinking about what you've written of your method. It probably doesn't get worse the more elements you match, only how nested the element you are matching is. Is this correct?

Posted by Justin Meyer on February 18, 2008 at 05:37 AM MST #

> Is your matching function O(1) with the number of elements

> Actually, thinking about what you've written of your method. It probably doesn't get worse the more elements you match, only how nested the element you are matching is. Is this correct?

I think so. I think it would be best case O(1) for flat DOM structures and worst case O(N) for maximally nested DOM structures, and for the average case probably O(log N) where DOM structures are tree-like. If you were devious enough you could probably create a selector in combination with a page and make the whole thing be O(N\^2) but I don't think it would occur very often in nature.

Posted by Greg on February 18, 2008 at 05:57 AM MST #

Also the 0.041 ms is just the average, plus I have a fast machine. For example if I have "div.foo div.bar span.baz" all it has to do is see if the nodeName == 'span', if not then return false. That ends up being the case most of the time. But in the rare case it is a span then it has to move to the previous item (div.bar) and compare that, so at that point the runtime gets a lot longer.

Posted by Greg on February 18, 2008 at 06:02 AM MST #

Oh and also check hasClassName(elmt, 'baz') before moving to check for div.bar.

Posted by Greg on February 18, 2008 at 06:12 AM MST #

Yeah, your method is better, so much for being sneaky. Are you going to make reg open-source?

If so, what license (MIT!)? And, is there anyway you can post your match method before you release it? It will help me avoid writing my own for the next 2 days.

I'm basically combining Dean Edwards behaviors library with reg. It would allow you to write JS 'controllers' like:

Controller('todos',{
click : function(params) { .... },
'a mouseover' : function(params) { ....}
})

Finally, wouldn't your method have to inspect the ancestors of clicked elements to see if they match 'div.foo div.bar span.baz'? This is related to the nested image problem. If an IMG tag was clicked inside a span.baz tag and you simply checked if nodeName == 'span', it would be false.

The only way I see of allowing this would be to traverse the parentNodes all the way to body to build a path, and comparing the CSS selector against that. But, I am probably missing something.

Posted by Justin Meyer on February 18, 2008 at 08:18 AM MST #

> Are you going to make reg open-source?

Actually one of my reasons for blogging this, other than to share ideas and get feedback, is to see if there'd be interest, i.e. whether it would be worth the fuss to get approval to release the code.

> Finally, wouldn't your method have to inspect the ancestors of clicked elements to see if they match 'div.foo div.bar span.baz'?

Yes.

Posted by Greg on February 18, 2008 at 12:26 PM MST #

Greg,
Are you able to handle submit events? I don't think they bubble in IE. At least not on documentElement or body.

Posted by Justin Meyer on February 19, 2008 at 04:30 PM MST #

The first version won't do submit events, only because we haven't added it to the code. We're starting out with click, mouseover, mouseout, focus, and blur. In future versions we'll tackle other ones like keypress and submit. Thanks for the heads up about IE though.

BTW - Can't make any promises at this point, but I've initiated a request for approval to release this code under an OSL. Incidentally, why the MIT license?

Posted by Greg on February 20, 2008 at 03:14 AM MST #

JavaScriptMVC is open-sourced under MIT and this would make a very nice addition.

How are you handling focus and blur if you can't listen on documentElement for them?

Posted by Justin Meyer on February 20, 2008 at 06:55 AM MST #

We actually can listen on documentElement for focus/blur, we just have to handle the event in the capturing phase, or in IE, listen to onactivate/ondeactivate instead of focus/blur.

MIT license is GNU-compatible, isn't it?

Posted by Greg on February 20, 2008 at 07:19 AM MST #

Ah, sweet! Do you know of a similar trick for submit for IE?

I might be wrong about this, but if Reg is GPL and I adopt code from it, the rest of my library has to be GPL. However, this is not true of LGPL.

For that reason, I avoid GPL, but LGPL is fine. I don't want to force people to keep GPL, just to keep the copyright.

Posted by Justin Meyer on February 20, 2008 at 08:03 AM MST #

For "focus/blur" I am also using capturing on FF/OP/WK, while for IE I have found that their IE cousins "focusin/focusout" does bubble up to document, then I decided to go with them.

I haven't yet found a real substitute for the "submit", just a small kludge by using the "mouseup" event instead and checking if the event target is an input element of type "submit". That will not work with a programmatical javascript call to form.submit(). However that's an IE bug.

I am very interested in your thought and suggestion on better ways to solve this on IE, on other browser the "submit" does bubble correctly.

Posted by Diego Perini on February 20, 2008 at 11:24 AM MST #

> Do you know of a similar trick for submit for IE?

Alas, no.

Posted by Greg on February 20, 2008 at 11:55 AM MST #

Reading Wikipedia, MIT license might not be an issue with GPL.

http://en.wikipedia.org/wiki/MIT_License

"The MIT License is ... GPL-compatible, meaning that the GPL permits combination and redistribution with software that uses the MIT License."

Posted by Greg on February 20, 2008 at 12:00 PM MST #

> I haven't yet found a real substitute for the "submit", just a small kludge by using the "mouseup" event instead and checking if the event target is an input element of type "submit".

Wouldn't this miss people hitting enter to submit the form?

> I am very interested in your thought and suggestion on better ways to solve this on IE

Seconded and thirded. Maybe some technique will be revealed.If I discover anything, I'll definitely blog about it.

Posted by Greg on February 20, 2008 at 12:05 PM MST #

Greg,
> Wouldn't this miss people hitting enter to submit the form?

Yes. The "mouseup" event was just for mouse handling, "keydown/keyup" must be used for keyboard handling.

Note that this is only a bug in IE and only with the "submit" event, the technique is not specific for handling IE form/event bugs but is a generic event delegation technique that is a big advance in javascript development.

Posted by Diego Perini on February 20, 2008 at 10:33 PM MST #

@Greg
They are compatible, but the problem still exists. It says in, http://en.wikipedia.org/wiki/GNU_General_Public_License#Compatibility_and_multi-licensing

'Many of the most common free software licenses, such as the original MIT/X license, the BSD license (in its current 3-clause form), and the LGPL, are "GPL-compatible". That is, their code can be combined with a program under the GPL without conflict (the new combination would have the GPL applied to the whole).'

I wouldn't want all the other work I've done to instantly have
to be GPL.

Posted by Justin Meyer on February 22, 2008 at 07:46 AM MST #

Ah. Well I'll definitely check into using the MIT license.

Posted by Greg on February 22, 2008 at 11:59 AM MST #

Thanks for that. I never been comfortable with the viral nature of GPL.

I'm having trouble with focus/blur. I've set it to capture, but it calls my observer multiple times when I click the element once. Are you canceling the event after you callback the passed in event handler?

If you are doing this, would there be any problems with it?

Posted by Justin Meyer on February 22, 2008 at 08:41 PM MST #

Actually, there must be a gap in my understanding of how focus event is handled. For example, I attach an event to in input like:

document.getElementById('input').addEventListener('focus', function(){log('focused')}, false);

The first time I focus on the element, it writes 'focused' once, the second 2 times, the third 4 times, the 4th 8 times, etc.

I couldn't really find anything about this. But it happens in FF, Safari, and Opera, so I'm assuming it's expected.

Posted by Justin Meyer on February 22, 2008 at 09:10 PM MST #

Greg,
Please ignore my craziness. I figured out my problem. I need to get some sleep.

Posted by Justin Meyer on February 22, 2008 at 09:50 PM MST #

Greg,
I put together a demo of my little version of Reg here:

http://www.javascriptmvc.com/learningcenter/controller/demo.html

I've got submit, focus, and blur working across all browsers. For onsubmit in IE, I check for click on a submit button, or a return keypress in an input element.

You have to do click and keypress or you can't cancel the form submission.

Posted by Justin Meyer on February 25, 2008 at 04:08 AM MST #

Hi, to Diego's post about perfomance:(February 14)
There is a plugin for event delegation, that doesn't have great overhead.
It's called jQuery.Listen: http://flesler.blogspot.com/2007/10/jquerylisten.html
It has one limitation( nothing's perfect :) ), the selector support is low.
The selectors are indexed with hashes so the access is virtually instantaneous.

Posted by Ariel Flesler on February 26, 2008 at 09:36 PM MST #

Ariel,
Does jQuery even support event capture in it's default event registration functionality?

Posted by Justin Meyer on February 27, 2008 at 08:02 AM MST #

Hi Justin
jQuery doesn't support event capture. The reason (I suppose) is that it can't be used on IE.
Now that you mention 'event capture' (sorry, off-topic), it has been used to simulate cross-browser bubbling for blur and focus.
Thanks to the new $.event.special feature by Brandon Aaron(jQuery 1.2.2), Jörn Zaefferer was able to make a small plugin named 'delegate', that he used for his Validate plugin. He kindly lent me the focusin/focusout part, and I integrated it into Listen. So it now supports blur and focus (what a relief).
So no.. jQuery doesn't support event capture natively.

Posted by Ariel Flesler on February 27, 2008 at 09:03 AM MST #

Ariel,
thank you for letting us know. From the code I have seen your approach is aiming at the same, I mean using the "document" as the delegate in absence of a rendered element supplied in the registration of the event. You will have noticed I used "document.documentElement" instead. ;-)

Two are the things that makes this complicated in jQuery:

- no support for event capturing (in jQuery substituted by "data" ?!?)
- no support for fast element matching (but this is for any library)

For this reason more code is needed, extra to those 30Kb of jQuery, and you still have a non performant and non scalable solution, achieved by stacking other plugins over and over.

Ariel, you know I like jQuery, though some decisions made in the past are driving it out of MY standards, W3C says there is a capture flag in the setup of events, it is of no importance if only one browser (IE) isn't implementing it correctly, this is not a good reason to amputate browsers correctly implementing it. I have repeated this several times, somebody else listened and believed in the capture...just wait.

All browsers except IE follow the W3C recommendations regarding events, there is no need to go through the .fix() for all browsers just to setup a pair of coordinates and mouse keyboard state for each event type. This is slowing down all browsers in favor of IE.

I still like the work John has made, his contribution to the shift is great, his chaining and shortness is unique in the world of frameworks, but there is space to improve, and hopefully to go back with some decision.-

Posted by Diego Perini on February 28, 2008 at 02:04 AM MST #

Hi Diego, I'll reply in order.

\* Do you think there's a difference between using document and document.documentElement ?
My vision of the plugin was really to use it globally, independent from document ready and window.load.
That's why I added a shortcut ($.listen) so people could simply bind, ignoring events, listeners, etc.

\* What's the relation between event capturing the data of the handlers ??
\* jQuery.Listen doesn't do selector matching, it does the opposite proccess. I'd say, selector decoupling. It's not about a selector matching an element, but about an element matching or not, a selector.

\* As for event capture... I disagree. I wouldn't measure it by amount of browsers but amount of users. IE can be an awful browser, but it's the most used world wide, that's a (sad) fact. Hopefully time will change that.
By ignoring IE, you are not letting down one browser, but most users in the world...

Well, I'm not saying anything new.. I suppose the best approach (the most complicated though) is to have different files for IE and the rest. Maybe a clean file, and another one patching the original. This way only IE suffers the overhead.. still, you are slowing your site for most visitors :)

Posted by Ariel Flesler on February 28, 2008 at 02:41 AM MST #

It was meant to be
What's the relation between event capturing AND the data of the handlers ??

Posted by Ariel Flesler on February 28, 2008 at 02:44 AM MST #

Ariel,
I was just saying that the last parameter of W3C addEventListener is a boolean true/false that allows handling of events in the capturing or bubbling phases, while in jQuery that parameter has been replaced by a "data" parameter that can be passed during registration.

Regarding IE, I do not forget it in my code, I just fix it, so users are not forgotten, none of them, any browser, the fact is that we are suppressing very nice functionality in other newer W3C browsers and in my view that is the worst we can do.

Without forgetting new capabilities in W3C browser, my event manager can fix the wrong things for IE, I don't go through a .fix() in every browser like in jQuery. It is not necessary the W3C events are fixed (almost).

Your idea of implementing an IE patch to be loaded after jQuery core is loaded is a great idea. I will vote for it. That will make everything wonderfully fast.

Posted by Diego Perini on February 28, 2008 at 08:40 AM MST #

Post a Comment:
Comments are closed for this entry.
About

My name is Greg Reimer and I'm a web technologist for the Sun.COM web design team.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today