<form id="form">
  
  <div>
    <input type="text" name="_text" required/>
  </div>
  
  <button id="submit" type="submit" disabled>送信</button>
</form>
input,
select {
  &:valid {
    border: 1px solid green;
  }
  &:invalid {
    border: 1px solid red;
  }
}

// 案件上デフォルトのstyleで使われることがほぼないのでstyleは適当
[type='radio'],
[type='checkbox'] {
  &:valid {
    box-shadow: green 0px 0px 3px 1px;
  }
  &:invalid {
    box-shadow: red 0px 0px 3px 1px;
  }
}

small {
  color: red;
}
View Compiled
const $form = document.getElementById('form');
const $submit = document.getElementById('submit');

// 親要素(form)にイベントをつけると子要素のイベントが拾える
$form.addEventListener('change', update);
$form.addEventListener('input', update);

// デフォルトではEnterキー押下でも送信されてしまうのでsubmitイベントで制御する
$form.addEventListener('submit', (e) => {
  e.preventDefault();
  // $form.submit(); or ajaxする など
});

function update(e) {
  // バリデーションが総合的に通っているかどうかのフラグ
  const isValid = $form.checkValidity();
  
  if (isValid) {  // 通っていれば活性化
    $submit.removeAttribute('disabled');
    return;
  }
  // 通っていなければ非活性にする
  $submit.setAttribute('disabled', 'disabled');
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.