I'm trying to figure out how to add interactive features in a bar chart.
Now I'm stuck about program fuctions for a click action using a button to sort ascending and descending.
This is my code:
const width = 800
const height = 400
const margin = {
top: 40,
bottom: 60,
left: 40,
right: 10
}
const svg = d3.select("div#chart").append("svg").attr("width", width).attr("height", height)
const elementGroup = svg.append("g").attr("id", "elementGroup").attr("transform", `translate(${margin.left}, ${margin.top})`)
const axisGroup = svg.append("g").attr("id", "axisGroup")
const xAxisGroup = axisGroup.append("g").attr("id", "xAxisGroup").attr("transform", `translate(${margin.left}, ${height - margin.bottom})`)
const yAxisGroup = axisGroup.append("g").attr("id", "yAxisGroup").attr("transform", `translate(${margin.left}, ${margin.top})`)
const x = d3.scaleBand().range([0, width - margin.left - margin.right]).padding(0.1)
const y = d3.scaleLinear().range([height - margin.bottom - margin.top, 0])
const xAxis = d3.axisBottom().scale(x)
const yAxis = d3.axisLeft().scale(y).ticks(5)
d3.csv("WorldCup.csv").then(data => {
let nest = d3.nest()
.key(d => d.Winner)
.entries(data)
nest.map(d => d.values = d.values.length)
console.log(nest)
x.domain(nest.map(d => d.key))
y.domain([0, d3.max(nest.map(d => d.values))])
xAxisGroup.call(xAxis)
yAxisGroup.call(yAxis)
//Asceding sort
function sortAscending() {nest.sort((a, b) => d3.ascending(a.values, b.values))
let xLabel = elementGroup.append("text").text("Countries")
.attr("transform", `translate(${width - margin.right - 30}, ${height - margin.bottom})`)
.attr("text-anchor", "end").attr("font-weight", 700)
let yLabel = elementGroup.append("text").text("Cups per country")
.attr("transform", `translate(${-20}, ${-10})`).attr("font-weight", 700)
let elements = elementGroup.selectAll("rect").data(nest)
elements.enter().append("rect")
.attr("class", "bar")
.attr("x", d => x(d.key))
.attr("width", x.bandwidth())
.attr("height", d => height - margin.top - margin.bottom - y(d.values))
.attr("y", d => y(d.values))
}
d3.select("#Ascending").on("click", sortAscending)
//Descending sort
function sortDescending() {nest.sort((a, b) => d3.descending(a.values, b.values))
let xLabel = elementGroup.append("text").text("Countries")
.attr("transform", `translate(${width - margin.right - 30}, ${height - margin.bottom})`)
.attr("text-anchor", "end").attr("font-weight", 700)
let yLabel = elementGroup.append("text").text("Cups per country")
.attr("transform", `translate(${-20}, ${-10})`).attr("font-weight", 700)
let elements = elementGroup.selectAll("rect").data(nest)
elements.enter().append("rect")
.attr("class", "bar")
.attr("x", d => x(d.key))
.attr("width", x.bandwidth())
.attr("height", d => height - margin.top - margin.bottom - y(d.values))
.attr("y", d => y(d.values))
}
d3.select("#Ascending").on("click", sortDescending)
})
I tried different examples.
By the moment, I just understood it is about configure a function, and then link the fuction with a button by 'on'.
CodePudding user response:
You are close, but you have to separate the code that is used to draw the plot for the first time, and what is needed to update it after the data was sorted. A possible framework that works, based on on your code, is this
const height = 400, width = 600;
const margin = {
top: 40,
bottom: 60,
left: 40,
right: 10
};
const svg = d3.select("div#chart").append("svg").attr("width", width).attr("height", height)
const elementGroup = svg.append("g").attr("id", "elementGroup").attr("transform", `translate(${margin.left}, ${margin.top})`)
const axisGroup = svg.append("g").attr("id", "axisGroup")
const xAxisGroup = axisGroup.append("g").attr("id", "xAxisGroup").attr("transform", `translate(${margin.left}, ${height - margin.bottom})`)
const yAxisGroup = axisGroup.append("g").attr("id", "yAxisGroup").attr("transform", `translate(${margin.left}, ${margin.top})`)
const x = d3.scaleBand().range([0, width - margin.left - margin.right]).padding(0.1)
const y = d3.scaleLinear().range([height - margin.top - margin.bottom, 0])
const xAxis = d3.axisBottom().scale(x)
const yAxis = d3.axisLeft().scale(y).ticks(5)
function drawBarPlot(nest){
x.domain(nest.map(d => d.key))
y.domain([0, d3.max(nest.map(d => d.values))])
xAxisGroup.call(xAxis)
yAxisGroup.call(yAxis)
elementGroup.append("text").text("Countries")
.attr("transform", `translate(${width - margin.right - 30}, ${height - margin.bottom})`)
.attr("text-anchor", "end").attr("font-weight", 700);
elementGroup.append("text").text("Cups per country")
.attr("transform", `translate(${-20}, ${-10})`).attr("font-weight", 700);
elementGroup.selectAll("rect").data(nest).enter().append("rect")
.attr("class", "bar")
.attr("x", d => x(d.key))
.attr("width", x.bandwidth())
.attr("height", d => height - margin.top - margin.bottom - y(d.values))
.attr("y", d => y(d.values));
}
function updateBarPlot(nest){
x.domain(nest.map(d => d.key));
xAxisGroup.call(xAxis);
elementGroup.selectAll("rect").data(nest).call(update => update
.attr("height", d => height - margin.top - margin.bottom - y(d.values))
.attr("y", d => y(d.values)));
}
//Ascending sort
function sortAscending(nest) {
nest.sort((a, b) => d3.ascending(a.values, b.values));
updateBarPlot(nest);
}
function sortDescending(nest) {
nest.sort((a, b) => d3.descending(a.values, b.values));
updateBarPlot(nest);
}
//d3.csv("WorldCup.csv").then(data => {
new Promise(resolve => resolve(d3.csvParse("Year,Winner\n" "1930,Uruguay\n" "1934,Italy\n" "1938,Italy\n" "1950,Uruguay\n"
"1954,Germany\n" "1958,Brazil\n" "1962,Brazil\n" "1966,England\n" "1970,Brazil\n"
"1974,Germany\n" "1978,Argentina\n" "1982,Italy\n" "1986,Argentina\n" "1990,Germany\n"
"1994,Brazil\n" "1998,France\n" "2002,Brazil\n" "2006,Italy\n" "2010,Spain\n"
"2014,Germany\n" "2018,France\n" "2022,Argentina\n"))).then(data => {
let nest = d3.nest()
.key(d => d.Winner)
.entries(data)
nest.map(d => d.values = d.values.length)
drawBarPlot(nest);
d3.select("#Ascending").on("click", ()=>sortAscending(nest));
d3.select("#Descending").on("click", ()=>sortDescending(nest));
});
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://d3js.org/d3-collection.v1.min.js"></script>
<div id="chart"></div>
<button id="Ascending">Ascending</button><button id="Descending">Descending</button>