0

I'm trying to assign different elements different attribtsty. But everyone is given the same (last) attribute. What's the matter?

for (var i = 1; i < 12; i++) {
    d3.select("#id_" + i)
        .text(parseFloat(data[i - 1] / 1000000).toFixed(2))
        .on("mouseover", function (d) {
             d3.select("#tooltip")
                .style("left", "200px")
                .style("top", d3.event.pageY - 30 + "px")
                .select("#info")
                .html("<b>" + keys[i - 2] + "</b>");
             d3.select("#tooltip").classed("hidden", false);
                })
        .on("mouseout", function () {
             d3.select("#tooltip").classed("hidden", true);
    });
}

Link: JSFIDDLE

Cœur
  • 37,241
  • 25
  • 195
  • 267
NickDevil
  • 185
  • 3
  • 14
  • Which attributes are you assigning that are not being assigned as you expected? What do you expect and what do you see? – Mike Bell Jul 22 '14 at 14:23
  • 1
    http://stackoverflow.com/questions/1451009/javascript-infamous-loop-issue could be the problem. Need to wrap the assignment to i in a self-invoking anonymous function. I assume you are asking about the text. – John Powell Jul 22 '14 at 14:25

3 Answers3

2

Try this:

for (var i = 1; i < 12; i++) {
  (function (i) {  
      d3.select("#id_" + i)
        .text(parseFloat(data[i - 1] / 1000000).toFixed(2))
        .on("mouseover", function (d) {
             d3.select("#tooltip")
                .style("left", "200px")
                .style("top", d3.event.pageY - 30 + "px")
                .select("#info")
            .html("<b>" + keys[i - 2] + "</b>");
             d3.select("#tooltip").classed("hidden", false);
                })
        .on("mouseout", function () {
             d3.select("#tooltip").classed("hidden", true);
        });
  })(i)
}
sergeyz
  • 1,339
  • 10
  • 14
2

The code that you have written is an example of imperative programming, where you tell the computer how to do something.

D3 is an example of declarative programming, where you tell the computer what to do, and let the computer decide how to do it.

D3 was not designed to loop through to assign values like in your question. Instead of loops and conditionals and other "hows", you should focus on what you want to happen.

To do this, you should use what is called "data binding", where you bind your dataset to the svg to draw your text (here is a beginner tutorial/simple explanation http://bost.ocks.org/mike/circles/)

In relation to your problem, you should put your data in one object like this:

var data = [
    ["key": 1, "value": 0.0, "x": 84, "y": 310],
    ...
    ];

You then bind this data to the svg element, where you can then tell D3 to draw your text with those attributes without specifiying the exact implementation.

svg.selectAll("text")
    .data(data)
    .enter()
    .attr("x", function(d, i) {
        return d.x;
    })
    .attr("y", function(d, i) {
        return d.y;
    })
    ...
    .on("mouseover", function(d, i) {
        d3.select("#tooltip")
            .style("left", "200px")
            .style("top", d3.event.pageY - 30 + "px")
            .select("#info")
            .html("<b>" + d.key + "</b>");
        d3.select("#tooltip").classed("hidden", false);
    })
    ...

The function function(d, i) gives the actual object d as an argument, and i as the object's location in the array.

jluckin
  • 662
  • 4
  • 6
1

Your issue with the tooltips is that i is the same value (12) no mater which element is moused over. To see this, you can log i in your mouseover handler. There are a few ways you could recover the index of the element that is moused over. One of the easiest (though perhaps not cleanest) would be to recover it from the element ID.

for (var i = 1; i < 12; i++) {
    d3.select("#id_" + i)
        .text(parseFloat(data[i - 1] / 1000000).toFixed(2))
        .on("mouseover", function (d) {
            var index = this.id.slice(3,9);              //ADDED THIS LINE
             d3.select("#tooltip")
                .style("left", "200px")
                .style("top", d3.event.pageY - 30 + "px")
                .select("#info")
                .html("<b>" + keys[index - 2] + "</b>");       //USE THE INDEX
             d3.select("#tooltip").classed("hidden", false);
        })
        .on("mouseout", function () {
             d3.select("#tooltip").classed("hidden", true);
    });
}

I also suspect that you mean keys[index-1] not keys[index-2]. The latter goes out of bounds of the array.

Mike Bell
  • 1,356
  • 11
  • 9