title: How to correctly repeat nested transitions in d3?
body: <p>Currently, the code below moves the squares in an undulating fashion, but I want to change it so there is a single traveling wave (like they do in the stadiums). I guess what I have to do is figure out how to add a delay to the repeat loop so the first square goes down and stays down until the last square starts to go up.</p>
  
  <p><img src="https://i.stack.imgur.com/RxSAb.jpg" alt="enter image description here"></p>
  
  <p>Here's my code and a <a href="http://jsfiddle.net/k5zm18hn/" rel="nofollow noreferrer">jsfiddle</a>:</p>
  
  <pre><code>var margin = {top: 40, bottom: 40, left: 40, right: 40},
      width = 960 - margin.left - margin.right,
      height = 200 - margin.bottom - margin.top;
  
  var svg = d3.select("body").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
  var n = 20,
      rect_width = 20,
      padding = 1, 
      speed = 1000,
      item_delay = 40;
  
  var x = d3.scale.ordinal()
      .domain(d3.range(n))
      .rangePoints([0, n * (rect_width + padding)]);
  
  var rects = svg.selectAll(".rect")
      .data(x.domain())
    .enter().append("rect")
      .attr("x", function(d,i){ return x(i); })
      .attr("y", 0)
      .attr("height", rect_width)
      .attr("width", rect_width)
      .attr("class", "rect")
    .transition() 
      .duration(speed)
      .delay(function(d,i){ return i * 120; })
      .each(start_repeat);
  
  function start_repeat(){
      var rect = d3.select(this);
      (function repeat(){
          rect = rect.transition()
              .attr("y", 0)
            .transition()
              .attr("y", 100)
              .each("end", repeat);    
      })()
  }
  </code></pre>
  
  <p>I think my problem is that I don't really understand what's going on inside the <code>repeat</code> function. </p>
  
  <p><strong>UPDATE</strong></p>
  
  <p>I figured out a simple to loop the transition, but it requires that I manually calculate the loop duration and I'd like to avoid this (see snippet for a live version).</p>
  
  <pre><code>speed = 500;
  var loop_duration = 2000;
  (function loop(){
  rects.transition()
      .delay(function(d,i){ return i * item_delay; })
      .duration(speed)
      .attr("y", 100) // down
    .transition()
      .attr("y", 0)
  setTimeout(loop, loop_duration)
  })()    
  </code></pre>
  
  <p><div class="snippet" data-lang="js" data-hide="true" data-console="false" data-babel="false">
  <div class="snippet-code snippet-currently-hidden">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>var margin = {top: 40, bottom: 40, left: 40, right: 40},
      width = 960 - margin.left - margin.right,
      height = 300 - margin.bottom - margin.top;
  
  var svg = d3.select("body").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
  var n = 20,
      rect_width = 20,
      padding = 1, 
      speed = 500,
      item_delay = 100;
  
  var x = d3.scale.ordinal()
      .domain(d3.range(n))
      .rangePoints([0, n * (rect_width + padding)]);
  
  var rects = svg.selectAll(".rect")
      .data(x.domain())
    .enter().append("rect")
      .attr("x", function(d,i){ return x(i); })
      .attr("height", rect_width)
      .attr("width", rect_width)
      .attr("class", "rect")
      .attr("y", 0); // up - very important, if you don't set this, there is no interpolation
  
  var loop_duration = 2000;
  (function loop(){
      rects.transition()
          .delay(function(d,i){ return i * item_delay; })
          .duration(speed)
          .attr("y", 100) // down
        .transition()
          .attr("y", 0)
      setTimeout(loop, loop_duration)
  })()</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"&gt;&lt;/script&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>I've tried deparsing the <code>start_repeat</code> function to understand what's going on, but I still don't get it. For example, <strong>what is the difference between <code>old_rect</code> and <code>rect</code></strong>?</p>
  
  <pre><code>function start_repeat() {
    var rect = d3.select(this);
    (function repeat() {
      var move_down_t0 = rect.transition() // chain the down-transition on the same selection
          .attr("y", 100);
  
       var move_up_t1 = move_down_t0.transition()
          .attr("y", 0)
  
       old_rect = rect;
       rect = move_up_t1.each("end", repeat);
    })();
  }
  </code></pre>