<div id="todos">
  <form method="post" action="/my-backend-script">
    <input type="text" placeholder="What's next?" />
    <input type="submit" value="Add" />
  </form>
  <ul>
    <li>Item n.1</li>
    <li>Item n.2</li>
  </ul>
  <button>cleanup list</button>
</div>
#todos {
  font-family: sans-serif;
  color: #444;
}

/**
 * Todo List Input
 */
#todos form {
  display: flex;
}

#todos form input[type="text"],
#todos form input[type="submit"] {
  padding: 5px 10px;
  border: 1px solid #666;
  outline: none;
}

#todos form input[type="text"] {
  flex: 1;
}

#todos form input[type="text"]:focus {
  border-color: #369;
}

#todos form input[type="submit"] {
  display: none;
  text-transform: uppercase;
}

#todos form input[type="submit"]:active {
  border-color: #369;
  background-color: #369;
  color: #fff;
}

/*
By adding a simple "is-valid" class to the
Todo's FORM - that wraps all the other
components - then we can easily fix the
style to display a "ready to sent" UI.

It's Javascript that implements the logic
to add or remove the "is-valid" class.
*/
#todos form.is-valid input[type="submit"] {
  display: block;
}

#todos form.is-valid input[type="text"] {
  margin-right: 10px;
}



/**
 * Todos List
 */

#todos ul {
  margin: 10px 0 0 0;
  padding: 0;
  border: 1px solid #ddd;
  border-radius: 4px;
}

#todos ul li {
  padding: 5px 10px;
  border-bottom: 1px solid #ddd;
  transition: background-color 500ms ease;
}

#todos ul li:last-child {
  border-bottom: none;
}

#todos ul li:hover {
  background-color: #cde;
}

#todos ul li.is-done {
  font-style: italic;
  text-decoration: line-through;
  font-size: 10px;
  background: #eee;
}

#todos button {
  border: none;
  margin: 10px 0 0 0;
  padding: 0;
  color: #4ad;
  outline: none;
}
/**
 * Utility Functions
 */

// Shortcut for a crazy long API name!
const query = (selector, asList) =>
  asList
    ? document.querySelectorAll(selector)
    : document.querySelector(selector)

// Specialized version of "query"
const queryAll = selector =>
  query(selector, true)

const getTodosForm = () =>
  query('#todos form')

const getTodosInput = () =>
  query('#todos input[type="text"]')

const getTodosList = () =>
  query('#todos ul')

const getTodosItems = () =>
  queryAll('#todos li')

const getCleanupButton = () =>
  query('#todos button')

const getMessage = () =>
  getTodosInput().value

const messageIsValid = text =>
  text.length > 0

const cancelEvent = (event) => {
  event.preventDefault()
  event.stopPropagation()
}

const resetTodosInput = () => {
  const input = getTodosInput()
  input.value = ''
  input.focus()
}

const toggleItemStatus = event =>
  event.target.classList.toggle('is-done')

const addToggleListener = node =>
  node.addEventListener('click', toggleItemStatus)


/**
 * App's Logic
 */

const checkStatus = () => {
  const classList = getTodosForm().classList
  const message = getMessage()

  messageIsValid(message)
    ? classList.add('is-valid')
    : classList.remove('is-valid')
}

const appendMessage = (message) => {
  const textEl = document.createTextNode(message)
  const itemEl = document.createElement('li')
  itemEl.appendChild(textEl)
  
  // Add the event toggle listener for every
  // dynamically generated item
  addToggleListener(itemEl)
  
  getTodosList().appendChild(itemEl)
}

const onSubmit = (evt) => {
  cancelEvent(evt)
  
  const message = getMessage()
  messageIsValid && appendMessage(message)

  resetTodosInput()
}

const cleanup = () => {
  const items = queryAll('#todos li.is-done')
  items.forEach(node => node.remove())
}

// Setup the chat status listener
const inputEl = getTodosInput()
inputEl.addEventListener('input', checkStatus)
inputEl.addEventListener('change', checkStatus)
checkStatus()

// Setup the submit listener
const formEl = getTodosForm()
formEl.addEventListener('submit', onSubmit)

// Handle the toggle event to items that
// exist when the App boot
const items = getTodosItems()
items.forEach(addToggleListener)

// Setul a handler for the list cleanup
const cleanupEl = getCleanupButton()
cleanupEl.addEventListener('click', cleanup)
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.