sprucegoose sprucegoose - 6 months ago 24
Javascript Question

cannot clear d3.js div on dropdown change

I'm creating some tables using d3.js, where the tables are populated based on a dropdown option selected by the user, which filters the data. That is working fine.
I'm adding footnotes to the tables. The issue I'm having is that the footnotes div doesn't clear when the dropdown changes. So when you select "Section A", you see footnotes a,b,c. Then when you select "Section B", footnote d is appended to those. I want to only see footnote d in that scenario.

I have added d3.select("#footnotes").remove();
above where the footnotes are created, but that isn't working.
How can I clear the footnotes div before it's created on dropdown?

Here is a Plunker:
https://plnkr.co/edit/SnuLaLcNGgOh15Vn0456?p=preview

Code is also below:

<!DOCTYPE html>
<html>
<head>
<style>
body{
font-family:Arial, sans-serif;
font-size:14px;
}
table{
border-spacing:0;
padding:0;
}
th{
text-align:left;
font-weight:normal !important;
border-top:1px solid #ddd;
border-left:1px solid #ddd;
border-bottom:1px solid #ddd;
height:25px;
padding-left:5px;
width: 50px;
}
td{
border:1px solid #ddd;
width:30px !important;
height:25px;
padding-left:5px;
}
tr.row-odd,
.row-odd{
background: #eee;
}
</style>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>

</head>
<body>
<select size="1" id="sections" type="select" name="style">
<option>SELECT</option>
<option value="a">Section A</option>
<option value="b">Section B</option>
</select>

<div id="content">
</div>

<script>

function filterJSON(json, key, value) {
var result = [];
for (var foo in json) {
if (json[foo][key] === value) {
result.push(json[foo]);
}
}

return result;
}

definitions = {
"a" : "a: footnote 1",
"b" : "b: footnote 2",
"c" : "c: footnote 3",
"d" : "d: footnote 4"
};

var notes="";
var added=[];

d3.json("data3.json", function(json) {
d3.json("footnotes.json", function(foot){
json.forEach(function(d) {
d.year = +d.year;
});

$('#sections')

.on("change", function () {
var section = $(this).val();

data1 = filterJSON(json, 'set', section);
filterFootnotes = filterJSON(foot, 'set', section);

//make an object with footnotes
function merge(data1, filterFootnotes) {

function makeObj(a) {
obj[a.state] = obj[a.state] || {};
obj[a.state][a.year] = obj[a.state][a.year] || {};
Object.keys(a).forEach(function (k) {
obj[a.state][a.year][k] = a[k];
});
}
var array = [],
obj = {};

data1.forEach(makeObj);
filterFootnotes.forEach(makeObj);
Object.keys(obj).forEach(function (k) {
Object.keys(obj[k]).forEach(function (kk) {
array.push(obj[k][kk]);
});
});
return array;
}
var fullData = merge(data1, filterFootnotes);

// add years for select indicator
var nestyr = d3.nest()
.key(function(d) { return d.year; })
.sortKeys(d3.ascending)
.map(data1);

var yearstring = Object.keys(nestyr);

var tableData = [],
states = {};
fullData.forEach(function (d) {
var state = states[d.state];
if (!state) {
tableData.push(state = states[d.state] = {});
}
if ( d.footnote_id ){
state[d.year] = d.value + " <sup class='footnote'>" + d.footnote_id + "</sup>";
if(added.indexOf(d.footnote_id)==-1){
states[d.state]['footnote'] = d.footnote_id;
if(undefined!=definitions[d.footnote_id]){
notes+=definitions[d.footnote_id]+"<br />";
added.push(d.footnote_id);
}
}
} else{
state[d.year] = d.value;
}
states[d.state].State = d.state;
});

yearstring.unshift("State");

// render the table
tabulate(tableData, yearstring);

// add footnotes
d3.select("#footnotes").remove();
var fnotes = d3.selectAll('#content').append('div').attr("id", "footnotes").html(notes);

});

var width = 200, height = 25;
var minInd = d3.min(json, function(d) { return d.value;} )
var maxInd = d3.max(json, function(d) { return d.value;} )

xScale = d3.scale.linear().range([0, width - 10]).domain(d3.extent(json, function(d) { return d.year; })),
yScale = d3.scale.linear().range([height, 0]).domain([minInd,maxInd]),

xAxis = d3.svg.axis().scale(xScale).tickFormat(d3.format('0f')),
yAxis = d3.svg.axis().scale(yScale).orient("left");

}); // close footnotes json
}); // close json


function tabulate(newData, columns) {

d3.select("#table").remove();

var table = d3.select('#content').append('table').attr("id", "table")
var thead = table.append('thead')
var tbody = table.append('tbody');

thead.append('tr')
.selectAll('th')
.data(columns).enter()
.append('th')
.text(function (column) { return column; });

var rows = tbody.selectAll('tr')
.data(newData)
.enter()
.append('tr');

rows.attr("class", function(d, i){ if (i++ % 2 === 0){return 'row-even'}else {return 'row-odd'}});

var cells = rows.selectAll('td')
.data(function (row) {
return columns.map(function (column) {
return {column: column, value: row[column]};
});
})
.enter()
.append('td')
.attr("class", function (d,i) { return columns[i]; })
.html(function (d) { return d.value; });

return table;
};
</script>
</body>
</html>


The JSON files are in the Plunker.

Answer

Your d3.select("#footnotes").remove(); is working, that is not the problem. The problem is that your var notes and var added should be cleared after every change. What I did was simply putting them both inside your "change" function.

Here is the plunker: https://plnkr.co/edit/Co61GlCydJAzxiV24Adt?p=preview

PS: I'm not a big fan of using remove() in D3. You can easily have the same results without using it (for instance, this is the same, but without the remove() part: https://plnkr.co/edit/5OS2BTwSWl1om4IPqcEV?p=preview).

Comments