JavaScript DOM Manipulation Basics Without a Framework

Before reaching for a framework, it’s worth knowing that vanilla JavaScript’s DOM APIs handle a surprising amount of interactivity cleanly on their own.

const button = document.querySelector('.add-item');
const list = document.querySelector('.item-list');

button.addEventListener('click', () => {
    const li = document.createElement('li');
    li.textContent = 'New item';
    li.className = 'item';

    const removeBtn = document.createElement('button');
    removeBtn.textContent = '×';
    removeBtn.addEventListener('click', () => li.remove());

    li.appendChild(removeBtn);
    list.appendChild(li);
});

// Event delegation: one listener handles clicks on many, even future, children
list.addEventListener('click', (e) => {
    if (e.target.matches('.remove-btn')) {
        e.target.closest('li').remove();
    }
});

Event delegation (attaching one listener to a parent instead of one per child) is the pattern that scales — it automatically handles elements added later without re-binding listeners, and uses far less memory than hundreds of individual event listeners.