Reputation: 1
I am writing a d3 program that reads in stats from NFL teams from a csv file and the user selects the team to view from a dropdown menu. The program then creates circles in the formation of the offense and defense for the stats for that team and displays it. I have the program able to display the some of it so far, but when I select another team, the old circles stay on the screen and the new ones are just appended on top. My question is how do I fix this? I tried adding remove at the end of the below function but it just removes that circle completely
function menuChanged()
{
var selectedValue = d3.event.target.value;
var picked;
for(var i = 0; i < 32; i++)
if(nest[i].key == selectedValue)
picked = i;
rT = svg.selectAll("rTcircle")
.data(nest)
.enter().append("circle")
.attr("class", "dot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].rushing) * 0.7; })
.attr("cx", 40)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
rG = svg.selectAll("rGcircle")
.data(nest)
.enter().append("circle")
.attr("class", "rGdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].rushing) * 0.7; })
.attr("cx", 190)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
cO = svg.selectAll("oCcircle")
.data(nest)
.enter().append("circle")
.attr("class", "oCdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].rushing) * 0.7; })
.attr("cx", 340)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
lG = svg.selectAll("lGcircle")
.data(nest)
.enter().append("circle")
.attr("class", "lGdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].rushing) * 0.7; })
.attr("cx", 490)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
lT = svg.selectAll("lTcircle")
.data(nest)
.enter().append("circle")
.attr("class", "lTdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].rushing) * 0.7; })
.attr("cx", 640)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
var oLineT = svg.selectAll(".text")
.data(nest)
.enter().append("text")
.attr("class","text")
.style("text-anchor", "middle")
.attr("x", 40)
.attr("y", 300)
.style("fill", function() { return nest[picked].values[0].color1; })
.style("stroke", function() { return nest[picked].values[0].color2; })
.style("font-family", "verdana")
.style("stroke-width", 0.7)
.text(function () {return nest[picked].values[0].team; });
rT.data(nest).transition()
.duration(500)
.attr("class", "dot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].passing) * 0.7; })
.attr("cx", 40)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
rG.data(nest).transition()
.duration(500)
.attr("class", "rGdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].passing) * 0.7; })
.attr("cx", 190)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
cO.data(nest).transition()
.duration(500)
.attr("class", "oCdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].passing) * 0.7; })
.attr("cx", 340)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
lG.data(nest).transition()
.duration(500)
.attr("class", "lGdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].passing) * 0.7; })
.attr("cx", 490)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
lT.data(nest).transition()
.duration(500)
.attr("class", "lTdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].passing) * 0.7; })
.attr("cx", 640)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
oLineT.data(nest).transition()
.duration(500)
.attr("class","text")
.style("text-anchor", "middle")
.attr("x", 40)
.attr("y", 300)
.style("fill", function() { return nest[picked].values[0].color1; })
.style("stroke", function() { return nest[picked].values[0].color2; })
.style("font-family", "verdana")
.style("stroke-width", 0.7)
.text(function () {return nest[picked].values[0].team; });
}
Upvotes: 0
Views: 91
Reputation: 102174
This is a classic case of not properly defining the "enter" and the 'update" selections (and the "exit", if any). Unfortunately, just quickly skimming your menuChanged
function, it seems to me that you will have to do a lot of changes, in order to make it the "D3 way". Also, you could store the variable parts in... well, variables!
I made a simple demo to show you how the "enter" and the "update" selections work (I'm using v4 here). First, bind the data:
var circles = svg.selectAll(".teamCircles")
.data(data[0][team].positions);
Both "enter" and "update" selections rely on this bound data.
Then, set the "enter" selection (here, I put it inside the menuChanged
function, but if the number of players never change, it can be outside the function):
circlesEnter = circles.enter()
.append("circle")
.attr("class", "teamCircles")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 8)
.attr("fill", data[0][team].color);
And, finally, the "update" selection. The update selection only works on previously existing elements:
circlesUpdate = circles.transition()
.duration(1000)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 8)
.attr("fill", data[0][team].color);
Here is the demo:
var w = 500,
h = 300;
var svg = d3.select("#svg").append("svg")
.attr("width", w)
.attr("height", h);
var data = [{
"San Francisco 49": {
color: "red",
positions: [{
x: 110,
y: 50
}, {
x: 35,
y: 56
}, {
x: 230,
y: 200
}, {
x: 431,
y: 50
}, {
x: 310,
y: 250
}]
},
"Green Bay Packers": {
color: "green",
positions: [{
x: 360,
y: 120
}, {
x: 51,
y: 156
}, {
x: 30,
y: 60
}, {
x: 130,
y: 210
}, {
x: 410,
y: 250
}]
},
"Baltimore Ravens": {
color: "purple",
positions: [{
x: 200,
y: 200
}, {
x: 34,
y: 236
}, {
x: 390,
y: 98
}, {
x: 330,
y: 66
}, {
x: 10,
y: 210
}]
}
}];
d3.select("#selection").on("change", menuChanged);
function menuChanged() {
var team = this.value;
var circles = svg.selectAll(".teamCircles")
.data(data[0][team].positions);
circlesEnter = circles.enter()
.append("circle")
.attr("class", "teamCircles")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 8)
.attr("fill", data[0][team].color);
circlesUpdate = circles.transition()
.duration(1000)
.attr("class", "teamCircles")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 8)
.attr("fill", data[0][team].color);
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<select name="select" id="selection">
<option value="">Select</option>
<option value="San Francisco 49">San Francisco 49</option>
<option value="Green Bay Packers">Green Bay Packers</option>
<option value="Baltimore Ravens">Baltimore Ravens</option>
</select>
<div id="svg"></div>
PS: by the way, the reason your circles are piling up is because in your enter selections you're selecting something that doesn't exist. For instance:
rT = svg.selectAll("rTcircle")
rTcircle
doesn't exist, and thus your enter selection is never empty.
Upvotes: 1