score:3

Accepted answer

The error is not caused by the first call to tick(path) but by subsequent calls when tick() gets called as a callback you registered by .each("end", tick);. These calls will not pass the required parameter path to your function. And, looking at D3's source I don't see a way of passing this parameter to the callback.

You can circumvent these issues by parameterising your callback function with the path you want it to act on:

tick(path)();

function tick(path) {
    var paramTick = function() {
      // push a new data point onto the back
      path.datum().push(random());

      // redraw the line, and slide it to the left
      path
          .attr("d", line)
          .attr("transform", null)
        .transition()
          .duration(500)
          .ease("linear")
          .attr("transform", "translate(" + x(-1) + ",0)")
          .each("end", paramTick);

      // pop the old data point off the front
      path.datum().shift();
    };
    return paramTick;
}

This one uses a closure to save the reference to the path for future calls. The function tick(path) defines an inner function paramTick() which is basically the function you used previously. It does, however, save the reference to the parameter path by means of a closure. Notice, that it passes the reference to itself as the callback parameter for the end event instead of tick(). This way you'll end up with a callback which gets parameterised by the call

tick(path)();

The call to tick(path) returns the parameterised version of your rendering logic which is immediately executed by the second set of parentheses.

I set up a working JSFiddle moving two paths to demonstrate this.


Although this does answer your question, it doesn't seem to be a perfect solution for the problem. Looking at the example moving two paths you will notice that they will get slightly out of sync every now and then. This is caused by utilizing different transitions for each path, which will not get perfectly synchronized.

In my opinion the better approch would be to group multiple paths into one g and apply the transition to this group. With only slight modifications to your original code (JSFiddle v2) this will give a smooth, synchronized movement of all paths.

var data = [
        d3.range(n).map(random),
        d3.range(n).map(random)
];

var paths = group.selectAll("path")
        .data(data)
        .enter()
          .append("path")
            .attr("class", function(d,i) {
                return "line" + i;
            })
            .attr("d", line);

tick();

function tick() {
      // push a new data point onto the back
    paths.each(function(d) {d.push(random());});

    // redraw the line
    paths
        .attr("d", line);

    // Apply the transition to the group
    group
        .attr("transform", null)
      .transition()
        .duration(500)
        .ease("linear")
        .attr("transform", "translate(" + x(-1) + ",0)")
        .each("end", tick);

    // pop the old data point off the front
    paths.each(function(d) {d.shift();});
}

Related Query

More Query from same tag