<h3>Todos:</h3>
<ul id="list1"></ul>
<hr />
<button id="btn1">+ Add new item</button>
// This the initial state of the app
const todos = [
  'buy milk',
  'clean kitchen',
  'learn js',
]

// This helper function will "render" a new
// DOM item into an HTML list
const appendItem = (item) => {
  const el = document.createElement('li')
  const elText = document.createTextNode(item)
  el.appendChild(elText)
  document.getElementById('list1').appendChild(el)
}

// Yes, DOM APIs often return arrays that
// you can loop through!
// And arrow functions let us write a very
// simple and readable piece of code!
const clearList = () =>
  document
    .querySelectorAll('#list1 *')
    .forEach(item => item.remove())

// This is the main rendering logic, it is
// often a simple 2 steps operation:
// 1. cleanup
// 2. re-create everything
const renderList = () => {
  clearList()
  todos.forEach(appendItem)
}

// Here we write the logic to apply when
// the "Add new item" is pressed:
const addNewItem = () => {
  
  /**
   * SOLUTION!
   * this is where we actually make the
   * change to the state of our app. It's
   * clean and simple thanks to the
   * Array APIs!
   */
  todos.push(`Item n.${todos.length + 1}`)
  
  /**
   * Once the data is changes, we simply
   * call the methos that translates data
   * into DOM.
   *
   * This concept is the foundation of
   * every modern framework such Vue or
   * React.
   *
   * We work so to keep our data
   * up-to-date, then we use the frameworks
   * to simply "translate"
   * our data into DOM elements for the
   * browser.
   */
  renderList()
}

// Associate the logic to the user action
document
  .querySelector('#btn1')
  .addEventListener('click', addNewItem)


// As soon the app starts, we run the first
// rendering so to show the initial state.
renderList()

Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.