Filtering window events by individual events on DOM nodes
Consider the following code example:
import R from 'ramda';
import {Observable} from 'Rx';
var allClicks_ = Observable.fromEvent(window, 'click').share();
var getClicks = function(klass) {
return allClicks_.filter(e => {
return R.contains(klass, e.target.classList);
});
};
getClicks('red').subscribe(x => {
render('RED: ' + x.target.className);
});
getClicks('blue').subscribe(x => {
render('BLUE: ' + x.target.className);
});
Instead of adding event listeners to ".red" and ".blue" window
events, I added an event listener to ".red" and ".blue" events and filtered events.
Now, what could possibly go wrong with code like this? Is this more (or less) efficient than adding event listeners to individual DOM nodes? Or does it have no performance advantage?
Edit: Share a hot observable so that only one event handler is connected.
source to share
This is an example of a delegated event handler. This template is very useful. So useful that libraries like jQuery
and dojo
have built-in support for this pattern (see the selector
jQuery.on and dojo.on argument ).
Adding event handlers to each DOM node is actually an O (n) operation whereas it is an O (1) operation. As the number of matching DOM nodes increases, the delegated event handler pattern gains more advantage.
What could go wrong?
-
If, in this case
window
, an event handler is set between your top-level element ( ) and your target elements, and this event handler is setev.stopPropagation()
, then your delegated handler will never see the event. -
If the filter function is too complex and slow, the browser will have to spend more time than usual using the filter.
-
You will receive events for DOM nodes added after adding event handlers. This is usually seen as a good thing. But if for some reason you didn't expect it, it might leave you.
Note that in your specific example, two handlers are actually registered click
. You can reduce it to one instance with share
:
var allClicks_ = Observable.fromEvent(window, 'click').share();
source to share