somejonus
somejonus

Reputation: 134

How to update the index (i) on bound data in d3?

On the first enter I bind data to a svg like this:

var dateContainer = svg.selectAll("g.dateContainerClass")
    .data(dateData, function(d) {
       return d.id
    });

var dateContainerEnter = dateContainer.enter()
    .append("g")
    .attr("class", "dateContainerClass");

At this point the bound data consists of an array with 5 entries. So i is 0 to 4, first to last. Now the array is updated: two new entries are spliced in between 0 and 1.

dateData.splice((i + 1), 0, rawData[(rawIndex + 1)], rawData[(rawIndex + 2)])

After that the new dateData is bound to the selection again, as shown above. What I was expecting to happen is this:

i=0 (old)
i=1 (new)
i=2 (new)
i=3 (old)
i=4 (old)
etc.

What actually happens is this:

i=0 (old)
i=1 (new)
i=2 (new)
i=1 (old)
i=2 (old)
etc.

Am I doing something wrong or is this expected behavior? Any help would be really appreciated!

EDIT:

Added snippet: Please run snippet full screen and click on Day1. When you inspect the element, the id of class "dateflag" should go up one each, which it does not.

var windowWidth = window.innerWidth;
var storyline = d3.select(".Storyline").append("svg");

function update(dateData, rawData) {
  console.log(dateData)
  var x = d3.scaleLinear()
    .domain([0, (dateData.length - 1)])
    .range(["10%", "80%"]);

  storyline
    .transition()
    .duration(500)
    .delay(50)
    .attr("width", (dateData.length * 20) + "%")
    .attr("height", "100%")

  var dateFlags = storyline.selectAll("g.dateflag")
    .data(dateData, function(d, i) {
      return d
    });

  //ENTER

  var dateFlagsEnter = dateFlags.enter()
    .append("g")
    .attr("class", "dateflag")
    .attr("id", function(d, i) {
      console.log("ContainerID: " + i)
      return i
    });

  dateFlagsEnter.append("foreignObject")
    .attr("class", "timePoints")
    .attr("width", "130px")
    .attr("height", "100%")
    .attr("x", function(d, i) {
      return x(i);
    })
    .attr("y", function(d, i) {
      var topBottom;
      if ((i % 2) == 1) {
        topBottom = "35%";
      } else {
        topBottom = "40%";
      }
      return topBottom;
    })
    .attr("opacity", "0%");

  dateFlagsEnter.select(".timePoints").append("xhtml:div")
    .attr("class", "tpGroup")

  dateFlagsEnter.select(".tpGroup").append("xhtml:div")
    .attr("id", "dateLabel")
    .append("xhtml:div")
    .html(function(d, i) {
      return d + " index= " + i;
    })
    .attr("id", "dateLabelText")
    .on("click", function(d, i) {
      var clickIndex = i
      console.log("clickedID: " + clickIndex)
      expand(dateData, clickIndex);
    });

  //UPDATE

  dateFlags.merge(dateFlagsEnter).select(".timePoints")
    .transition()
    .duration(500)
    .delay(50)
    .attr("x", function(d, i) {
      return x(i);
    })
    .attr("opacity", "100%");

  dateFlags.merge(dateFlagsEnter).select("#dateLabelText")
    .html(function(d) {
      return d;
    });

  //EXIT

  dateFlags.exit().remove();

};

function expand(dateData, clickIndex) {
  var lineIndex = clickIndex

  dateData.splice((lineIndex + 1), 0, "Day1.1", "Day1.2")
  update(dateData);
};

function getDateData() {
  var dateData = ["Day1", "Day2", "Day3", "Day4", "Day5", ]
  update(dateData);
};

getDateData();
<div class="Storyline"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Upvotes: 0

Views: 183

Answers (1)

Ruben Helsloot
Ruben Helsloot

Reputation: 13129

When you set .attr("id" to a function, the function is executed immediately, and only once. That means that when you insert new values into the data array, you also need to re-calculate that attribute. If you move .attr("id" to after the .merge, everything works as expected:

var windowWidth = window.innerWidth;
var storyline = d3.select(".Storyline").append("svg");

function update(dateData, rawData) {
  console.log(dateData)
  var x = d3.scaleLinear()
    .domain([0, (dateData.length - 1)])
    .range(["10%", "80%"]);

  storyline
    .transition()
    .duration(500)
    .delay(50)
    .attr("width", (dateData.length * 20) + "%")
    .attr("height", "100%")

  var dateFlags = storyline.selectAll("g.dateflag")
    .data(dateData, function(d, i) {
      return d
    });

  //ENTER

  var dateFlagsEnter = dateFlags.enter()
    .append("g")
    .attr("class", "dateflag");

  dateFlagsEnter.append("foreignObject")
    .attr("class", "timePoints")
    .attr("width", "130px")
    .attr("height", "100%")
    .attr("x", function(d, i) {
      return x(i);
    })
    .attr("y", function(d, i) {
      var topBottom;
      if ((i % 2) == 1) {
        topBottom = "35%";
      } else {
        topBottom = "40%";
      }
      return topBottom;
    })
    .attr("opacity", "0%");

  dateFlagsEnter.select(".timePoints").append("xhtml:div")
    .attr("class", "tpGroup")

  dateFlagsEnter.select(".tpGroup").append("xhtml:div")
    .attr("id", "dateLabel")
    .append("xhtml:div")
    .html(function(d, i) {
      return d + " index= " + i;
    })
    .attr("id", "dateLabelText")
    .on("click", function(d, i) {
      var clickIndex = i
      console.log("clickedID: " + clickIndex)
      expand(dateData, clickIndex);
    });

  //UPDATE

  dateFlags.merge(dateFlagsEnter)
    .attr("id", function(d, i) {
      console.log("ContainerID: " + i)
      return i
    })
    .select(".timePoints")
    .transition()
    .duration(500)
    .delay(50)
    .attr("x", function(d, i) {
      return x(i);
    })
    .attr("opacity", "100%");

  dateFlags.merge(dateFlagsEnter).select("#dateLabelText")
    .html(function(d) {
      return d;
    });

  //EXIT

  dateFlags.exit().remove();

};

function expand(dateData, clickIndex) {
  var lineIndex = clickIndex

  dateData.splice((lineIndex + 1), 0, "Day1.1", "Day1.2")
  update(dateData);
};

function getDateData() {
  var dateData = ["Day1", "Day2", "Day3", "Day4", "Day5", ]
  update(dateData);
};

getDateData();
<div class="Storyline"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Upvotes: 1

Related Questions