dlamblin February 2016

Reducing the number of anonymous functions needed in calls to D3's `attr` and the like

So I've got some rudimentary code that works just fine [BTW I'm not using any scales yet]:

        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", function(d, i) {
                return i * (w / dataset.length);
           })
           .attr("y", function(d) {
                return h - (d.close * 4);
           })
           .attr("width", w / dataset.length - barPadding)
           .attr("height", function(d) {
                return d.close * 4;
           })
           .attr("fill", function(d) {
                return "rgb(0, 0, " + (d.close * 10) + ")";
           })
           .append("title").text(function(d) {
                if (d.date == null) {
                    return "Close: " + d.close;
                } else {
                    return "Date: " + dateFmt(d.date)
                         + "\nClose: " + d.close;
                }
           });

Now I'm told I can reduce the calls to attr by having the first argument be an object of name to value pairs like:

        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr({
           "x": function(d, i) {
                return i * (w / dataset.length);
           },
           "y": function(d) {
                return h - (d.close * 4);
           },
           "width": w / dataset.length - barPadding,
           "height": function(d) {
                return d.close * 4;
           },
           "fill": function(d) {
                return "rgb(0, 0, " + (d.close * 10) + ")";
           }})
           .append("title").text(function(d) {
                if (d.date == null) {
                    return "Close: " + d.close;
                } else {
                    return "Date: " + dateFmt(d.date)
                         + "\nClose: " + d.close;
                }
           });

It seems t

Answers


Gilsha February 2016

Try this way:

svg.selectAll("rect")
   .data(dataset)
   .enter()
   .append("rect")
   .each(function (d,i) {
       d3.select(this)
         .attr({
             "x": i * (w / dataset.length),
             "y": h - (d.close * 4),
             "width": w / dataset.length - barPadding,
             "height": d.close * 4,
             "fill": "rgb(0, 0, " + (d.close * 10) + ")"
         });
   });


altocumulus February 2016

This is one of the features I have definitely been missing every now and then.

There have been some discussions about pros & cons on issue #277 and similarly for .classed() on issue #2254 with both sides having good arguments. The best one against including it into .attr() was brought up by Mike Bostock in one of his comments:

This preserves D3’s philosophy of encouraging only values (and not names) to be specified as functions.

Notwithstanding, the wait may soon be over! As I just noticed, we may see this feature in the near future with the upcoming D3 v4. The consensus now seems to be to not further overload the methods .attr(), .property() and .style() to avoid cluttering those methods' signatures. The change as it is currently in the repo is implemented in the new methods .attrs(), .properties() and .styles(). Note the plural names of these methods.


For the time being, the answer posted by Gilsha is obviously correct and as close, as one can get to this with D3 v3. It has also been proposed by Mike Bostock in his comment to the above mentioned issue, but to some others, including myself, it feels somewhat hacky.

Post Status

Asked in February 2016
Viewed 2,975 times
Voted 9
Answered 2 times

Search




Leave an answer