0

I am using vanilla JS, I can push to the array, and remove last item from array. Next I am trying to remove array item on click. The function I have created below kind of works, but it's not correct? Where am I going wrong? I don't want to remove the item from the DOM, I would rather remove the item from the array and then update the UI state.

So code below:

let someData = [
    'This is some serious data part 2',
    'This is some serious data part 3', 
    'This is some serious data part 4', 
]; 

const loopIt = (data) => {
    let item = '';
    for (let i = 0; i < data.length; i++) {
        item += `<li>${data[i]}<button class='testBtn'>Remove ${i}</button></li>`;
    }
    return item;
}

let getHtml = () => {
    domElem.selector.innerHTML = loopIt(someData);
}

const clickEvents = () => {
    domElem.button.addEventListener ('click', function () {
        let inputData = domElem.input.value;
        if (inputData.length > 0) {
            someData.push(inputData);
        } else {
            alert('no input');
        }
        getHtml();
    });
    domElem.buttonRemove.addEventListener ('click', function () {
        someData.pop();
        getHtml();   
    });
}


const removeArrayItem = () => {
    let queryS = document.querySelectorAll('.testBtn');
        for (let i = 0; i < queryS.length; i++) {
            let index = queryS[i];
            index.addEventListener('click', function () {
                alert(i);
                someData.splice(i, 1);
                console.log(someData);
                getHtml();
            });
        };
}

const init = () => {
    getHtml();
    elemAttributes();
    clickEvents();
    removeArrayItem();
}

let initialise = init();

My removeArrayItem is

  • 1 - querying the dom (let queryS = document.querySelectorAll('.testBtn');)
  • 2 - looping over the array, returning the index value (i) (Loop function)
  • 3 - adding event listener to each element in the array
  • 4 - removing the array item (Splice method) -
  • 5 - updating the UI (getHtml() function);

But something somewhere is going wrong? as the UI does not update after first click.

If I push items I can't remove them on click.

Sole
  • 3,100
  • 14
  • 58
  • 112

2 Answers2

1

This has to do with that you're resetting the innerHTML where your buttons are in. This means that all bindings to event listeners are gone. So you'll have to either reset the event listeners every time your HTML is re-rendered or use event delegation.

Event delegation is having a single event listener that listens for the events that bubble up. For example: you click your button, the click event bubbles up the DOM and reaches the element where you listen for the event. From there you can determine what element was clicked and what to do next. The advantage of this method is that the children within that element that you are listening to can be dynamic, so added, moved, removed and changed without having to account for it.

In your loopIt function pass the i index to an attribute in the button. You are no longer going to listen for the click on the button itself, so it needs to hold a bit of data for the event listener to handle. The value attribute is perfect for this purpose.

const loopIt = (data) => {
    let item = '';
    for (let i = 0; i < data.length; i++) {       // Right here ↓
        item += `<li>${data[i]}<button class="testBtn" value="${i}">Remove ${i}</button></li>`;
    }
    return item;
}

The button now knows the index of the array index that needs to spliced whenever you click the button.

Modify the removeArrayItem function. Instead of listening for click on every button, listen for the click event on the parent element, which I think is domElem.selector?

const removeArrayItem = () => {
    domElem.selector.addEventListener('click', function(event) {
        const clickedElem = event.target; // This is the element that fired the click.
        if (clickedElem.classList.contains('testBtn')) {
            const index = clickedElem.value // The index in the array.
            someData.splice(index, 1); // Splice array.
            getHtml(); // Re-render the HTML.
        }
    });
}

This adds a single event listener and gets the Event object for the target, which is the element that triggered the click event. It then checks if it has a class that contains the testBtn class. If it does, it gets the i index from the value attribute, splices the array and re-renders the HTML.

Try it out. Your clicks should work every time.

Emiel Zuurbier
  • 19,095
  • 3
  • 17
  • 32
  • Thank you, that was well explained and give me a foundation on what to look into more – Sole Sep 26 '20 at 20:50
1

I would approach this challenge by finding the element and removing the element in the array, after the deletion I would empty the innerHTML and rebuild the UI based on the new Array, much like you described.

My code solution would look like this:

Here I assign each item via the onClick property. I created a method called removeItem and pass a Parameter which response to the element in the array.

The method removeItem can then remove the given element, and then promptly rebuild the UI.

let someData = [
    'This is some serious data part 2',
    'This is some serious data part 3',
    'This is some serious data part 4'
];

const loopIt = (data) => {
    let item = '';
    for (let i = 0; i < data.length; i++) {
        item += `<li>${data[i]}<button onClick='removeItem(${i})'class='testBtn'>Remove ${i}</button></li>`;
    }
    return item;
};

let removeItem = (index) => {
    someData.splice(index, 1);
    getHtml();
};
let getHtml = () => {
    domElem.innerHTML = '';
    domElem.innerHTML = loopIt(someData);
};

const init = () => {
    getHtml();
};

let initialise = init();