<body>
    <form id="form" novalidate>
        <p>
            <label>
                <span>必須チェック: </span>
                <input type="text" id="input" required>
            </label>
            <span class="errorMessage"></span>
        </p>
        <p>
            <label>
                <span>メールアドレス形式チェック: </span>
                <input type="email" required>
            </label>
            <span class="errorMessage"></span>
        </p>
        <p>
            <label>
                <span>特定の形式チェック: </span>
                <input type="text" title="「banana」もしくは「cherry」を入力してください。" required pattern="[Bb]anana|[Cc]herry">
            </label>
            <span class="errorMessage"></span>
        </p>
        <p>
            <button>submit</button>
        </p>
    </form>
</body>
label.is-error > span {
  color: red;
}
label.is-error > input {
  border-color: red;
  background-color: rgba(200, 0, 0, 0.1);
}

.errorMessage {
  display: block;
  color: red;
  font-size: 12px;
}
const form = document.getElementById('form');
const inputElms = form.querySelectorAll('input');

form.addEventListener(
  'submit',
  (e) => {
    e.preventDefault();
    inputElms.forEach((input) => {
      const label = input.closest('label');
      label.classList.remove('is-error');
      const errorMessage = label.nextElementSibling;
      errorMessage.textContent = '';
    });
    const isValid = form.checkValidity();
    if (isValid) {
      alert('submit!');
    }
  },
  { passive: false }
);
inputElms.forEach((input) => {
  input.addEventListener('invalid', (e) => {
    const currentTarget = e.currentTarget;
    const label = currentTarget.closest('label');
    label.classList.add('is-error');
    const errorMessage = label.nextElementSibling;
    errorMessage.textContent = currentTarget.validationMessage;
  });
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.