1

Yet another javascript beginner with a closure issue... However, I have been reading up on various sources on closure binding (most helpful), but I still can't fully transfer this to my specific issue.

For convenience, I have prepared a minimal jsfiddle. My problem is to use d3-tip with multiple, different rendering logics. In the jsfiddle you can see a plot on the top and one below. When you hover the mouse over the boxes, each plot should generate its own tooltip. However, as you can see, the first plot also uses the tooltip callback of the second plot. Or more general: The last tooltip callback overwrites previous callbacks.

The implementation follows some standard d3 / d3-tip patterns. Basiscally I have multiple plotting functions like this:

function somePlottingFunction(data, locator) {
   var svg = ... // append svg

   // define plot-specific tooltip logic
   function tipRenderer(d) {
     return "Renderer of plot 1: " + d;
   } 
   tip = d3.tip()
           .attr("class", "d3-tip")
           .html(tipRenderer);
   svg.call(tip);

   // and a .enter()
   svg.selectAll("rect")
      .data(data)
      .enter()
      .append("rect")
      // ... attr ...
      .on("mouseover", (d) => tip.show(d)) // <= issue here
      .on("mouseout", (d) => tip.hide(d));
}

The code does work when simply using .on("mouseover", tip.show). However in my actual code, I need additional logic in mouseover, which is why I need a wrapping closure. My questions are:

  • Why does the closure close over the wrong tip variable? Or rather: How can the second plotting function modify the closure binding of the first function?
  • How would an experience javascript programmer solve this?

Note: In the jsfiddle the two plotting functions (and the tooltip logics) are almost identical to keep the example small, which suggest to simply use the same tipRenderer anyway. My actual use case is a page with entirely different plots, and thus, the tooltip rendering cannot (or should not) be unified.

bluenote10
  • 23,414
  • 14
  • 122
  • 178

1 Answers1

3

If you don't declare your variable using var, it becomes a global.

So, this is the simple fix, inside both functions:

var tip = d3.tip()
    .attr("class", "d3-tip")
    .direction("s")
    .html(tipRenderer);

Here is your updated fiddle: https://jsfiddle.net/wd08acg1/

Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • Takeaway: Learn the basics _before_ advanced topics. Adding a question to avoid having my comment deleted again: Why does it work with the plain `tip.show` though? I guess because in this case `this` for the `show` function is bound to the actual `tip` objects, whereas in the closure case, javascript won't close over anything due to seeing a global? – bluenote10 Jan 01 '17 at 14:50
  • I'm not following this last question of yours ("Why does it work with the plain tip.show though?"), I'd appreciate if you clarify it. However, have in mind that you're not passing `this` to the `tip.show()` (which won't work with an arrow function), but the *datum* instead. – Gerardo Furtado Jan 02 '17 at 00:58
  • What I mean is to replace the arrow function by just `tip.show` itself ([updated fiddle](https://jsfiddle.net/vb3xfe5k/6/)). That is why I got confused in the first place. – bluenote10 Jan 02 '17 at 06:47
  • Now I see... I reckon [this question](http://stackoverflow.com/questions/3246928/in-javascript-does-it-make-a-difference-if-i-call-a-function-with-parentheses) will give you a good reading material. – Gerardo Furtado Jan 02 '17 at 07:35
  • Isn't this question just about passing a function versus invoking it? `tip.show()` does not make sense (and doesn't work), it is more about the difference of the two functions: `tip.show` vs `(d) => tip.show(d)`, which behave differently. – bluenote10 Jan 02 '17 at 07:46
  • It's the same principle. `tip.show(d)` is the same of `tip.show()` with the extra argument. If you doubt me, do this: write this issue as a new question (it's a good question, it will receive upvotes **if** it's not closed as a duplicate) with the *javascript* tag only (that attracts more attention). – Gerardo Furtado Jan 02 '17 at 07:50