Reputation: 134
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
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