<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>
</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;
}
/**
 * Utility Functions
 */

const getTodosForm = () =>
  document
    .querySelector('#todos form')

const getTodosInput = () =>
  document
    .querySelector('#todos input[type="text"]')

const getTodosList = () =>
  document
    .querySelector('#todos ul')

const getTodosItems = () =>
  document
    .querySelectorAll('#todos li')

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 removeNode = event =>
  event.target.remove()

const addDeleteListener = node =>
  node.addEventListener('click', removeNode)


/**
 * 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 listener for every
  // dynamically generated item
  addDeleteListener(itemEl)
  
  getTodosList().appendChild(itemEl)
}

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

  resetTodosInput()
}

// 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 items that exist at boot time
const items = getTodosItems()
items.forEach(addDeleteListener)
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.