HTML 5 验证自定义

HTML 5 Validation Customization

我想要一个表格:

  1. 以自定义格式而非默认样式显示验证消息。
  2. 一次显示所有无效字段气泡,而不是一次显示一个。

现在,我一直受困于无聊的特定于浏览器的消息外观,在更正上一个错误之前我看不到下一个错误。这是一个非常糟糕的用户体验,因此寻找一些关于如何解决这个问题的建议。

这是我当前的 JavaScript 代码:

const contactUsForm = document.querySelector('#Form');

if (contactUsForm) {

    function Validate() {
    
        validatedFields = contactUsForm.querySelectorAll('[data-validation-required],[data-validation-format]');
        validatedFields.forEach(field => {
            /* RegEx patterns */
            const emailPattern = /^((([a-z]|\d|[!#$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
                
            if (field.getAttribute('type') === 'email')
            {
                field.setAttribute('pattern', emailPattern);
            }


            if (field.validity.valueMissing) {
                field.setCustomValidity(field.dataset.validationRequired);
            }
            else if (field.validity.patternMismatch) {
                field.setCustomValidity(field.dataset.validationFormat);
            }
            else {
                field.setCustomValidity('');
            }

            
            field.reportValidity();
            contactUsForm.checkValidity();


            /* Recheck on field value change */
            field.addEventListener('change', function() {
                field.setCustomValidity('');
                Validate();
            });
        });
    }

    Validate();
    
    
    contactUsForm.addEventListener('submit', function(e) {
        e.preventDefault;
        
        if (e.checkValidity() == false) {
            return false;
        }
        else {
            // form.submit()
        }
    });

}

样式验证 bubbles/tooltips 曾经是一个功能,但仅供 Chrome 使用,但已被删除。 有关它的更多信息,请访问: How do you style the HTML5 form validation messages?

但是,您可以创建自己的工具提示或气泡来显示验证消息。通过使用 div 容器和一个跨度以及一点点 CSS,您可以创建一个几乎任何您可以想象的外观的气泡。

.ttCont {
  position: relative;
  display: inline-block;
}
.ttCont .ttText {
  display: inline-block;
  visibility: hidden;
  min-width: 200px;
  background-color: darkblue;
  color: #fff;
  text-align: center;
  border-radius: 6px;
  padding: 5px;
  opacity: 0;
  transition: opacity 0.5s;

  /* Place bubble to the right of container */
  position: absolute;
  z-index: 1;
  top: 5px;
  left: 105%;
}
.ttCont .ttText::after {
  content: " ";
  position: absolute;
  top: 50%;
  right: 100%; /* To the left of the bubble */
  margin-top: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: transparent darkblue transparent transparent;
}
.ttCont .ttText.active{
  visibility: visible;
  opacity: 1;
}

现在使用自定义气泡意味着您可能需要使用更少的 ValidityState API,但是您仍然可以使用相同的方法验证您的字段。除了使用 field.reportValidity(),您还可以创建一个自定义函数,在每个字段验证

时显示一个气泡

function customReportValidatity(elem, type) {
  let msg = "";
  ///check if validity is based on required or mismatch///
  switch (type) {
    case 'required':
      msg = $(elem).attr('data-validation-required');
      ///without jQuery
      // msg = elem.dataset.validationRequired;
      break;
    case 'format':
      msg = $(elem).attr('data-validation-format');
      ///without jQuery
      // msg = elem.dataset.validationFormat;
      break;
    default:
      break;
  }
  ///make popup appear///

  let ttText = $(elem).parent().children('.ttText');
  $(ttText).text(msg);
  $(ttText).addClass('active');

  ///without jQuery
  // let ttText = elem.parentElement.querySelector('.ttText');
  // ttText.innerText = msg;
  // ttText.classList.add('active');

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>

使用该样式和自定义函数,您可以将其应用于您愿意应用的任何表单的现有代码。在这种情况下,我制作了一个示例表单,其中包含需要的姓名和电子邮件,而不需要描述。

const contactUsForm = document.querySelector('#Form');
const submitBtn = document.querySelector('#submitBtn');

if (contactUsForm) {
  function customReportValidatity(elem, type) {
    let msg = "";
    ///check if validity is based on required or mismatch///
    switch (type) {
      case 'required':
        msg = $(elem).attr('data-validation-required');
        ///without jQuery
        // msg = elem.dataset.validationRequired;
        break;
      case 'format':
        msg = $(elem).attr('data-validation-format');
        ///without jQuery
        // msg = elem.dataset.validationFormat;
        break;
      default:
        break;
    }
    ///make popup appear///

    let ttText = $(elem).parent().children('.ttText');
    $(ttText).text(msg);
    $(ttText).addClass('active');

    ///without jQuery
    // let ttText = elem.parentElement.querySelector('.ttText');
    // ttText.innerText = msg;
    // ttText.classList.add('active');

  }

  function Validate() {
    let isValid = true;

    /* RegEx patterns */
    const emailPattern = /^((([a-z]|\d|[!#$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;

    let validatedFields = contactUsForm.querySelectorAll('[data-validation-required],[data-validation-format]');
    validatedFields.forEach(field => {

      if (field.getAttribute('type') === 'email') {
        field.setAttribute('pattern', emailPattern);
      }

      if (field.validity.valueMissing) {
        field.setCustomValidity(field.dataset.validationRequired);
        customReportValidatity(field, 'required');
        isValid = false;
      } else if (field.validity.typeMismatch) {
        //using typeMismatch instead of patternMismatch because the regex is not working for emails
        field.setCustomValidity(field.dataset.validationFormat);
        customReportValidatity(field, 'format');
        isValid = false;
      }

      contactUsForm.checkValidity();


      /// Recheck on field value change ///
      field.addEventListener('change', function() {
        $('.ttText').removeClass('active');

        ///without jquery
        /*document.querySelectorAll('.ttText').forEach((tt)=>{
          tt.classList.remove('active');
        });*/

        Validate();
      });

    });
    return isValid;
  }

  submitBtn.addEventListener('click', function(e) {
    e.preventDefault();
    e.stopPropagation();

    if (Validate()) {
      // contactUsForm.submit();
      //you can use this output to check if the form will submit
      console.log("Form Submitted!");
    } else {
      return false;
    }

  });

}
.ttCont {
  position: relative;
  display: inline-block;
}

.ttCont .ttText {
  display: inline-block;
  visibility: hidden;
  min-width: 200px;
  background-color: darkblue;
  color: #fff;
  text-align: center;
  border-radius: 6px;
  padding: 5px;
  opacity: 0;
  transition: opacity 0.5s;
  position: absolute;
  z-index: 1;
  top: 5px;
  left: 105%;
}

.ttCont .ttText::after {
  content: " ";
  position: absolute;
  top: 50%;
  right: 100%;
  margin-top: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: transparent darkblue transparent transparent;
}

.ttCont .ttText.active {
  visibility: visible;
  opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<form id="Form">
  <div class="ttCont">
    <label for="name">Name</label><br/>
    <input type="text" name="name" required data-validation-required="Name is required!" /><br/>
    <span class="ttText"></span>
  </div>
  <br/><br/>

  <div class="ttCont">
    <label for="email">Email</label><br/>
    <input type="email" name="email" required data-validation-required="Email is required" data-validation-format="Email must have the format similar to example@email.com!" /><br/>
    <span class="ttText"></span>
  </div>
  <br/><br/>

  <div class="ttCont">
    <label for="desc">Description</label><br/>
    <input type="text" name="desc" data-validation-required="Description is required!" /><br/>
    <span class="ttText"></span>
  </div>
  <br/><br/>

  <button type="button" id="submitBtn">Submit</button>
</form>

您可以在此处详细了解如何创建和处理自定义工具提示: