javascripthtmlcssdropdowndatalist

How to add ability to dropdown for list?


I've created list of list containing some data about countries (grouped by subregions, each country contains following data: name, capital, population and area). Now I would like to add ability to hide or show all the countries belonging to its subregion. For example after clicking on subregion North America all the countries belonging to NA should show up, or hide if they were shown up already. Right now all I can do is to show the whole list of subregions with all its countries and I am clueless how to add the ability of dropdown. I will be extremely grateful for help.

Below is source code of what i was able to do.

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;
        }
    })
    //count no of subregions and totals of subregion area and population data
    var prevSR = null;
    var cntSR = 0;
    var subregPop = [];
    var subregArea = [];
    var localPop = 0;
    var localArea = 0;
    for (const x of data) {
        if (prevSR != x.subregion) {
            cntSR += 1;
            prevSR = x.subregion;
            subregPop.push(localPop);
            subregArea.push(localArea);
            localArea = 0;
            localPop = 0;
        }
        localArea += x.area;
        localPop += x.population;
    }
    //loop to upload data to lists
    var i = 0
    prevSubregion = data[0].subregion;
    for (var a = 0; a < cntSR; a++) {
        //creating subregion
        let li = createSubregion(data[i].subregion, subregPop[a + 1], subregArea[a + 1]);
        let subOl = document.createElement('ol');

        while (prevSubregion == data[i].subregion) {
            //creating country
            prevSubregion = data[i].subregion;
            subLi = createCountry(data[i].name.common, data[i].capital, data[i].area, data[i].population);
            subOl.appendChild(subLi);
            i += 1;
        }
        prevSubregion = data[i].subregion;
        li.appendChild(subOl);
        ul.appendChild(li);
    }

}

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 {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background-color: aliceblue;
    font-family: 'Open Sans', Arial;
    font-size: 18px;
}
header{
    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>

        <script src="script.js"></script>
    </main>
</body>

</html>

Second thing, but not as important as first, I would also need to add feature of sorting and filtering each column after clicking div element on the very top. So for example after clicking AREA, on the header section, countries should be sorted by their area. And the filter option allowing to show only countries that, for example, start with letter 'A'.


Solution

  • You can group the subregions first like,

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

    And then create a select element and inject the text and value respectively.

    Then handle the onchange and get the slected subregion data instead of all at once like subRegions[subRegionName] .

    Forked example:

    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 {
        min-height: 100vh;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        background-color: aliceblue;
        font-family: 'Open Sans', Arial;
        font-size: 18px;
    }
    header{
        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>
        </main>
    </body>
    
    </html>

    Update:

    Here is the updated version with each sub region is grouped as an accordion and on toggle, the respective countries under the sub region were displayed..

    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 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 {
        min-height: 100vh;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        background-color: aliceblue;
        font-family: 'Open Sans', Arial;
        font-size: 18px;
    }
    header{
        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;
      padding: 18px;
      width: 100%;
      border: none;
      text-align: left;
      outline: none;
      font-size: 15px;
      transition: 0.4s;
      margin: 15px 2px;
    }
    
    .accordion li {
      list-style-type: none;
    }
    
    .active, .accordion:hover {
      background-color: #ccc; 
    }
    
    .panel {
      padding: 0 18px;
      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>
        </main>
    </body>
    
    </html>