小影志 > 怀旧风情 > 前端开发 > 【翻译】创建 HTML5 表单验证气泡的替代方案

【翻译】创建 HTML5 表单验证气泡的替代方案

作者:佚名 来源:小影志 时间:2014-10-13

\

在过去几年里,我发表了关于 HTML5 表单验证的文章和演讲,其中最常见的问题就是关于气泡。这里的气泡是指浏览器显示验证错误信息的 UI 空间。下面分别是 Chrome、Firefox 和 IE 的实现:

\

无论什么原因,我们开发者(或更可能是设计师同学)都有很强的欲望想样式化这些东西。但不幸的是我们不做不到,因为没有一款浏览器提供指向这些气泡的样式。Chrome 曾经提供了一系列带供应商前缀的伪元素(::-webkit-validation-bubble-*),但是它们在 Chrome 28 中被移除了。

那么开发者可以做什么呢?虽然浏览器不允许你自定义他们的气泡,但 constraint validation spec 可以允许你阻止浏览器的气泡 UI 并让你自己创建。文章的余下部分将向你如何做到这一点。

警告:不要轻易的创建气泡替代方案。使用默认的气泡,可以免费获得一些非常复杂的功能,比如定位和可达性。使用自定义气泡你需要自己解决这些问题(或使用库替代)。

阻止默认气泡

创建自定义 UI 的第一步是阻止原生气泡。你可以通过监听表单控件的 invaild 事件并阻止其默认行为。举例来说,下面的表单没有验证气泡,因为在 invalid 事件处理函数中调用了 event.preventDefault()


<script> document.querySelector( "input" ).addEventListener( "invalid", function( event ) { event.preventDefault(); }); </script>

invaild 事件并没有冒泡,所以如果你想阻止在多个元素上的验证气泡,你必须注册一个捕获阶段的事件监听。

如果你不了解 DOM 事件中冒泡和捕获阶段的区别,这篇 MDN 文章给出了不错的解释。

下面的代码通过在父元素

上单一的 invaild 事件监听程序阻止了两个文本框的气泡:

 


    
    

<script>
    document.querySelector( "form" )
        .addEventListener( "invalid", function( event ) {
            event.preventDefault();
        }, true );
</script>

你可以通过这个方法移除浏览器的表单验证 UI,但是你一旦这样做,你必须建立一些自定义的来替代它。

建立替代 UI

显示表单验证错误的方法不计其数。没有一种是绝对的正确或者错误(好吧,有些错误的实现方法)。让我们来看看这几种你可能会采用的常见方法。对每一个,我们都会使用如下非常简单的名称和邮箱地址表单。我们还将使用一些简单的 CSS 让这份表单看起来还不错。


在开始之前有一个重要的点需要说明:所有这些 UI 只在 Internet Explorer 10 及以上版本可用,constraint validation API 在旧版中不存在。如果你需要支持旧版 IE 并且仍然希望使用 HTML5 表单验证,在这个幻灯片中我概括了一些可选方案。

替代 UI #1: 消息列表

一种显示验证错误消息的常见方式就是显示在屏幕顶部的一个框中,并且这种行为比较容易和 HTML5 表单验证 API 建立结合。在进入到代码部分之前,这是实际应用中的样子:

 

下面是你建立这个 UI 所需的代码:

function replaceValidationUI( form ) {
    // Suppress the default bubbles
    form.addEventListener( "invalid", function( event ) {
        event.preventDefault();
    }, true );

    // Support Safari, iOS Safari, and the Android browser—each of which do not prevent
    // form submissions by default
    form.addEventListener( "submit", function( event ) {
        if ( !this.checkValidity() ) {
            event.preventDefault();
        }
    });

    // Add a container to hold error messages
    form.insertAdjacentHTML( "afterbegin", "

    " ); var submitButton = form.querySelector( "button:not([type=button]), input[type=submit]" ); submitButton.addEventListener( "click", function( event ) { var invalidFields = form.querySelectorAll( ":invalid" ), listHtml = "", errorMessages = form.querySelector( ".error-messages" ), label; for ( var i = 0; i < invalidFields.length; i++ ) { label = form.querySelector( "label[for=" + invalidFields[ i ].id + "]" ); listHtml += "

    • " + label.innerHTML + " " + invalidFields[ i ].validationMessage + "
    • "; } // Update the list with the new error messages errorMessages.innerHTML = listHtml; // If there are errors, give focus to the first invalid field and show // the error messages container if ( invalidFields.length > 0 ) { invalidFields[ 0 ].focus(); errorMessages.style.display = "block"; } }); } // Replace the validation UI for all forms var forms = document.querySelectorAll( "form" ); for ( var i = 0; i < forms.length; i++ ) { replaceValidationUI( forms[ i ] ); }

      这个示例为每个表单字段设定了一个对应的 ,并且 的 for 属性设置成和表单字段的 id 属性一样。你也许想调整代码,建立消息以适配你自己的应用,但是其他的方法也许更容易达到目的。

      替代 UI #2: 字段下面的消息

      有时候,和在屏幕顶部显示消息列表相比,你更想把消息和他的对应字段管理起来。下面的 UI 实现了这点:

       

      要建立这个 UI,大部分代码和第一种方法相同,只是在提交按钮的 click 事件处理函数上有些细小区别。

      function replaceValidationUI( form ) {
          // Suppress the default bubbles
          form.addEventListener( "invalid", function( event ) {
              event.preventDefault();
          }, true );
      
          // Support Safari, iOS Safari, and the Android browser—each of which do not prevent
          // form submissions by default
          form.addEventListener( "submit", function( event ) {
              if ( !this.checkValidity() ) {
                  event.preventDefault();
              }
          });
      
          var submitButton = form.querySelector( "button:not([type=button]), input[type=submit]" );
          submitButton.addEventListener( "click", function( event ) {
              var invalidFields = form.querySelectorAll( ":invalid" ),
                  errorMessages = form.querySelectorAll( ".error-message" ),
                  parent;
      
              // Remove any existing messages
              for ( var i = 0; i < errorMessages.length; i++ ) {
                  errorMessages[ i ].parentNode.removeChild( errorMessages[ i ] );
              }
      
              for ( var i = 0; i < invalidFields.length; i++ ) {
                  parent = invalidFields[ i ].parentNode;
                  parent.insertAdjacentHTML( "beforeend", "
      " + invalidFields[ i ].validationMessage + "
      " ); } // If there are errors, give focus to the first invalid field if ( invalidFields.length > 0 ) { invalidFields[ 0 ].focus(); } }); } // Replace the validation UI for all forms var forms = document.querySelectorAll( "form" ); for ( var i = 0; i < forms.length; i++ ) { replaceValidationUI( forms[ i ] ); }

      替代 UI #3: 替代气泡

      最后一个 UI 介绍了一种通过 JavaScript 模仿浏览器验证气泡的方式,一种完全自定义(和可样式化)的气泡。以下是实际应用:

       

      在这个示例中我使用了 Kendo UI tooltip,因为我不想考虑该如何处理气泡的定位逻辑。下面是我建立这个 UI 的代码。在这个实现中我选择了使用 jQuery 来清理 DOM 代码(Kendo UI 依赖于 jQuery)。

      $( "form" ).each(function() {
          var form = this;
      
          // Suppress the default bubbles
          form.addEventListener( "invalid", function( event ) {
              event.preventDefault();
          }, true );
      
          // Support Safari, iOS Safari, and the Android browser—each of which do not prevent
          // form submissions by default
          $( form ).on( "submit", function( event ) {
              if ( !this.checkValidity() ) {
                  event.preventDefault();
              }
          });
      
          $( "input, select, textarea", form )
              // Destroy the tooltip on blur if the field contains valid data
              .on( "blur", function() {
                  var field = $( this );
                  if ( field.data( "kendoTooltip" ) ) {
                      if ( this.validity.valid ) {
                          field.kendoTooltip( "destroy" );
                      } else {
                          field.kendoTooltip( "hide" );
                      }
                  }
              })
              // Show the tooltip on focus
              .on( "focus", function() {
                  var field = $( this );
                  if ( field.data( "kendoTooltip" ) ) {
                      field.kendoTooltip( "show" );
                  }
              });
      
          $( "button:not([type=button]), input[type=submit]", form ).on( "click", function( event ) {
              // Destroy any tooltips from previous runs
              $( "input, select, textarea", form ).each( function() {
                  var field = $( this );
                  if ( field.data( "kendoTooltip" ) ) {
                      field.kendoTooltip( "destroy" );
                  }
              });
      
              // Add a tooltip to each invalid field
              var invalidFields = $( ":invalid", form ).each(function() {
                  var field = $( this ).kendoTooltip({
                      content: function() {
                          return field[ 0 ].validationMessage;
                      }
                  });
              });
      
              // If there are errors, give focus to the first invalid field
              invalidFields.first().trigger( "focus" ).eq( 0 ).focus();
          });
      });

      虽然替换验证气泡需要的代码量不少,但是这带来了相当于浏览器实现的复刻版。不同之处在于 JavaScript 的实现有着更多的可定制性,实现你的心中所想。举例来说,如果你想添加一些粉色、绿色和 Comic Sans 到你的气泡中,你现在完全可以做到:

       

      Kendo UI tooltip 小工具是 Kendo UI Core 中25+个小工具之一,来自 Kendo UI 免费和开源分发。

      所以你可以在当下使用这份代码而无须担心授权限制 — 或付费。你可以直接下载 Kendo UI core 源码,或使用我们的 CDN,或者从 Bower 抓取库(bower install kendo-ui-core)

      结束语

      虽然你不能样式化浏览器的验证气泡,但你可以阻止他们并建立你自己的 UI。你可以随意尝试更改文章中的实现方式来迎合你自己的需求。如果你曾经使用过其他方式也欢迎留言分享。

      原文:Building HTML5 Form Validation Bubble Replacements

      翻译:小影