Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3.js should I detach event listener on exit/remove?

I have some code that adds a mouseover event handler to svg circles to display tooltips. Should I remove/unbind these handlers when I remove the circle elements? I do not know if these handlers are attached to the svg object and I am afraid it may lead to shadow dom or memory leaks. See code below :

circles.enter().append("svg:circle")
   .on("mouseenter", function(d) {
      // show tooltip
   });
circles.exit()
   .on("mouseenter", null) // necessary?
   .remove();
like image 738
Renaud Avatar asked Aug 22 '13 13:08

Renaud


People also ask

Should I remove event listeners before removing elements?

Removing the event listener first always results in lower memory usage (no leaks). Results: IE6 - memory leak.

Should you always remove event listeners?

TLDR; Always remove event listeners when you don't plan on using them any longer.

What happens if you don't remove event listeners?

The main reason you should remove event listeners before destroying the component which added them is because once your component is gone, the function that should be executed when the event happens is gone as well (in most cases) so, if the element you bound the listener to outlasts the component, when the event ...

How does remove event listener work?

The removeEventListener() is an inbuilt function in JavaScript which removes an event handler from an element for a attached event. for example, if a button is disabled after one click you can use removeEventListener() to remove a click event listener.


1 Answers

I think you have your answer already but I was interested in how you show that this is true, at least in latest Chrome.

This is the section of the D3 code that removes DOM nodes:

function remove() {
  var parent = this.parentNode;
  if (parent) parent.removeChild(this);
}

export default function() {
  return this.each(remove);
}

So as you can see it's depending on the browser to do cleanup of any associated listeners.

I created a simple stress test of adding/removing lots of circle nodes with D3:

  var circles = svg.selectAll("circle")
    .data(data, function(d) { return d.id; } );

  circles.exit().remove();

  circles.enter().append("circle")
    .attr("id", function(d) { return d.id; })
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .attr( { r: 5, fill: 'blue' })
    .on("mouseenter", function(d) { console.log('mouse enter') });    

Live version here: http://bl.ocks.org/explunit/6413685

  1. Open the above with latest Chrome
  2. Open the Developer Tools
  3. Click the Timeline tab
  4. Click the Record button at the bottom
  5. Let it run for a couple minutes, then click the button again to stop recording
  6. Drag the selector in the top timeline view to cover several of the garbage collection sawtooth patterns

You will notice that the DOM node garbage collection counts correspond with the event listener garbage collection counts. In fact, you can't really tell them apart in the below screenshot since the lines are superimposed:

Chrome Screenshot

Note that for Internet Explorer, things are a little more complicated.

See also this article for more tips on tracking memory usage in Chrome tools.

like image 163
explunit Avatar answered Oct 01 '22 11:10

explunit