javascripthtmlcssarrayshtmlcollection

How to keep updating array created by converting html collection?


So, I'm creating this system of adding and removing boxes. By default, there are 4 boxes. Users can delete those boxes or add new boxes. Problem is that I can only remove the default 4 boxes I created initially. The new boxes are not getting removed.

I think because I'm converting the HTML Collection (live) into an array (non-live) so as to be able to add event listener to each box.. the problem is occurring. Because, I guess the array is not getting updated even though HTML Collection gets updated.

How can I update the array as soon as HTML Collection gets updated?

        var boxes = document.getElementsByClassName("box");

        function removeBox(i) {
            var boxWrapID = "bw-" + i;
            var boxToDelete = document.getElementById(boxWrapID);
            boxToDelete.remove();
        }

        function addBox() {
            var newBoxWrap = document.createElement("div");
            newBoxWrap.innerHTML = 
            `
            <div class="box">x</div>
            <p class="content"></p>
            `
            document.getElementById("container").appendChild(newBoxWrap);
            addIDs(newBoxWrap);
        }

        function addIDs(newBoxWrap) {
            var numOfBoxes = document.getElementById("container").childElementCount;
            var newWrapID = "bw-" + numOfBoxes;
            newBoxWrap.setAttribute('id', newWrapID);
            newBoxWrap.setAttribute('class', "box-wrap");
            addContent(numOfBoxes, newWrapID);
        }

        function addContent(numOfBoxes, newWrapID) {
            var newBox = document.getElementById(newWrapID);
            newBox.getElementsByClassName("content")[0].innerText = numOfBoxes;
        }

        // TO REMOVE A BOX
        Array.from(boxes).forEach(item => {
            item.addEventListener('click', function(event) {
                var i = event.target.id;
                alert("Box #" + i + " was clicked");
                removeBox(i);
            });
        });

        // TO ADD A BOX
        document.getElementById("btn").addEventListener('click', addBox);
        body {
            height: 100vh;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }
        .box {
            width: 50px;
            height: 50px;
            background-color: palevioletred;
            text-align: center;
            font-family: sans-serif;
            line-height: 50px;
            cursor: pointer;
        }
        .box-wrap {
            width: 20rem;
            height: 50px;
            border: 1px solid #ccc;
            margin-bottom: 20px;
            cursor: pointer;
            display: flex;
            gap: 8rem;
        }
        .content {
            display: inline;
        }
<!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>Problem</title>
</head>
<body>
    <div id="container">
        <div class="box-wrap" id="bw-1">
            <div class="box" id="1">x</div>
            <p class="content">1</p>
        </div>
        <div class="box-wrap" id="bw-2">
            <div class="box" id="2">x</div>
            <p class="content">2</p>
        </div>
        <div class="box-wrap" id="bw-3">
            <div class="box" id="3">x</div>
            <p class="content">3</p>
        </div>
        <div class="box-wrap" id="bw-4">
            <div class="box" id="4">x</div>
            <p class="content">4</p>
        </div>
    </div>
    <button type="button" id="btn">Add</button>
</body>
</html>


Solution

  • Listen clicks on #container and use event.target.closest('.box') to determine whether you clicked anything inside the cross button. Also you don't need id attributes to determine the box wrapper to remove, use the same .closest() to find the box wrapper.

    So you don't have to use box IDs at all.

    Also you have wrong logic of assigning IDs because after deletion the number of boxes decreases and an ID could be reused and lead to a wrong box reference.

    So use some global ID incremented variable.

    To simplify adding a new box you could use insertAdjacentHTML(). It's a new feature of JS so maybe a polyfill could be needed for some browsers.

    const container = document.querySelector('#container');
    let id = container.children.length + 1;
    
    // TO REMOVE A BOX
    container.addEventListener('click', event =>
      event.target.closest('.box')?.closest('.box-wrap').remove()
    );
    
    // TO ADD A BOX
    document.getElementById("btn").addEventListener('click', () =>     
      container.insertAdjacentHTML('beforeend',
        `<div class="box-wrap">
          <div class="box">x</div>
          <p class="content">${id++}</p>
          </div>
        `)
    );
    body {
                height: 100vh;
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
            }
            .box {
                width: 50px;
                height: 50px;
                background-color: palevioletred;
                text-align: center;
                font-family: sans-serif;
                line-height: 50px;
                cursor: pointer;
            }
            .box-wrap {
                width: 20rem;
                height: 50px;
                border: 1px solid #ccc;
                margin-bottom: 20px;
                cursor: pointer;
                display: flex;
                gap: 8rem;
            }
            .content {
                display: inline;
            }
    <!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>Problem</title>
    </head>
    <body>
        <div id="container">
            <div class="box-wrap">
                <div class="box">x</div>
                <p class="content">1</p>
            </div>
            <div class="box-wrap">
                <div class="box">x</div>
                <p class="content">2</p>
            </div>
            <div class="box-wrap">
                <div class="box">x</div>
                <p class="content">3</p>
            </div>
            <div class="box-wrap">
                <div class="box">x</div>
                <p class="content">4</p>
            </div>
        </div>
        <button type="button" id="btn">Add</button>
    </body>
    </html>