2

I am trying to update my text element on click to roll out like it is being typed. When I click on the text, it should pull the next text element from the data array and print character by character until complete, however, nothing happens. No errors are thrown though. Attached below is the code.

    var data = [
        "Hello",
        "World!",
        "What's up?"
       ];
    var i = 0;
    var body = d3.select("body");
    var element = body.append("svg");

   element.append("text")
       .data(data)
       .text(function(d) {return d})
       .attr("x", 150)
       .attr("y", 75)
       .attr("text-anchor", "middle")
       .attr("fill", "white")
           .on("click", function() {
               d3.select(this).transition()
                  .duration(5000)
                  .ease(d3.easeLinear)
                  .tween("text", function () {
                      var newText = data[i];
                      var textLength = newText.length;
                      return function (t) {
                          this.textContent = newText.slice(0, Math.round(t * textLength));
              };
          });

      //wrap around function for the data
      i = (i + 1) % data.length;
    });
J Iguadeza
  • 77
  • 7

1 Answers1

6

Updated answer:

D3 version 5.8.0 introduced an important change:

Tween functions can now use this to refer to the current node.

Therefore, using D3 v5.8.0 or higher, your code works as it is.


Old answer (for versions older than v5.8.0):

The problem is just the meaning of this inside the innermost function.

As in most of D3 methods, this is the current DOM element. For transition.tween it's not different:

When the transition starts, the value function is evaluated for each selected element, in order, being passed the current datum d and index i, with the this context as the current DOM element. (emphasis mine)

However, inside that inner function, this is just the window.

The easiest solution is using var self = this in the outer function:

.tween("text", function() {
    var self = this;
    var newText = data[i];
    var textLength = newText.length;
    return function(t) {
        self.textContent = newText.slice(0, Math.round(t * textLength));
    };
});

Here is your updated code:

var data = [
  "Hello",
  "World!",
  "What's up?"
];
var i = 0;
var body = d3.select("body");
var element = body.append("svg");

element.append("text")
  .data(data)
  .text(function(d) {
    return d
  })
  .attr("x", 150)
  .attr("y", 75)
  .attr("text-anchor", "middle")
  .on("click", function() {
    d3.select(this).transition()
      .duration(5000)
      .ease(d3.easeLinear)
      .tween("text", function() {
        var self = this;
        var newText = data[i];
        var textLength = newText.length;
        return function(t) {
          self.textContent = newText.slice(0, Math.round(t * textLength));
        };
      });

    //wrap around function for the data
    i = (i + 1) % data.length;
  });
<script src="https://d3js.org/d3.v4.min.js"></script>
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171