使用 JavaScript 进行即时表单验证
920302
7年前
<p>HTML5引入了几个新的属性来实现基于浏览器的表单验证。 pattern属性是一个正则表达式,用于定义textarea元素和大多数input元素类型的有效输入范围。 required 属性指定某个字段是否需要输入。 对于没有实现这些属性的传统浏览器,我们可以使用它们的值作为填充表单的基础。 我们还可以使用它们来提供更有趣的增强功能 - 即时表单验证。</p> <h3>来自作者的更多内容</h3> <ul> <li><a href="https://www.sitepoint.com/show-password-javascript-accessibility/?utm_source=sitepoint&utm_medium=relatedinline&utm_term=&utm_campaign=relatedauthor" rel="nofollow,noindex">一个JavaScript可访问性的失败实验的经验教训</a></li> <li><a href="https://www.sitepoint.com/accessible-drag-drop/?utm_source=sitepoint&utm_medium=relatedinline&utm_term=&utm_campaign=relatedauthor" rel="nofollow,noindex">可用的多个元素的拖放</a></li> </ul> <p>我们必须非常小心以避免得意忘形,创建过于侵略性的表单验证,打破了自然的浏览行为,给用户造成麻烦。 例如,我见过无法使用Tab键跳出无效字段的表单 - JavaScript被使用(或者被 <em>滥用</em> )来强制焦点留在字段中直到有效为止。 这是非常糟糕的可用性,并且直接违反了 <a href="/misc/goto?guid=4959751186565171412" rel="nofollow,noindex">可访问性准则</a> 。</p> <p>我们在本文中要做的是更少的侵入性。 它甚至不是完整的客户端验证 - 它只是一个微妙的可用性增强,以可访问的方式实现,(正如我在测试脚本时发现的)几乎完全相同于Firefox的原生功能。</p> <h2>基本概念</h2> <p>在最新版本的Firefox中,如果必填( required )字段为空,或者其值与模式( pattern )不匹配,那么该字段将显示一个红色的轮廓,如下图所示。</p> <p><img src="https://simg.open-open.com/show/10b8b0564659bd59a69ca48a8f29ef8a.jpg"></p> <p>当然,这种情况不会马上发生。如果是这样的话,则默认情况下,每个必填字段都将具有该轮廓。相反,这些轮廓仅在您与该字段进行交互 <strong>之后</strong> 显示,这基本上(尽管不是精确)类似于onchange事件。</p> <p>所以这就是我们要做的,使用onchange作为触发事件。作为替代方案,我们可以使用oninput事件,一旦任何值被输入或粘贴到该字段中即可触发。但这实在是 <em>太 </em> 快了,因为它很容易在打字时快速地触发和关闭,产生一种闪烁的效果,这将使一些用户感到讨厌或不可避免地分散了用户的注意力。而且,在任何情况下,oninput不会从程序输入中触发,而onchange会,而且我们可能还需要它处理像第三方插件那样的自动完成的事情。</p> <h2>定义HTML和CSS</h2> <p>让我们看一看我们的实现,从它的HTML开始:</p> <pre> <code class="language-javascript"><form action="#" method="post"> <fieldset> <legend><strong>Add your comment</strong></legend> <p> <label for="author">Name <abbr title="Required">*</abbr></label> <input aria-required="true" id="author" name="author" pattern="^([- \w\d\u00c0-\u024f]+)$" required="required" size="20" spellcheck="false" title="Your name (no special characters, diacritics are okay)" type="text" value=""> </p> <p> <label for="email">Email <abbr title="Required">*</abbr></label> <input aria-required="true" id="email" name="email" pattern="^(([-\w\d]+)(\.[-\w\d]+)*@([-\w\d]+)(\.[-\w\d]+)*(\.([a-zA-Z]{2,5}|[\d]{1,3})){1,2})$" required="required" size="30" spellcheck="false" title="Your email address" type="email" value=""> </p> <p> <label for="website">Website</label> <input id="website" name="website" pattern="^(http[s]?:\/\/)?([-\w\d]+)(\.[-\w\d]+)*(\.([a-zA-Z]{2,5}|[\d]{1,3})){1,2}(\/([-~%\.\(\)\w\d]*\/*)*(#[-\w\d]+)?)?$" size="30" spellcheck="false" title="Your website address" type="url" value=""> </p> <p> <label for="text">Comment <abbr title="Required">*</abbr></label> <textarea aria-required="true" cols="40" id="text" name="text" required="required" rows="10" spellcheck="true" title="Your comment"></textarea> </p> </fieldset> <fieldset> <button name="preview" type="submit">Preview</button> <button name="save" type="submit">Submit Comment</button> </fieldset> </form></code></pre> <p>这个例子是一个简单的评论表单,其中一些字段是必需的,一些是需要验证的,一些是二者都有的。 具有 required属性的 字段也具有aria-required属性,这为那些不了解新的input类型的辅助技术提供了回退语义。</p> <p><a href="/misc/goto?guid=4959751186661241114" rel="nofollow,noindex">ARIA规范</a> 还定义了aria-invalid属性,这就是我们将要用来指示字段何时无效(HTML5中没有等效属性)的属性。 aria-invalid属性显然提供了可访问的信息,但它也可以用作CSS钩子来应用红色轮廓:</p> <pre> <code class="language-javascript">input[aria-invalid="true"], textarea[aria-invalid="true"] { border: 1px solid #f00; box-shadow: 0 0 4px 0 #f00; }</code></pre> <p>我们可以只使用box-shadow,而不是border,坦率地说,这看上去更好,但是不支持box-shadow的浏览器将不会显示,比如IE8。</p> <h2>阅读现代JavaScript</h2> <h3>跟上不断变化的的JavaScript世界</h3> <p><img src="https://simg.open-open.com/show/c2f446879b06dcef13f7961725241203.png"> <a href="/misc/goto?guid=4959751186749542981" rel="nofollow,noindex">开始阅读</a></p> <h2>添加JavaScript</h2> <p>现在我们已经有了静态代码,我们可以添加脚本。 我们首先需要的是一个基本的addEvent()函数:</p> <pre> <code class="language-javascript">function addEvent(node, type, callback) { if (node.addEventListener) { node.addEventListener(type, function(e) { callback(e, e.target); }, false); } else if (node.attachEvent) { node.attachEvent('on' + type, function(e) { callback(e, e.srcElement); }); } }</code></pre> <p>接下来,我们需要一个函数来确定一个给定的字段是否应该被验证,它只是测试该字段既没被禁用(disabled )也不是只读(readonly),并且它具有一个 pattern 或一个 required 属性:</p> <pre> <code class="language-javascript">function shouldBeValidated(field) { return ( !(field.getAttribute("readonly") || field.readonly) && !(field.getAttribute("disabled") || field.disabled) && (field.getAttribute("pattern") || field.getAttribute("required")) ); }</code></pre> <p>前两个条件可能看起来很冗长,但它们是必需的,因为元素的 disabled 和 readonly 属性不一定反映其属性状态。 例如,在Opera中,硬编码属性readonly =“readonly”的字段对于其readonly属性仍将返回undefined(点属性仅匹配通过脚本设置的状态)。</p> <p>一旦我们得到了这些实用程序,我们就可以定义主要的验证函数,它测试这个字段,然后执行实际的验证,如果适用的话:</p> <pre> <code class="language-javascript">function instantValidation(field) { if (shouldBeValidated(field)) { var invalid = (field.getAttribute("required") && !field.value) || (field.getAttribute("pattern") && field.value && !new RegExp(field.getAttribute("pattern")).test(field.value)); if (!invalid && field.getAttribute("aria-invalid")) { field.removeAttribute("aria-invalid"); } else if (invalid && !field.getAttribute("aria-invalid")) { field.setAttribute("aria-invalid", "true"); } } }</code></pre> <p>所以当一个字段是必需的却没有赋值,或者它的值不匹配它的模式,则该字段是无效的。</p> <p>由于模式( pattern )已经定义了正则表达式的字符串形式,所以我们要做的就是将该字符串传递给RegExp构造函数,它将创建一个可以对该值进行测试的正则表达式对象。 但是,我们必须 <em>预先测试 </em> 该值以确保它不为空,以便正则表达式本身不必考虑空字符串。</p> <p>一旦我们确定一个字段是否无效,我们就可以控制它的aria-invalid属性来指示该状态 - 将它添加到一个不具有该字段的无效字段中,或者从一个有效的字段中删除它。 简单吧! 最后,为了把这一切付诸实施,我们需要将验证函数绑定到一个onchange事件。 <em>就是 </em> 这么简单:</p> <pre> <code class="language-javascript">addEvent(document, "change", function(e, target) { instantValidation(target); });</code></pre> <p>然而,为了使它工作,onchange事件 <strong>必须冒泡</strong> (使用通常称为 <a href="/misc/goto?guid=4959751186842078658" rel="nofollow,noindex">事件委托</a> 的技术),但在Internet Explorer 8和更早版本中,onchange事件 <em>不会冒泡</em> 。 我们可以选择忽略这些浏览器,但我认为这将是一种耻辱,尤其是当问题的解决方法是如此简单。 它只是意味着代码更加复杂 - 我们必须获取input和textarea元素的集合,遍历它们,并将onchange事件单独绑定到每个字段上:</p> <pre> <code class="language-javascript">var fields = [ document.getElementsByTagName("input"), document.getElementsByTagName("textarea") ]; for (var a = fields.length, i = 0; i < a; i++) { for (var b = fields[i].length, j = 0; j < b; j++) { addEvent(fields[i][j], "change", function(e, target) { instantValidation(target); }); } }</code></pre> <h2>结论及更多</h2> <p>至此我们已经完成了 - 一个简单和非侵入性的即时表单验证增强,提供可访问和可视化的提示,以帮助用户完成表单。 您可以查看以下演示:</p> <p>请参阅 <a href="/misc/goto?guid=4959750106831607434" rel="nofollow,noindex">CodePen</a> 上SitePoint( <a href="/misc/goto?guid=4959751186959921139" rel="nofollow,noindex">@SitePoint</a> )的Pen <a href="/misc/goto?guid=4959751187061459227" rel="nofollow,noindex">即时表单验证</a> 。</p> <p> </p> <p>来自:https://coyee.com/article/12365-instant-form-validation-using-javascript</p> <p> </p>