Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance: use event delegation #319

Merged
merged 20 commits into from
Oct 24, 2017
Merged

Conversation

Indigo744
Copy link
Collaborator

@Indigo744 Indigo744 commented Oct 3, 2017

Ok, this one is quite big.

Until now, we attached to each area/plot/link node the following event listeners:

  • mouseover (for hover attributes and tooltips)
  • mousemove (for tooltip position)
  • mouseout (for hover attributes and tooltips)
  • click (for href)

This can become quite a lot for big maps with lots of elements.

The solution is actually quite simple: use event delegation!
Basically, instead of attaching all event listener per node, we attach only one event listener to the container, and filter the child element with delegation.
More information: http://api.jquery.com/on/ and https://learn.jquery.com/events/event-delegation/

So the modifications are:

  • remove any event attachment for nodes ($().on()..)
  • implements each event listeners on container in initDelegatedMapEvents() and dispatch relevant action

For now, I haven't touched the way the custom eventHandlers works. They are still attached to each node according to the user option. What do you think?

Here is a test using one of the example with lots of plots and tooltips
Before:
Huge number of listener events
1113 events listeners, 21 MB JS heap
After:
Small number of listener events
4 events listeners, 15 MB JS heap

This may help for #304

I've checked all example without finding any regression. But I'll let you review this ;-)

@Indigo744 Indigo744 requested a review from neveldo October 3, 2017 11:11
@Indigo744
Copy link
Collaborator Author

Ah, I may need to update the tests

@neveldo
Copy link
Owner

neveldo commented Oct 4, 2017

Hi @Indigo744 ,

Wow, indeed its a big one. Thanks a lot for it, I was having in my mind this issue regarding the huge number of events added by Mapael since a long time, but never took the time to dive into it !

I will take the necessary time to look at your PR and I will keep you informated within the next few days !

@Indigo744
Copy link
Collaborator Author

Added delegation for custom events!
I've namespaced them like 'click.mapael.custom' to be able to easily unbind/rebind them.

@neveldo
Copy link
Owner

neveldo commented Oct 10, 2017

Wow thanks ! I keep you informed ASAP for the review !

self.$map.on("mouseover." + pluginName, "[data-id]", function () {
var elem = this;
clearTimeout(mapMouseOverTimeoutID);
mapMouseOverTimeoutID = setTimeout(function(){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing space between ) and {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

self.$map.on("mousemove." + pluginName, "[data-id]", function (event) {
var elem = this;
clearTimeout(mapMouseMoveTimeoutID);
mapMouseMoveTimeoutID = setTimeout(function(){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing space between ) and {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


/* Handle tooltip position update */
if (elem.tooltip !== undefined) {
console.log(elem);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

console.log() should be removed

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see it actually... Did you check the latest rev?

Copy link
Owner

@neveldo neveldo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Indigo744 ,

Wow, very ... very huge work for a great improvement upcoming !

I have reviewed your PR, but commit by commit, I hope you will be able to read all the comments (some of them are obselete as you have fixed the issues in the next commits, like the trailing console.log() for instance).

All seems to work, except that the "onhover" (for instance to display the tooltips) behaviour is now jerky. I think this is due to the setTimeout() within the mouseover event handler ?

I think its ok to have a timeout before displaying a tooltip on an area or a plot (this is the current behaviour). But the tooltip should not be jerky when it is already displayed and the user is moving the cursor within an area.

var id = $elem.attr('data-id');
var type = $elem.attr('data-type');

if (type === 'area' || type === 'area-text') {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this if/elseif/elseif condition could be simplified with :

typesMapping = {
	'area'  : 'areas',
	'area-text' : 'areas',
	'plot' : 'plots',
	'plot-text' : 'plots',
	'link' : 'links',
	'link-text' : 'links'
};

self.elemClick(self[typesMapping[type]][id]);

(maybe typesMapping could be more global, as this if/elseif/elseif pattern is repeating in each event handling functions).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really good idea! I just committed the change.

@@ -1954,7 +1952,7 @@
if (elem.tooltip.overflow.right === true) {
tooltipPosition.left = mouseX - self.$map.offset().left + 10;
}
if (selem.tooltip.overflow.bottom === true) {
if (elem.tooltip.overflow.bottom === true) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that you have fixed this typo twice ;)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahah yes indeed!

@Indigo744
Copy link
Collaborator Author

I've updated as per your recommendation =)

I tried to create enough small commits so you can review one by one. But in the end, maybe we should use the "squash and merge" option of GH. What do you think?

@Indigo744
Copy link
Collaborator Author

Since we are already talking about v3 (plugin related), I'm wondering if we should postpone this evolution to the next major release?

I am afraid it may break on some people who has weirdly override some event handlers on each element...

What do you think?

@neveldo
Copy link
Owner

neveldo commented Oct 15, 2017

Hi @Indigo744 ,

Thanks for the fixes and for having separated the work in small commits, it helped a lot for the review ! (I agree for the squash when we will merge it with the master branch).

Have you some weirdly override cases that will not work anymore when this PR will be merged ?
Don't you think that all regular use cases should still work fine after this PR will be merged ?

I think that if some user extend some of the internal functions of Mapael, they should do this with full knowledge of the facts.Otherwise, each minor change of any internal function interface should lead to a new major release of mapael.

@Indigo744
Copy link
Collaborator Author

Nah I don't have any weird override cases ^^ but I was trying to imagine what could be the implication.

I think you are right, and the benefits can be important for users with lots of plots.

As a side note, I was wondering if we should put the filtering Timeout values as a variable so user can override them if needed.

@neveldo
Copy link
Owner

neveldo commented Oct 17, 2017

I think it could be indeed a good idea to make the timeout delay an internal variable to allow overriding. However, I think we shouldn't expose it as an regular option as the great majority of users will not need to override it.

@Indigo744 Indigo744 merged commit 5c66915 into neveldo:master Oct 24, 2017
@Indigo744 Indigo744 deleted the event_delegation branch October 24, 2017 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants