mjwals
mjwals

Reputation: 152

Filter data when checking multiple checkboxes

Currently in my application the user can search game titles by typing in the game name. As they start typing the results update using the filter method. I'm now trying to do the same using multiple checkboxes. So when the user checks playstation and or xbox for example the results show games only from these categories. As they uncheck, the results will refresh with the updated values.

At the moment I've successfully created change events for all checkboxes and pushing their values to the filters.categories array. The problem I'm having is I have no idea how to connect this array to my existing filter method. I need to somehow loop though the array and compare the current value with category.name in my stores object to see whether they match. Is this possible to do inside the filter method? Any ideas where I'm going wrong?

Thanks in advance.

The html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <aside>
        <ul>
            <li>
                <label>
                    <input type="checkbox" value="playstation" class="category-cb">
                    <span>Playstation</span>
                </label>
                <li>
                    <label>
                        <input type="checkbox" value="xbox" class="category-cb">
                        <span>Xbox</span>
                    </label>
                </li>
                <li>
                    <label>
                        <input type="checkbox" value="nintendo" class="category-cb">
                        <span>Nintendo</span>
                    </label>
                </li>
            </li>
        </ul>
    </aside>
    <main>
        <input type="text" class="store-search" placeholder="Search">
        <ul class="grid"></ul>
    </main>

    <script src="app.js"></script>
</body>
</html>

The js:

/*----------------------------
    data
----------------------------*/

const stores = [{
    id: 0,
    name: 'The Last Of Us',
    studio: 'Naughtydog',
    thumbnail: '',
    category: {
        id: 0,
        name: 'Playstation'
    },
    price: 50.00
}, {
    id: 1,
    name: 'Animal Crossing',
    studio: 'Nintendo',
    thumbnail: '',
    category: {
        id: 1,
        name: 'Nintendo'
    },
    price: 60.00
}, {
    id: 2,
    name: 'Gears 5',
    studio: 'The Coalition',
    thumbnail: '',
    category: {
        id: 2,
        name: 'Xbox'
    },
    price: 10.00
}, {
    id: 3,
    name: 'DOOM Eternal',
    studio: 'id Software',
    thumbnail: '',
    category: {
        id: 3,
        name: 'Playstation'
    },
    price: 50.00
}, {
    id: 4,
    name: 'The Legend of Zelda: Link\'s Awakening',
    studio: 'Grezzo',
    thumbnail: '',
    category: {
        id: 4,
        name: 'Nintendo'
    },
    price: 35.00
}, {
    id: 5,
    name: 'Resident Evil 3',
    studio: 'Capcom',
    thumbnail: '',
    category: {
        id: 5,
        name: 'Playstation'
    },
    price: 40.00
}];

let filters = {
    searchText: '',
    categories: []
}

/*----------------------------
    functions
----------------------------*/

let filterStores = function (stores, filters) {

    let filteredStores = stores.filter(function (store) {
        let storeName = store.name.toLowerCase().includes(filters.searchText.toLowerCase());
        return storeName;
    });

    let grid = document.querySelector('.grid');
    grid.innerHTML = '';

    filteredStores.forEach(function (store) {
        let li = document.createElement('li');
        li.textContent = store.name;
        grid.appendChild(li);
    });
}

/*----------------------------
    events
----------------------------*/

document.addEventListener('DOMContentLoaded', function () {
    filterStores(stores, filters);

    document.querySelector('.store-search').addEventListener('input', function (event) {
        filters.searchText = event.target.value;
        filterStores(stores, filters);
    });

    document.querySelectorAll('.category-cb').forEach(function (checkbox) {
        checkbox.addEventListener('change', function (event) {
            if (checkbox.checked) {
                filters.categories.push(event.target.value);
                filterStores(stores, filters);
                console.log(filters.categories);
            }
        });
    });
});

https://jsfiddle.net/bjgp0ux4/

Upvotes: 1

Views: 2581

Answers (1)

FZs
FZs

Reputation: 18619

There are two main features to implement:

  1. Remove categories when they get unchecked

    I recommend you to use a Set as filters.categories, so you can easily check, add and remove entries by has, add and delete, respectively.

    //onchange event
    if (checkbox.checked) {
        filters.categories.add(event.target.value);
    } else {
        filters.categories.delete(event.target.value);
    }
    filterStores(stores, filters);
    
  2. Filter the stores based on categories as well

    Modify your .filter callback to this into account as well:

    store.name.toLowerCase().includes(filters.searchText.toLowerCase()) &&
    filters.categories.has(store.category.name.toLowerCase())
    

    Also, to show all when no categories are selected, do:

    store.name.toLowerCase().includes(filters.searchText.toLowerCase()) &&
    (
        filters.categories.has(store.category.name.toLowerCase()) ||
        filters.categories.size === 0
    )
    

All together:

/*----------------------------
    data
----------------------------*/

const stores = [{
    id: 0,
    name: 'The Last Of Us',
    studio: 'Naughtydog',
    thumbnail: '',
    category: {
        id: 0,
        name: 'Playstation'
    },
    price: 50.00
}, {
    id: 1,
    name: 'Animal Crossing',
    studio: 'Nintendo',
    thumbnail: '',
    category: {
        id: 1,
        name: 'Nintendo'
    },
    price: 60.00
}, {
    id: 2,
    name: 'Gears 5',
    studio: 'The Coalition',
    thumbnail: '',
    category: {
        id: 2,
        name: 'Xbox'
    },
    price: 10.00
}, {
    id: 3,
    name: 'DOOM Eternal',
    studio: 'id Software',
    thumbnail: '',
    category: {
        id: 3,
        name: 'Playstation'
    },
    price: 50.00
}, {
    id: 4,
    name: 'The Legend of Zelda: Link\'s Awakening',
    studio: 'Grezzo',
    thumbnail: '',
    category: {
        id: 4,
        name: 'Nintendo'
    },
    price: 35.00
}, {
    id: 5,
    name: 'Resident Evil 3',
    studio: 'Capcom',
    thumbnail: '',
    category: {
        id: 5,
        name: 'Playstation'
    },
    price: 40.00
}];

let filters = {
    searchText: '',
    categories: new Set
}

/*----------------------------
    functions
----------------------------*/

let filterStores = function (stores, filters) {

    let filteredStores = stores.filter(store => (
        store.name.toLowerCase().includes(filters.searchText.toLowerCase()) &&
        (
            filters.categories.has(store.category.name.toLowerCase()) ||
            filters.categories.size === 0 //No selected categories, show all
        )
    ));

    let grid = document.querySelector('.grid');
    grid.innerHTML = '';

    filteredStores.forEach(function (store) {
        let li = document.createElement('li');
        li.textContent = store.name;
        grid.appendChild(li);
    });
}

/*----------------------------
    events
----------------------------*/

document.addEventListener('DOMContentLoaded', function () {
    filterStores(stores, filters);

    document.querySelector('.store-search').addEventListener('input', function (event) {
        filters.searchText = event.target.value;
        filterStores(stores, filters);
    });

    document.querySelectorAll('.category-cb').forEach(function (checkbox) {
        checkbox.addEventListener('change', function (event) {
            if (checkbox.checked) {
                filters.categories.add(event.target.value);
            } else {
                filters.categories.delete(event.target.value);
            }
            filterStores(stores, filters);
        });
    });
});
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <aside>
        <ul>
            <li>
                <label>
                    <input type="checkbox" value="playstation" class="category-cb">
                    <span>Playstation</span>
                </label>
            </li>
            <li>
                <label>
                    <input type="checkbox" value="xbox" class="category-cb">
                    <span>Xbox</span>
                </label>
            </li>
            <li>
                <label>
                    <input type="checkbox" value="nintendo" class="category-cb">
                    <span>Nintendo</span>
                </label>
            </li>
        </ul>
    </aside>
    <main>
        <input type="text" class="store-search" placeholder="Search">
        <ul class="grid"></ul>
    </main>

    <script src="app.js"></script>
</body>
</html>

You can also use arrays:

/*----------------------------
    data
----------------------------*/

const stores = [{
    id: 0,
    name: 'The Last Of Us',
    studio: 'Naughtydog',
    thumbnail: '',
    category: {
        id: 0,
        name: 'Playstation'
    },
    price: 50.00
}, {
    id: 1,
    name: 'Animal Crossing',
    studio: 'Nintendo',
    thumbnail: '',
    category: {
        id: 1,
        name: 'Nintendo'
    },
    price: 60.00
}, {
    id: 2,
    name: 'Gears 5',
    studio: 'The Coalition',
    thumbnail: '',
    category: {
        id: 2,
        name: 'Xbox'
    },
    price: 10.00
}, {
    id: 3,
    name: 'DOOM Eternal',
    studio: 'id Software',
    thumbnail: '',
    category: {
        id: 3,
        name: 'Playstation'
    },
    price: 50.00
}, {
    id: 4,
    name: 'The Legend of Zelda: Link\'s Awakening',
    studio: 'Grezzo',
    thumbnail: '',
    category: {
        id: 4,
        name: 'Nintendo'
    },
    price: 35.00
}, {
    id: 5,
    name: 'Resident Evil 3',
    studio: 'Capcom',
    thumbnail: '',
    category: {
        id: 5,
        name: 'Playstation'
    },
    price: 40.00
}];

let filters = {
    searchText: '',
    categories: []
}

/*----------------------------
    functions
----------------------------*/

let filterStores = function (stores, filters) {

    let filteredStores = stores.filter(store => (
        store.name.toLowerCase().includes(filters.searchText.toLowerCase()) &&
        (
            filters.categories.includes(store.category.name.toLowerCase()) ||
            filters.categories.length === 0 //No selected categories, show all
        )
    ));

    let grid = document.querySelector('.grid');
    grid.innerHTML = '';

    filteredStores.forEach(function (store) {
        let li = document.createElement('li');
        li.textContent = store.name;
        grid.appendChild(li);
    });
}

/*----------------------------
    events
----------------------------*/

document.addEventListener('DOMContentLoaded', function () {
    filterStores(stores, filters);

    document.querySelector('.store-search').addEventListener('input', function (event) {
        filters.searchText = event.target.value;
        filterStores(stores, filters);
    });

    document.querySelectorAll('.category-cb').forEach(function (checkbox) {
        checkbox.addEventListener('change', function (event) {
            if (checkbox.checked) {
                filters.categories.push(event.target.value);
            } else {
                filters.categories.splice(filters.categories.indexOf(event.target.value), 1);
            }
            filterStores(stores, filters);
        });
    });
});
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <aside>
        <ul>
            <li>
                <label>
                    <input type="checkbox" value="playstation" class="category-cb">
                    <span>Playstation</span>
                </label>
            </li>
            <li>
                <label>
                    <input type="checkbox" value="xbox" class="category-cb">
                    <span>Xbox</span>
                </label>
            </li>
            <li>
                <label>
                    <input type="checkbox" value="nintendo" class="category-cb">
                    <span>Nintendo</span>
                </label>
            </li>
        </ul>
    </aside>
    <main>
        <input type="text" class="store-search" placeholder="Search">
        <ul class="grid"></ul>
    </main>

    <script src="app.js"></script>
</body>
</html>

Upvotes: 1

Related Questions