How to add event listeners to multiple buttons which each modify different parts of the data

Viewed 277

I am working on my first personal project and I am almost finished, but I am struggling to get some buttons to work. My project is one where bookmarks can be saved, and there is a column for bookmarks that are favorites, and a column for the other saved bookmarks. These buttons each correspond to an item retrieved from the database (I'm using Firestore for my database, but for this post I have modified it to eliminate the need for Firestore), and hence are not hardcoded into the html file. When a new bookmark is saved, it has a name (such as "Reddit"), a url ("www.reddit.com"), and a boolean for its favorite status (true if it is a favorite, false if otherwise). I want each bookmark to have an associated button which can be clicked to move the bookmark to the other column. I am stuck on how to add an event listener to each item in the database that only modifies the data from that one item. The code is below (there is more explanation at the bottom.

The full code can be found here (includes everything you need to run the program in full): https://github.com/matt-manning/Bookmarker

Here is a minimized version (still sufficient enough to be used on its own):

const bookmarkList = document.querySelector('#other-list');
const favoriteList = document.querySelector('#favorite-list');
const form = document.querySelector('#add-bookmark');

function start() {
    const db = [
        {website: 'Reddit', url: 'www.reddit.com', favorite: true},
        {website: 'Google', url: 'www.google.com', favorite: false},
        {website: 'Netflix', url: 'www.netflix.com', favorite: true},
        {website: 'YouTube', url: 'www.youtube.com', favorite: false}
    ];
    const bookmarkData = setupBookmarks(db);
    registerBookmarkEventListeners(db, bookmarkData);
}

const setupBookmarks = (data) => {
    let favoritesHTML, bookmarksHTML = '';
    const bookmarkData = [];
    if (data && data.length) {
        data.forEach(doc => {
            const bookmarkId = Math.floor(Math.random() * 10000000);
            const bookmark = doc;
            if (bookmark.favorite) {
                favoritesHTML += bookmarkHTML(bookmark, bookmarkId);
            } else {
                bookmarksHTML += bookmarkHTML(bookmark, bookmarkId);
            }
            bookmarkData.push({bookmark, bookmarkId});
        })
    }
    favoriteList.innerHTML = favoritesHTML;
    bookmarkList.innerHTML = bookmarksHTML;
    return bookmarkData;
}

const bookmarkHTML = (bookmark, bookmarkId) => {
    let html = '';
    // cut for minimizing; here is a potentially relevant snippet. Full code can be found in the github link above.
    const li =
        `<li>
            <button class='btn-flat waves-light btn-small right listener' id='${bookmarkId}'>
                <i class='material-icons left'>compare_arrows</i>switch</button>
        <li>`;
    html += li;
    return html;
}

My problems lie in the registerBookmarkEventListeners function. What I want this function to do is add an event listener to a button on each bookmark, and when the button is clicked, the 'favorite' boolean in said bookmark changes to the opposite, which would then make the bookmark switch to the other column.

Here are a couple attempts I have made (more attempts can be found in the github link):

const registerBookmarkEventListeners = (data, bookmarkData) => {
    document.querySelectorAll('.listener').forEach(elem => {
        elem.addEventListener('click', e => {
            e.preventDefault();
            const bookmark = elem;
            if (bookmark.favorite) {
                bookmark.favorite = false;
            } else {
                bookmark.favorite = true;
            }
        })
    })
}

const registerBookmarkEventListeners = (data, bookmarkData) => {
    const {bookmark, bookmarkId} = bookmarkData;
    bookmarkData.forEach(item => {
        const btnElement = document.querySelector(`#${bookmarkId}`);
        btnElement.addEventListener('click', e => {
            e.preventDefault();
            if (bookmark.favorite) {
                bookmark.favorite = false;
            } else {
                bookmark.favorite = true;
            }
        })
    })
}

I understand why my attempts above don't work. The first one doesn't even use either of the parameters given with the data from the database. The second one uses bookmarkId, but it doesn't 'connect' (if that is the right word) to the data it is supposed to be modifying. I'm struggling to find out how to make this addEventListener work.

So, how do I add event listeners to buttons through a loop which each directly modify a different part of the data?

Thanks for any help.

Also, for completeness, here is the relevant HTML needed to run the program:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bookmarker</title>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
    <div class='row logged-in show-none'>
        <div class="col s6">
            <h5>Favorites</h5>
            <ul id='favorite-list'></ul>
        </div>
        <div class="col s6">
            <h5>Bookmarks</h5>
            <ul id='other-list'></ul>
        </div>
    </div>
    <script src='script.js'></script>
</body>
</html>
2 Answers

If you get the item in bookmarkData.forEach(item => ... then you should put the destructuring in that brace and have something like

const { bookmarkId, bookmark } = item;

The way I've done that:

  1. Pass data with id's to registerBookmarkEventListeners function.
  2. ForEach on every element.
  3. Find button with id that match data id and add listener.
  4. Change favorite of that element.

Below you can find code if you stuck. Notice that I changed id's of buttons in your html code by adding 'x' before number and you still need to figure how to render items after change.

registerBookmarkEventListeners(bookmarkData); // 1

const registerBookmarkEventListeners = (data) => {
    data.forEach(bookmark => { // 2
        document.querySelector(`#x${bookmark.bookmarkId}`).addEventListener('click', (e) => { // 3
                bookmark.bookmark.favorite = !bookmark.bookmark.favorite; // 4
                console.log(bookmark);
        })
    })
}

Related