1

let div = document.createElement('div');
let ul = document.createElement('ul');

div.appendChild(ul);
async function f() {
    //fetching and sorting data by regions and subregions
    const res = await fetch("https://restcountries.com/v3.1/all");
    const data = await res.json();
    data.sort((a, b) => {
        if (a.region > b.region) return 1;
        else if (a.region < b.region) return -1
        else {
            if (a.subregion > b.subregion) return 1;
            else return -1;
        }
    });
    const container = document.getElementById('container');
    const accordion = document.createElement('div');
    const olWrapper = document.getElementById('listWrapper');   
    const subRegionWrapper = document.getElementById('subRegionWrapper');

    const subRegions = data.reduce((r, a) => {
        r[a.subregion] = r[a.subregion] || [];
        r[a.subregion].push(a);
        return r;
    }, {});

   const dropdownValues = Object.entries(subRegions);
    dropdownValues.forEach(subRegion => {
      const accordionWrapper = document.createElement('div');
      const panel = document.createElement('div');
      panel.classList.add('panel');
      accordionWrapper.classList.add('accordion');

      const totalArea = subRegion[1].reduce((acc, curr) => acc+curr.area,0);
      const totalPopulation = subRegion[1].reduce((acc, curr) => acc+curr.population,0);
      const li = createSubregion(subRegion[0], totalPopulation, totalArea);

      accordionWrapper.appendChild(li);
      accordion.appendChild(accordionWrapper);

      subRegion[1].forEach(item => {
          const subLi = createCountry(item.name.common, item.capital, item.area, item.population);
          const subOl = document.createElement('ol');
          subOl.appendChild(subLi);
          panel.appendChild(subOl);
          accordion.appendChild(panel);
      });

     accordionWrapper.addEventListener('click', function() {
        this.classList.toggle("active");
        const panel = this.nextElementSibling;
        if (panel.style.display === "block") {
          panel.style.display = "none";
        } else {
          panel.style.display = "block";
        }
     });
    });
  
  container.appendChild(accordion);
}

function createSubregion(name, population, area) {
    var li = document.createElement("li");
    li.setAttribute("class", "subregion");

    var header = document.createElement("div");
    header.setAttribute("class", "subregion-header disp-flex");

    var nameDiv = document.createElement("div");

    var nameh2 = document.createElement("h2");
    nameh2.innerText = name;

    nameDiv.appendChild(nameh2);
    header.append(nameDiv);

    var emptyDiv = document.createElement("div");
    header.appendChild(emptyDiv);

    var populationDiv = document.createElement("div");
    var populationh2 = document.createElement("h3");
    populationh2.innerText = population;

    populationDiv.appendChild(populationh2);
    header.append(populationDiv);

    var areaDiv = document.createElement("div");
    var areah2 = document.createElement("h3");
    areah2.innerText = area;

    areaDiv.appendChild(areah2);
    header.append(areaDiv);

    li.appendChild(header);
    return li;
}

function createCountry(name, capital, area, population) {
    var country = document.createElement("li");
    country.setAttribute("class", "country disp-flex")

    var namediv = document.createElement("div");
    var nameh4 = document.createElement("h4");
    nameh4.innerText = name;
    namediv.appendChild(nameh4);
    country.appendChild(namediv);

    var capitaldiv = document.createElement("div");
    var capitalh4 = document.createElement("h4");
    capitalh4.innerText = capital;
    capitaldiv.appendChild(capitalh4);
    country.appendChild(capitaldiv);

    var popdiv = document.createElement("div");
    var poph4 = document.createElement("h4");
    poph4.innerText = population;
    popdiv.appendChild(poph4);
    country.appendChild(popdiv);

    var areadiv = document.createElement("div");
    var areah4 = document.createElement("h4");
    areah4.innerText = area;
    areadiv.appendChild(areah4);
    country.appendChild(areadiv);


    return country;
}

f();
body {
  margin: 0 15%;
    min-height: 100vh;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background-color: aliceblue;
    font-family: 'Open Sans', Arial;
    font-size: 18px;
}
header{
  margin: 0 10%;
    display:flex;
    justify-content: space-between;
    padding: 22px 0;
    color:rgb(5, 5, 5);
}
ul {
    list-style: none;
    list-style-type: none;
    outline: 2px solid #ddd;
    padding: 1rem 2rem;
    border-radius: 0.5rem;
    list-style-position: inside;
    color: blue;
}

ul ol { 
    color: rgb(197, 105, 18);
    list-style: none;
    list-style-type: none;
    font-size: .9em;
    margin: 0.4rem 0;
}
.country{
    display: flex;
    justify-content: space-between;
}
.disp-flex{
    display:flex;
    justify-content: space-between;
}

.disp-flex > div{
    width:23%;
    padding:15px 0px;
}


.subregion-header>div:nth-child(1){
    position: relative;
    left:30px;
}

.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
  margin: 15px 2px;
}

.accordion li {
  list-style-type: none;
}

.active, .accordion:hover {
  background-color: #ccc; 
}

.panel {
  margin-left: 5%;
  display: none;
  background-color: white;
  overflow: hidden;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <main class="container">
        <header>
            <div id="name">
                <h1>Name</h1>
            </div>
            <div id="capital">
                <h1>Capital</h1>
            </div>
            <div id="population">
                <h1>Population</h1>
            </div>
            <div id="area">
                <h1>Area</h1>
            </div>

        </header>

      
      <div id="container"></div>
      <div id="subRegionWrapper"> </div>
      <div id="listWrapper"></div>
      <script src="script.js"></script>
    </main>
</body>

</html>

Here I have little data table with information about countries, which are grouped by subregions. I would like to add sorting ability to lists, so that each column could be sorted. For example after clicking on CAPITAL on the very top, all of the positions should be sorted by capital.

I will be extremely grateful for any help. Here is my code:

let div = document.createElement('div');
document.body.appendChild(div);
let ul = document.createElement('ul');

div.appendChild(ul);

async function f() {
    //fetching and sorting data by regions and subregions
    const res = await fetch("https://restcountries.com/v3.1/all");
    const data = await res.json();
    data.sort((a, b) => {
        if (a.region > b.region) return 1;
        else if (a.region < b.region) return -1
        else {
            if (a.subregion > b.subregion) return 1;
            else return -1;
        }
    });
    
    const container = document.getElementById('container');
    const select = document.createElement('select');
    const olWrapper = document.getElementById('listWrapper');   
    const subRegionWrapper = document.getElementById('subRegionWrapper');

    const subRegions = data.reduce((r, a) => {
        r[a.subregion] = r[a.subregion] || [];
        r[a.subregion].push(a);
        return r;
    }, {});

   const dropdownValues = Object.keys(subRegions);
    const firstOption = document.createElement('option');
     firstOption.value = -1;
     firstOption.text = "Select a Subregion";
     select.appendChild(firstOption);
    dropdownValues.forEach(item => {
      const option = document.createElement('option');
      option.value = item;
      option.text = item;
      select.appendChild(option);
    });
  
 container.appendChild(select);

 select.onchange = (e) => {
   olWrapper.innerHTML = '';
   const subRegionName = e.target.value;
   const filteredValues = subRegions[subRegionName];
   const totalArea = filteredValues.reduce((acc, curr) => acc+curr.area,0);
   const totalPopulation = filteredValues.reduce((acc, curr) => acc+curr.population,0);
   const li = createSubregion(subRegionName, totalPopulation, totalArea);
   ul.innerHTML = '';
   ul.appendChild(li);
   subRegionWrapper.appendChild(ul);

    filteredValues.forEach(item => {
      const subLi = createCountry(item.name.common, item.capital, item.area, item.population);
      const subOl = document.createElement('ol');
      subOl.appendChild(subLi);
      olWrapper.appendChild(subOl);
    })     
 };

}

function createSubregion(name, population, area) {
    var li = document.createElement("li");
    li.setAttribute("class", "subregion");

    var header = document.createElement("div");
    header.setAttribute("class", "subregion-header disp-flex");

    var nameDiv = document.createElement("div");

    var nameh2 = document.createElement("h2");
    nameh2.innerText = name;

    nameDiv.appendChild(nameh2);
    header.append(nameDiv);

    var emptyDiv = document.createElement("div");
    header.appendChild(emptyDiv);

    var populationDiv = document.createElement("div");
    var populationh2 = document.createElement("h3");
    populationh2.innerText = population;

    populationDiv.appendChild(populationh2);
    header.append(populationDiv);

    var areaDiv = document.createElement("div");
    var areah2 = document.createElement("h3");
    areah2.innerText = area;

    areaDiv.appendChild(areah2);
    header.append(areaDiv);

    li.appendChild(header);
    return li;
}

function createCountry(name, capital, area, population) {
    var country = document.createElement("li");
    country.setAttribute("class", "country disp-flex")

    var namediv = document.createElement("div");
    var nameh4 = document.createElement("h4");
    nameh4.innerText = name;
    namediv.appendChild(nameh4);
    country.appendChild(namediv);

    var capitaldiv = document.createElement("div");
    var capitalh4 = document.createElement("h4");
    capitalh4.innerText = capital;
    capitaldiv.appendChild(capitalh4);
    country.appendChild(capitaldiv);

    var popdiv = document.createElement("div");
    var poph4 = document.createElement("h4");
    poph4.innerText = population;
    popdiv.appendChild(poph4);
    country.appendChild(popdiv);

    var areadiv = document.createElement("div");
    var areah4 = document.createElement("h4");
    areah4.innerText = area;
    areadiv.appendChild(areah4);
    country.appendChild(areadiv);


    return country;
}

f();
body {
    margin-left: 10%;
    margin-right: 10%;
    position: relative;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    background-color: aliceblue;
    font-family: 'Open Sans', Arial;
    font-size: 18px;
}
header{
    margin-left:10%;
    width: 70%;
    display:flex;
    justify-content: space-between;
    padding: 22px 0;
    color:rgb(5, 5, 5);
}
ul {
    list-style: none;
    list-style-type: none;
    outline: 2px solid #ddd;
    padding: 1rem 2rem;
    border-radius: 0.5rem;
    list-style-position: inside;
    color: blue;
}

ul ol { 
    color: rgb(197, 105, 18);
    list-style: none;
    list-style-type: none;
    font-size: .9em;
    margin: 0.4rem 0;
}
.country{
    display: flex;
    justify-content: space-between;
}
.disp-flex{
    display:flex;
    justify-content: space-between;
}

.disp-flex > div{
    width:23%;
    padding:15px 0px;
}


.subregion-header>div:nth-child(1){
    position: relative;
    left:30px;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <main class="container">
        <header>
            <div id="name">
                <h1>Name</h1>
            </div>
            <div id="capital">
                <h1>Capital</h1>
            </div>
            <div id="population">
                <h1>Population</h1>
            </div>
            <div id="area">
                <h1>Area</h1>
            </div>

        </header>
        <div id="container"></div>
        <div id="subRegionWrapper"> </div>
        <div id="listWrapper"></div>
        <script src="script.js"></script>
    </main>
</body>

</html>

3
  • What have you tried? What happened when you tried it? Commented Nov 15, 2022 at 20:28
  • How about using a library? github.com/Mottie/tablesorter Commented Nov 16, 2022 at 0:11
  • I noticed that your div#listWrapper element was not being re-drawn. See my response below, for a functioning sort/render example. I tried to keep as much of your original code intact as possible. Commented Nov 16, 2022 at 13:52

1 Answer 1

0

First, your rendering logic should be broken out into its own function.

Now, add click listeners to the headers, set the sort field, and re-render.

Note: I noticed that you added a <div> and <ul> at the very begignning of the script. This is not necessary.

// Elements
const container = document.querySelector('#container');
const subRegionWrapper = document.querySelector('#subRegionWrapper');
const listWrapper = document.querySelector('#listWrapper');

// State
let data = [];
let sortField;
let subRegion;

// Mappers and accessors
const getCapital = (region) => region.capital?.[0] ?? '';

// Sorters
const sortByName = (a, b) => a.name.common.localeCompare(b.name.common);
const sortByCapital = (a, b) => getCapital(a).localeCompare(getCapital(b));
const sortByPopulation = (a, b) => b.population - a.population;
const sortByArea = (a, b) => b.area - a.area;

// Fetch the data and populate the select
async function initialize() {
  const res = await fetch("https://restcountries.com/v3.1/all");
  data = await res.json();
  const subRegions = [...new Set(data.map(a => a.subregion))].sort();
  const select = document.createElement('select');
  const firstOption = document.createElement('option');
  firstOption.value = -1;
  firstOption.text = "Select a Subregion";
  select.appendChild(firstOption);
  subRegions.forEach(item => {
    select.appendChild(new Option(item, item));
  });
  container.appendChild(select);
  select.onchange = (e) => {
    listWrapper.innerHTML = '';
    setSubRegion(e.target.value);
  };
}

// Get current sorting function based on state
function getSorter() {
  switch (sortField) {
    case 'capital': return sortByCapital;
    case 'population': return sortByPopulation;
    case 'area': return sortByArea;
    case 'name':
    default: return sortByName;
  }
}

function render() {
  const sorter = getSorter();
  const filteredValues = data
    .filter(a => a.subregion === subRegion)
    .sort(sorter);     
  const totalArea = filteredValues.reduce((acc, curr) => acc + curr.area, 0);
  const totalPopulation = filteredValues.reduce((acc, curr) => acc + curr.population, 0);
  subRegionWrapper.innerHTML = '';
  const ul = document.createElement('ul');
  const li = createSubregion(subRegion, totalPopulation, totalArea);
  ul.innerHTML = '';
  ul.appendChild(li);
  subRegionWrapper.appendChild(ul);
  
  listWrapper.innerHTML = '';
  filteredValues.forEach(item => {
    const subLi = createCountry(item.name.common, item.capital, item.area, item.population);
    const subOl = document.createElement('ol');
    subOl.appendChild(subLi);
    listWrapper.append(subOl);
  });
}

function setSubRegion(subRegionName) {
  subRegion = subRegionName;
  render();
}

function createSubregion(name, population, area) {
  var li = document.createElement("li");
  li.setAttribute("class", "subregion");

  var header = document.createElement("div");
  header.setAttribute("class", "subregion-header disp-flex");

  var nameDiv = document.createElement("div");

  var nameh2 = document.createElement("h2");
  nameh2.innerText = name;

  nameDiv.appendChild(nameh2);
  header.append(nameDiv);

  var emptyDiv = document.createElement("div");
  header.appendChild(emptyDiv);

  var populationDiv = document.createElement("div");
  var populationh2 = document.createElement("h3");
  populationh2.innerText = population;

  populationDiv.appendChild(populationh2);
  header.append(populationDiv);

  var areaDiv = document.createElement("div");
  var areah2 = document.createElement("h3");
  areah2.innerText = area;

  areaDiv.appendChild(areah2);
  header.append(areaDiv);

  li.appendChild(header);
  return li;
}

function createCountry(name, capital, area, population) {
  var country = document.createElement("li");
  country.setAttribute("class", "country disp-flex")

  var namediv = document.createElement("div");
  var nameh4 = document.createElement("h4");
  nameh4.innerText = name;
  namediv.appendChild(nameh4);
  country.appendChild(namediv);

  var capitaldiv = document.createElement("div");
  var capitalh4 = document.createElement("h4");
  capitalh4.innerText = capital;
  capitaldiv.appendChild(capitalh4);
  country.appendChild(capitaldiv);

  var popdiv = document.createElement("div");
  var poph4 = document.createElement("h4");
  poph4.innerText = population;
  popdiv.appendChild(poph4);
  country.appendChild(popdiv);

  var areadiv = document.createElement("div");
  var areah4 = document.createElement("h4");
  areah4.innerText = area;
  areadiv.appendChild(areah4);
  country.appendChild(areadiv);

  return country;
}

function onHeaderSort(e) {
  e.preventDefault();
  sortField = e.currentTarget.getAttribute('data-field');
  render();
}

// Main
initialize();

// Add header click listeners
document.querySelectorAll('header div').forEach(header =>
  header.addEventListener('click', onHeaderSort));
body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.5rem;
  margin-left: 10%;
  margin-right: 10%;
  position: relative;
  min-height: 100vh;
  background-color: aliceblue;
  font-family: 'Open Sans', Arial;
  font-size: 18px;
}

header {
  display: flex;
  flex-direction: row;
  gap: 0.5rem;
  justify-content: center;
  align-items: center;
}

header div {
  padding: 0.25rem;
  border: thin solid grey;
  cursor: pointer;
}

header div h2 {
  margin: 0;
}

ul {
  list-style: none;
  list-style-type: none;
  outline: 2px solid #ddd;
  padding: 1rem 2rem;
  border-radius: 0.5rem;
  list-style-position: inside;
  color: blue;
}

ul ol {
  color: rgb(197, 105, 18);
  list-style: none;
  list-style-type: none;
  font-size: .9em;
  margin: 0.4rem 0;
}

.country {
  display: flex;
  justify-content: space-between;
}

.disp-flex {
  display: flex;
  justify-content: space-between;
}

.disp-flex>div {
  width: 23%;
  padding: 15px 0px;
}

.subregion-header>div:nth-child(1) {
  position: relative;
  left: 30px;
}
<header>
  <div data-field="name">
    <h2>Name</h2>
  </div>
  <div data-field="capital">
    <h2>Capital</h2>
  </div>
  <div data-field="population">
    <h2>Population</h2>
  </div>
  <div data-field="area">
    <h2>Area</h2>
  </div>
</header>
<main class="container">
  <div id="container"></div>
  <div id="subRegionWrapper">
    <ul><!-- Empty, but shows up via CSS --></ul>
  </div>
  <div id="listWrapper"></div>
</main>

Sign up to request clarification or add additional context in comments.

1 Comment

Ok, this is really useful, though I've changed the form data is displayed, so I also updated my code above. I will try to do the sort the way you did, but it might be kinda hard. What do I need to do to make sort such that if clicked once then it sorts ascending and after clicking for the second time it sorts descending?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.