Home > Software engineering >  How to add ascending - descending sort buttons in a bar chart using d3.js?
How to add ascending - descending sort buttons in a bar chart using d3.js?

Time:02-06

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>

  • Related