<form id="chat" method="post" action="/my-backend-script">
  <input type="text" placeholder="What's on your mind?" />
  <input type="submit" value="Post" />
</form>
/*
All the basic style definition implement
a "disabled" status, where you can't
actually send in the message.

The user is required to write a message
before the "POST" button becomes visible.
*/
#chat {
  display: flex;
}

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

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

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

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

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

/*
By adding a simple "is-valid" class to the
chat'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.
*/
#chat.is-valid input[type="submit"] {
  display: block;
}

#chat.is-valid input[type="text"] {
  margin-right: 10px;
}
/*
LIBRARY FUNCTIONS
small and easy to understand code blocks
that solve very specific problems.

we will use those functions later on
to create the App's logic
*/

const getChat = () =>
  document
    .getElementById('chat')

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

const getChatButton = () =>
  document
    .querySelector('#chat input[type="submit"]')

const getChatMessage = () =>
  getChatInput().value

const resetChatInput = () => {
  const input = getChatInput()
  input.value = ''
  input.focus()
}
  
const messageIsValid = text =>
  text.length > 0

const sendMessage = text =>
  alert(`Send: ${text}`)

const showError = () =>
  alert('Please type the message you want to sent')

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

/*
MAIN APP LOGIC
handle user input & events
*/
const onFormSubmit = (event) => {
  cancelEvent(event)
  
  const message = getChatMessage()
  
  messageIsValid(message)
    ? sendMessage(message)
    : showError()

  resetChatInput()
}

// This doesn't look like an event handler
// because we don't use the "event" param.
//
// It still works because any function can
// be assigned as event handlers!
//
// It's up to you - the engineer - to create
// the correct valuable logic for it.
const updateChatStatus = () => {
  const className = 'is-valid'
  const classList = getChat().classList
  const message = getChatMessage()

  messageIsValid(message)
    ? classList.add(className)
    : classList.remove(className)
}

getChat().addEventListener('submit', onFormSubmit)

// We can observe 2 different events that are
// related to the user input.
//
// This way we can correctly update the UI after
// each keystroke and also after the "resetChat()"
getChatInput().addEventListener('input', updateChatStatus)
getChatInput().addEventListener('change', updateChatStatus)

// But we can also simply call the newly created
// function when the App starts, so to be sure
// that the UI starts in the correct state.
//
// CHALLENGE:
// try to start the app with a pre-defined
// message using the "input value" attribute.
// then see how the app behave with or without
// this initial call!
updateChatStatus()
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.