Andreas Dominguez
Andreas Dominguez

Reputation: 65

How can I dynamically update text labels in d3?

I want to add labels to my vertical bar chart that display the current percentage value that corresponds to the current hight of the bar. So I need to continuously update the percentage value and I also need a transition to make the text element move insync with the bar chart. I tried this:

var percentageLabels = svg.selectAll(".percentage-label")
    .data(dataset);

    percentageLabels.remove();

    percentageLabels
    .enter()
    .append("text")
            .attr("class", "percentage-label")
            .style("fill", "white")
    .text(function(d) {
        return d;
    })
    .attr("y", function(d) {
        return y(d);
    })
    .attr("x", function(d, i) {
        return i * (w / dataset.length) + 2.5 / 100 * w + w * 10/100;
    })
    .transition().duration(1750).ease("linear")
    .attr("y", function(d) {
                    return y(d);
    });

Check out the fiddle

Upvotes: 3

Views: 3449

Answers (3)

Mark
Mark

Reputation: 108517

I'd make a couple changes here. First, wrap the rect and the text in a g, so you only need to data-bind once. Then you are free to transition them together:

var uSel = svg.selectAll(".input")
    .data(dataset); //<-- selection of gs

uSel.exit().remove(); //<-- anybody leaving?  remove g (both rect and text)

var gs = uSel
    .enter()
    .append("g")
    .attr("class", "input"); //<-- enter selection, append g

gs.append("rect")
    .attr("fill", "rgb(250, 128, 114)"); //<-- enter selection, rect to g

gs.append("text")
    .attr("class", "percentage-label")
    .style("fill", "white")
    .attr("x", function(d, i) {
        return i * (w / dataset.length) + 2.5 / 100 * w + w * 10/100;
    }); //<-- enter selection, text to g

uSel.select("rect")
    .attr("x", function(d, i) {
        return i * (w / dataset.length) + 2.5 / 100 * w;
    })
    .attr("width", w / dataset.length - barPadding)
    .attr("height", y(0))
    .transition().duration(1750).ease("linear")
    .attr("y", function(d) {
        return y(d);
    })
    .attr("height", function(d) {
        return h - y(d);
    }); //<-- update rects with transition

uSel.select("text")
            .transition().duration(1750).ease("linear")
            .attr("y", function(d) {
                    return y(d);
            })
    .text(function(d) {
        return d + "%";
    }); //<-- update text with transition

Updated fiddle.


EDITS

To transition the text, you are probably going to have to use a custom tween function:

uSel.select("text")
    .transition().duration(1750).ease("linear")
    .attr("y", function(d) {
        return y(d); //<-- move the text
    })
    .tween("", function(d) {
      var self = d3.select(this),
          oldValue = y.invert(self.attr("y")), //<-- get the current value
          i = d3.interpolateRound(oldValue, d); //<-- interpolate to new value        
      return function(t) {
        self.text(i(t) + '%') <-- update the text on each iteration
      };
    });     

Updated, updated fiddle.

Upvotes: 2

Andreas Daiminger
Andreas Daiminger

Reputation: 69

You might want to check out this: https://github.com/mbostock/d3/wiki/Transitions#tween

Upvotes: 0

Damien Fayol
Damien Fayol

Reputation: 958

From the docs:

The transition.each method can be used to chain transitions and apply shared timing across a set of transitions. For example:

d3.transition()
    .duration(750)
    .ease("linear")
    .each(function() {
        d3.selectAll(".foo").transition()
       .style("opacity", 0)
       .remove();
    })
   .transition()
   .each(function() {
      d3.selectAll(".bar").transition()
      .style("opacity", 0)
      .remove();
   });

Upvotes: 0

Related Questions