Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3 - when setAttribute() works but .attr doesn't?

I'm puzzled as to why the d3 method for setting an element attribute fails (in this piece of code) while the traditional JS method works. (I'm trying to update the chloropleth colours as the user clicks a HTML button to change the data being plotted, all from the same JSON.)

The HTML is simple:

<div id="buttons">
    <button id="party">Parties</button>
    <button id="tenure">Tenure</button>
</div>

Here's the relevant JS with the two lines side-by-side. When I run it as is in Chrome, I get "Object # has no method 'attr'":

var paths = svg.selectAll("path");

var plot = {
    "mode": "party",
    "redraw": function()    {
        var e = e || window.event;
        var targ = e.target || e.srcElement;
        if (targ.nodeType == 3) targ = targ.parentNode;

        switch (targ.id)    {
        case "party":
            // some code in here
            break;
        case "tenure":
            paths.each(function(d,i) {
                this.setAttribute("class","");  // Same question here actually
            if (d.In_Office_Full_Date) {

// This line errors:

this.attr("style", "fill: " + t_scale(getJSDateFromExcel(d.In_Office_Full_Date).getFullYear()));

// ... but this line works:

this.setAttribute("style", "fill: " + t_scale(getJSDateFromExcel(d.In_Office_Full_Date).getFullYear()));

            }
            else
                this.setAttribute("style", "fill: #111");   // Neutral colour
            });
            break;
        default:
            console.log("Unknown event trigger in redraw()");
        }
    }
}

var t_scale = d3.scale.linear()
  .domain([1973,2013])
  .range(['red','white']);


d3.select("body").selectAll("#buttons button")
        .on("click",plot.redraw);

I hope you can help!

like image 355
MSC Avatar asked Feb 13 '23 13:02

MSC


1 Answers

The issue is with the way you're calling methods of this from within each(function(d, i) { ... }).

You're in the right direction: this refers to the plain html element that you're modifying. However the attr function you expect to call is a method of a d3 selection –– not of the html element. Hence, you need to wrap this in a d3 selection:

paths.each(function(d,i) {
  d3.select(this)
    .attr("class","");
});

Calling setAttribute worked because it's a method of the plain html element, but clearly d3's attr is more robust.

But having said all that, the more idiomatic way to achieve what you're doing is this:

paths
  .attr("class", "")
  .style("fill", function(d, i) {
    if (d.In_Office_Full_Date) {
      return t_scale(getJSDateFromExcel(d.In_Office_Full_Date).getFullYear());
    }
    return "#111";
  });
like image 136
meetamit Avatar answered Feb 16 '23 02:02

meetamit