I'm sure that this has been talked about before, but I'm wondering why event delegation hasn't been used for some of the main angular DOM events such as onClick, onDblClick, onMouseDown, onMouseUp, etc...
If we delegate each of these events then there would be a nice performance upgrade for angular since each of the ngEVENT directives won't have to register their own copy of the DOM event.
This can be done by having one event for each type and then figuring out the in betweens with some JavaScript CSS selectors and scope checking.
I forked over the code, installed the test dependencies and looked around in the repo to find where the onClick directive is set. So far so good, however, turns out that jQLite (the angular one) doesn't include the .delegate (or .live) jquery function in it.
I'm sure I could get this to work since and if anything doesn't work then it looks like there is plenty of test specs to catch the bug(s).
So could we start by adding element.delegate to JQLite?
there would be a nice performance upgrade
I'm not convinced that this would yield better performance in practice. In your example, there would be extra work involved if you listen to determine which element was actually clicked and evaluate the expression on that scope. I'd love to be proven wrong, though. Do you have some benchmarks that suggest that this is performance-critical?
If you include jQuery on the page, AngularJS will automatically use that instead of JQLite, giving you element.delegate and element.live. You can use this to test your theory.
I've done a lot of work building my own AJAX websites and the major speed boost I got was when I changed click handlers down to delegated events that are attached onto the document.body element. So let me first build a comparison test.
In regards to JQuery being included:
element.delegate and element.live then it would have to be in it's core and not only offered as a 3rd party feature.Iet me first make a comparison of the two and then we'll see what we can do from there.
Hi matsko. Did you make the comparison? I'm looking forward for it.
I am curious what y'all think of this delegate directive?
http://www.bennadel.com/blog/2448-Using-jQuery-Event-Delegation-In-AngularJS.htm
Would be interesting to see a ng-delegate-click ... or if I am missing something where angular doesn't see perf issues with event delegation, I am very interested in hearing/reading about that.
I actually discovered that $rootElement works already as a big delegator for click events. So any a elements will get caught up by the click event on the root element, but ngClick listens per element. So I actually think that this is good as it is, because if you have everything captured on one element and ngClick is still around then the developer has to capture the ngClick event before the global aLink event is caught--which is messy. But I do think we should add element.delegate to the core.
+1 for this issue.
@btford, If you are looking for any comparisons between bind and delegate then I have created a Bind vs On vs Delegate test on jsperf. As you can see, delegate is the clear winner here and this margin increases as number of DOM elements are increased.
_Note - Before creating this test, I was not aware that latest jQuery eventually invokes on for both delegate and bind. But I guess it does not matter here as jQuery Lite used in angular does not have implementation of on_.
master branch does contain an implementation of on in jqLite
On 17 July 2013 14:38, Nihar Sawant [email protected] wrote:
+1 for this issue.
If you are looking for any comparisons between bind and delegate then I
have created a Bind vs On vs Delegatehttp://jsperf.com/bind-vs-on-vs-delegatetest on jsperf. As you can see,
delegate is the clear winner here and this margin increases as number of
DOM elements are increased._Note - Before creating this test, I was not aware that latest jQuery
eventually invokes on for both delegate and bind. But I guess it does not
matter here as jQuery Lite used in angular does not have implementation of
on_.—
Reply to this email directly or view it on GitHubhttps://github.com/angular/angular.js/issues/1571#issuecomment-21113129
.
Can you be more specific about the version? I am using 1.0.7. I want to switch to 1.1.5 (as it has ng-animation) but being unstable I am not implementing it. Any idea when 1.1.5 will go stable?
It was landed in this commit:
https://github.com/angular/angular.js/commit/f1b94b4b599ab701bc75b55bbbbb73c5ef329a93
on
19 June. This is after the most recent release of 1.1.5. We are pushing
hard to release 1.2 very soon.
On 17 July 2013 14:55, Nihar Sawant [email protected] wrote:
Can you be more specific about the version? I am using 1.0.7. I want to
switch to 1.1.5 (as it has ng-animation) but being unstable I am not
implementing it. Any idea when 1.1.5 will go stable?—
Reply to this email directly or view it on GitHubhttps://github.com/angular/angular.js/issues/1571#issuecomment-21114242
.
Does not support namespaces, selectors or eventData :worried: If selectors are not supported then event delegation won't work, isn't it?
Yes you are right.
On 17 July 2013 15:10, Nihar Sawant [email protected] wrote:
_Does not support namespaces, selectors or eventData_ [image: :worried:]If selectors are not supported then event delegation won't work, isn't it?
—
Reply to this email directly or view it on GitHubhttps://github.com/angular/angular.js/issues/1571#issuecomment-21115275
.
Is there any particular reason why on is implemented partially? Otherwise I will start looking for alternatives.
@dewbot a comparison in a vacuum doesn't tell us anything. Using event delegation may introduce additional work that may be more expensive than whatever event delegation affords us. You need to benchmark it in the context of ngClick to know if it's worth it.
Hi @btford,
Sorry for the late reply. For last couple of days, I have been trying to create a new test in jsPerf for the comparison between bind, delegate and ng-click but unfortunately I can't create a proper one. Can you provide me some ng-click based existing tests if you are aware of any? Or if it is possible, can you take some time off to create new revision of the test I made? Or can you suggest me any other ways to do performance testing with (like using DevTools)?
Thanks
@btford anything we can do about this?
@tbosch has a branch that adds event delegation to all ng-* directives. https://github.com/tbosch/angular.js/compare/delegate-events it's still a bit rough, but if someone would like to evaluate it on a real app then please do and post feedback here. (we are primarily interested to know if it has perf/memory impact)
if we are going to do this we should also add event delegation to a directive: https://github.com/angular/angular.js/blob/75345e3487642fbc608c3673e64cd7c5d65cb386/src/ng/directive/a.js#L37 (that should be a separate PR though)
@matsko you said that $rootElement already works as event delegator, but that's only for link rewriting.
additionally there are other events that should be delegated that were not mentioned here. primarily all the events in forms (input[*] directives)
@IgorMinar yes. $rootElement handles the click operations for all children on itself and then makes the connection to the clicked element. But this is very coupled into $location so you can't use it on your own.
Hi,
I investigated event delegation for AngularJS with the following results and benchmarks:
ngClick, ...: directives that evaluate an expression when an event happens$location: Already implements event delegation using the click event (see here).a[href]: Has a link function to prevent clicks on empty links (see here). The link function could be removed with event delegation in $location.ngModel: the link function of the directive cannot be removed, only the installation of the event listener.touchstart event listener on document can cause jank during scrolling (see here).touchstart handler to see whether it event.preventDefault()ed before it can start scrolling. Same problem when using gesture events from a library like ngTouch or hammer.js: Installing a gesture listener for e.g. tap on the document requires the library to install a touchstart event listener on the document as wellel.addEventLisener('click', function() {}, false). See here.touchxxx events and a gesture library like ngTouch or hammer.js to get faster feedback to user input (e.g. no 300ms delay)blur and focuschange event is stopped.click to determine which elements are clickable, as touch gestures are not exact (a finger is actually an ellipsis, i.e. a fat pointer). See heretouchstart as it always uses the center of the finger...This benchmark compares ngClick to a possible event delegation directive. It measures how long it takes to instantiate directives.
Results (given the current master of AngularJS 1.x and in Chrome M37):
ngClick is very close to ngShow and text interpolation, especially when looking at a version of ngClick that does not use jqLite.on but element.addEventListener directlylink function has the same speed as a directive with a link function. I.e. ngClick is slower compared to the delegate event directive only because ngClick touches the DOM for every elementngClick. However, the overall performanceBesides the micro benchmarks we also tried delegation event directives in one of our big AngularJS apps to reduce startup time and save memory. However, in our tests it did not make a huge difference there as well.
Removing the event handler closure in ngClick is 130 Bytes (40000 listeners == 2.5MB).
Given the master of AngularJS 1.x and in Chrome M37.
We will not implement event delegation in AngularJS 1.x as there would be a lot of special cases, which also depend on other libraries that are used, and the speed and memory impact is not big enough for this. E.g. the click problem in mobile safari would result in no performance gain there, as ngClick would need to install those empty event listeners. However, when Fastclick is used, we don't need those listeners as Fastclick patches the DOM prototypes to use touchstart and touchend under the hoods, which can lead to jank as they would be installed on the document due to event delegation.
However, for Angular 2.0 we will revisit this topic.
Please let me know if I missed something important.
Please also let me know if you have a good real world use case for which you did performance tests that show that an event delegation directive in AngularJS would have a big impact.
/cc @IgorMinar
Most helpful comment
Hi,
I investigated event delegation for AngularJS with the following results and benchmarks:
Possible directives that could use event delegation
ngClick, ...: directives that evaluate an expression when an event happens$location: Already implements event delegation using theclickevent (see here).a[href]: Has a link function to prevent clicks on empty links (see here). The link function could be removed with event delegation in$location.ngModel: the link function of the directive cannot be removed, only the installation of the event listener.Possible benefits
Possible problems
touchstartevent listener ondocumentcan cause jank during scrolling (see here).Reason: The browser needs to call the
touchstarthandler to see whether itevent.preventDefault()ed before it can start scrolling. Same problem when using gesture events from a library likengTouchorhammer.js: Installing a gesture listener for e.g.tapon thedocumentrequires the library to install atouchstartevent listener on the document as wellel.addEventLisener('click', function() {}, false). See here.Note: the real click event is probably not very important for mobile apps as they want to use the
touchxxxevents and a gesture library likengTouchorhammer.jsto get faster feedback to user input (e.g. no 300ms delay)blurandfocusWorkaround: could have a black list or use capturing event listeners
changeevent is stopped.Note: This is not an issue for AngularJS 1.x, but will be important for AngularJS 2.x
clickto determine which elements are clickable, as touch gestures are not exact (a finger is actually an ellipsis, i.e. a fat pointer). See hereNote: The reference above suggests this is not an issue for
touchstartas it always uses the center of the finger...Performance benchmark (#8432)
This benchmark compares
ngClickto a possible event delegation directive. It measures how long it takes to instantiate directives.Results (given the current master of AngularJS 1.x and in Chrome M37):
ngClickis very close tongShowand text interpolation, especially when looking at a version ofngClickthat does not usejqLite.onbutelement.addEventListenerdirectlylinkfunction has the same speed as a directive with alinkfunction. I.e.ngClickis slower compared to the delegate event directive only becausengClicktouches the DOM for every elementngClick. However, the overall performanceof a use case depends on other factors as well, e.g. how many (and which) other directives are used and what other things are going on (e.g. compiling a template, ...).
Besides the micro benchmarks we also tried delegation event directives in one of our big AngularJS apps to reduce startup time and save memory. However, in our tests it did not make a huge difference there as well.
Memory benchmark
Removing the event handler closure in
ngClickis 130 Bytes (40000 listeners == 2.5MB).Given the master of AngularJS 1.x and in Chrome M37.
Conclusion
We will not implement event delegation in AngularJS 1.x as there would be a lot of special cases, which also depend on other libraries that are used, and the speed and memory impact is not big enough for this. E.g. the
clickproblem in mobile safari would result in no performance gain there, asngClickwould need to install those empty event listeners. However, when Fastclick is used, we don't need those listeners as Fastclick patches the DOM prototypes to usetouchstartandtouchendunder the hoods, which can lead to jank as they would be installed on thedocumentdue to event delegation.However, for Angular 2.0 we will revisit this topic.
Please let me know if I missed something important.
Please also let me know if you have a good real world use case for which you did performance tests that show that an event delegation directive in AngularJS would have a big impact.
/cc @IgorMinar