Bootstrap 4: add a “scroll to first invalid field”

2019-08-30 07:00发布

问题:

I am using Bootstrap 4 and i am stopping form submission if any of the fields are invalid with the below script. I am trying to figure out (with no success so far) what code do i need to add after the "event.stopPropagation();" in order to make the form scroll to the first invalid field that was found. Appreciate your help, thanks.

Form:

<form class="needs-validation" novalidate action="search.php" id="firstform" method="post" >

Prevent submission if invalid:

  <script>
// Example starter JavaScript for disabling form submissions if there are invalid fields
(function() {
  'use strict';
  window.addEventListener('load', function() {
    // Fetch all the forms we want to apply custom Bootstrap validation styles to
    var forms = document.getElementsByClassName('needs-validation');
    // Loop over them and prevent submission
    var validation = Array.prototype.filter.call(forms, function(form) {
      form.addEventListener('submit', function(event) {
        if (form.checkValidity() === false) {
          event.preventDefault();
          event.stopPropagation();
        }
        form.classList.add('was-validated');
      }, false);
    });
  }, false);
})();
</script>

Found the below code but couldn't find a way to embed it into the "disabling form submissions" script or use it as a standalone script:

$("#firstform").validate({
    focusInvalid: false,
    invalidHandler: function(form, validator) {

        if (!validator.numberOfInvalids())
            return;

        $('html, body').animate({
            scrollTop: $(validator.errorList[0].element).offset().top
        }, 2000);

    }
});

回答1:

You should use the input.form-control:invalid pseudo selector that is added to the elements you’ll see the :invalid and :valid styles applied to your form controls.

Although using custom validation you have to look for a lot of things that this article can cover up for you.

We will use the above selector document.querySelectorAll("input.form-control:invalid"); to get all the error fields list and scroll to the first error element in the form in case there are multiple errors.

See a demo below.

// Example starter JavaScript for disabling form submissions if there are invalid fields
(function() {
  'use strict';
  window.addEventListener('load', function() {
    // Fetch all the forms we want to apply custom Bootstrap validation styles to
    var forms = document.getElementsByClassName('needs-validation');
    // Loop over them and prevent submission
    var validation = Array.prototype.filter.call(forms, function(form) {
      form.addEventListener('submit', function(event) {

        if (form.checkValidity() === false) {
          event.preventDefault();
          event.stopPropagation();

          var errorElements = document.querySelectorAll(
            "input.form-control:invalid");
          errorElements.forEach(function(element) {
            element.parentNode.childNodes.forEach(function(node) {
              if (node.className == 'valid-feedback') {
                node.className = 'invalid-feedback';
                node.innerText =
                  'Please choose a Gender';
              }
            });
          });
          $('html, body').animate({
            scrollTop: $(errorElements[0]).offset().top
          }, 2000);
        }
        form.classList.add('was-validated');
      }, false);
    });
  }, false);
})();
input[type=text],
input[type=email],
input[type=number],
textarea,
fieldset {
  /* required to properly style form 
   elements on WebKit based browsers */
  -webkit-appearance: none;
  width: 100%;
  border: 1px solid #333;
  margin: 0;
  font-family: inherit;
  font-size: 90%;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

<form class="needs-validation" novalidate>
  <div class="form-row">
    <div class="col-md-4 mb-3">
      <label for="gender">Male</label>
      <input id="gender_male" type="radio" name="gender" class="form-control col-sm-2" required>
      <label for="gender">Female</label>
      <input id="gender_female" type="radio" name="gender" class="form-control col-sm-2">
      <label for="gender">Other</label>
      <input id="gender_other" type="radio" name="gender" class="form-control col-sm-2">

      <div class="valid-feedback">
        Looks good!
      </div>
    </div>
  </div>
  <div class="form-row">
    <div class="col-md-4 mb-3">
      <label for="validationCustom01">First name</label>
      <input type="text" class="form-control" id="validationCustom01" placeholder="First name" value="Mark" required>
      <div class="valid-feedback">
        Looks good!
      </div>
    </div>
    <div class="col-md-4 mb-3">
      <label for="validationCustom02">Last name</label>
      <input type="text" class="form-control" id="validationCustom02" placeholder="Last name" value="Otto" required>
      <div class="valid-feedback">
        Looks good!
      </div>
    </div>

  </div>
  <div class="form-row">
    <div class="col-md-4 mb-3">
      <label for="validationCustomUsername">Username</label>
      <div class="input-group">
        <div class="input-group-prepend">
          <span class="input-group-text" id="inputGroupPrepend">@</span>
        </div>
        <input type="text" class="form-control" id="validationCustomUsername" placeholder="Username" aria-describedby="inputGroupPrepend" required>
        <div class="invalid-feedback">
          Please choose a username.
        </div>
      </div>
    </div>
  </div>
  <div class="form-row">
    <div class="col-md-6 mb-3">
      <label for="validationCustom03">City</label>
      <input type="text" class="form-control" id="validationCustom03" placeholder="City" required>
      <div class="invalid-feedback">
        Please provide a valid city.
      </div>
    </div>

  </div>
  <div class="form-row">
    <div class="col-md-3 mb-3">
      <label for="validationCustom04">State</label>
      <input type="text" class="form-control" id="validationCustom04" placeholder="State" required>
      <div class="invalid-feedback">
        Please provide a valid state.
      </div>
    </div>
    <div class="col-md-3 mb-3">
      <label for="validationCustom05">Zip</label>
      <input type="text" class="form-control" id="validationCustom05" placeholder="Zip" required>
      <div class="invalid-feedback">
        Please provide a valid zip.
      </div>
    </div>
  </div>
  <div class="form-row">
    <div class="col-md-3 mb-3">
      <label for="validationCustom05">Zip</label>
      <input type="text" class="form-control" id="validationCustom05" placeholder="Zip" required>
      <div class="invalid-feedback">
        Please provide a valid zip.
      </div>
    </div>
  </div>
  <div class="form-group">
    <div class="form-check">
      <input class="form-check-input" type="checkbox" value="" id="invalidCheck" required>
      <label class="form-check-label" for="invalidCheck">
                Agree to terms and conditions
            </label>
      <div class="invalid-feedback">
        You must agree before submitting.
      </div>
    </div>
  </div>
  <button class="btn btn-primary" type="submit">Submit form</button>
</form>



回答2:

If you are using jQuery and only supporting modern browsers (which is likely if you're using Bootstrap 4), you can use the following code to validate the form and scroll to the field with an error:

$('form#contact').submit(function(e) {
    var form = $(this);

    // HTML5 validility checker
    if (form[0].checkValidity() === false) {
        // not valid
        form.addClass('was-validated');
        $('html,body').animate({scrollTop: $('.was-validated .form-control:invalid').first().offset().top - 50},'slow');
        e.preventDefault();
        e.stopPropagation();
        return;
    }
    // valid, do something else ...
});