【问题标题】:Understanding HTML Form Element behavior了解 HTML 表单元素的行为
【发布时间】:2020-12-10 00:59:41
【问题描述】:

我有一个包含几个文本字段和一个提交按钮的表单。提交处理程序附加到表单,以便在提交之前验证表单。此外,处理程序应该向用户显示 OK 消息,最后重定向到首页。

验证工作正常,但是当验证成功时,OK 消息仅短暂显示或根本不显示,并且页面被刷新而不是重定向。

这是表格:

<form id="form" method="post">
    <input name="firstname">
    <input name="lastname">
    <input type="submit" value="Submit">
</form>
<div class="hidden v-ok">Validation OK</div>
<div class="hidden v-failed">Validation failed</div>

以及相关的JS:

const form = document.querySelector('#form');

form.addEventListener('submit', e => {
    const controls = Array.from(form.elements),
        valid = controls.every(control => control.value !== '');
    if (!valid) {
        // Validation failed, don't submit
        e.preventDefault();
        // Show ValidationFailed message
        const message = document.querySelector('.hidden.v-failed');
        message.classList.remove('hidden');
        return;
    }
    // Validation OK, show message and submit
    const message = document.querySelector('.hidden.v-ok');
    message.classList.remove('hidden');
    window.setTimeout(() => {
        window.location.href = '/';
    }, 2000);
});

我也尝试过不超时重定向,但结果是一样的,页面只是刷新,它从不导航到首页。我该怎么做才能让我的表单发布并且仍然能够看到消息并进行重定向?

【问题讨论】:

    标签: javascript html forms


    【解决方案1】:

    TL;DR; 显示消息并稍后重定向可提供文档位置以保留在当前页面上。您不能提交表单并停留在当前页面上。使用 AJAX 又名 XMLHttpRequest 将数据发送到您的服务器,而不是提交表单。

    HTMLFormElement 的工作原理

    在早期,在 JavaScript 出现之前,表单元素旨在将数据从页面发送到服务器,然后服务器处理数据,并以浏览器加载的新页面进行响应。即使在今天,浏览器在提交表单时也是如此。

    The standard 说:

    表单元素代表一个超链接,可以通过与表单相关的元素的集合进行操作”(强调我的)。

    现在,如果您不想更深入地解释表单提交的工作原理,可以向下滚动到所有这些在实践中一章。

    但是当点击提交按钮时实际发生了什么?该标准定义了一个Form submission algorithm,非常详细,有点难以理解。以下是高度简化的主要步骤:

    1. 浏览器检查是否可以提交表单(文档完全活动、沙盒等),检查失败会取消算法
    2. 执行由元素属性定义的验证,如果验证失败则取消算法
    3. 附加的提交事件被执行(附加的事件只能触发一次)
    4. 如果任何名为 event.preventDefault 的提交处理程序被取消,算法将被取消
    5. 设置了表单的编码
    6. 创建表单控制元素的条目列表
    7. 解析表单的各种属性(例如,如果未设置属性,则操作默认为表单文档的 URL)
    8. 根据表单的method属性(默认为GET)和action属性中的URL架构选择内部发送方式
    9. 导航计划被创建(任务)并发送到事件队列(包括变形的表单数据)
    10. 执行事件队列中的任务,并导航到新页面

    在真正的算法中,会执行更多的动作,例如。步骤 1 在许多步骤之间执行,该算法防止并发提交。此外,所描述的算法仅在激活实际提交按钮时有效。如果通过form.submit方法通过JS调用提交,则跳过第2-4步。

    上述算法解释了提交事件处理程序中的 JS 表单验证如何取消提交(#4)。 unload document process (#10) 解释了当新页面即将加载时超时是如何中断的(所有未决的计时器和事件都被中止)。但是,这些都没有真正解释,提交事件处理程序中的location.href 如何在没有超时的情况下被忽略。

    这个问题的答案隐藏在表单的定义中:它“代表一个超链接”。激活超链接会设置特定的内部navigation 事件和内部“持续导航”标志。任何超链接的任何后续激活都会检查该标志,如果设置了该标志,则会取消该标志。设置location.href 实际上不是超链接,而是it uses 相同的导航机制,因此如果检测到待处理的导航,它也会被取消。值得注意的是,在 submit 处理程序中调用event.preventDefault 会立即取消设置“ongoing-navigation”标志,这使得设置location.href 稍后在处理程序中再次起作用。

    (上面描述的导航行为被大大简化了。为了完全理解浏览器导航的工作原理,提供标准中描述的事件队列和导航过程的深入知识(细节甚至部分依赖于实现) ),但这些不是此答案的目标。)

    所有这些都在实践中

    由于前面描述的 HTML 表单的大部分操作都是在后台执行的,从程序员的角度来看,这一切如何应用?让我们做一个综合(没有具体的理由)。

    • &lt;form&gt; 是一个链接,它可以向服务器发送比&lt;a&gt; 等常规链接更多的信息
    • 表单的action 属性大致等于常规链接的href 属性
    • 提交表单总是会加载一个新页面,这是提交的默认操作
    • 在提交事件未决时,阻止任何导航到另一个页面的尝试(包括单击链接或提交其他表单,或在 JS 中设置 location.href
    • 程序员可以通过为 form 设置 submit 事件监听器来干扰表单提交
    • 如果基于属性的表单验证失败,则不会执行附加的提交事件处理程序
    • 提交事件处理程序可以修改当前页面和要提交的数据,也可以取消表单提交
    • 尽管在提交事件处理程序中做了什么,在处理程序执行完成时提交表单,除非处理程序取消提交事件的默认操作

    那么是什么触发了表单提交呢?有多种方法可以开始表单提交。以下所有元素都是所谓的提交者元素,它们的默认操作是“提交表单”:

    1. &lt;input type="submit" value="Submit"&gt;
    2. &lt;input type="image" alt="Submit"&gt;
    3. &lt;button type="submit"&gt;Submit&lt;/button&gt;
    4. &lt;button&gt;Submit&lt;/button&gt;
    5. 在活动&lt;input type="date/number/password/search/tel/text/time/url/week"&gt; 上点击 ENTER
    6. 在JS中调用form.submit方法

    注意无类型的&lt;button&gt; 元素,默认情况下它也是一个提交按钮,放置在表单中时。

    防止事件的默认动作

    有些事件有一个默认动作,在事件处理函数完成后执行。例如submit事件的默认动作是提交表单触发事件。您可以通过调用事件对象的preventDefault 方法来阻止在事件处理程序中执行默认操作:

    event.preventDefault();
    

    event 是在处理程序的参数中传递的对象,或者是全局 event 对象。

    事件处理函数返回false 不会阻止表单提交。这仅适用于内联侦听器,当return false; 写入onsubmit 属性时。

    上面提交者元素列表中的前三个元素比其他元素“更强”。如果您已将 click 侦听器附加到提交/图像的输入类型或提交的按钮类型,阻止 (click) 事件的默认操作不会阻止表单提交。其他元素也可以在 click 侦听器中执行此操作。为了防止在所有情况下提交,您必须在 表单上 收听 submit 事件,而不是点击提交者元素。

    再说一遍:form.submit 方法在任何脚本中调用,将跳过所有表单验证并且不会触发任何提交事件,它不能通过 JS 取消。值得注意的是,这仅代表本机 submit 方法,例如。 jQuery 的 .submit 不是原生的,它调用附加的提交处理程序。

    发送数据并停留在当前页面

    只有不提交表单才能发送数据并仍然停留在当前页面上。有多种方法可以防止提交。最简单的方法是根本不在标记中包含表单,但这会在 JS 中进行大量额外的数据处理工作。最好的方法是防止提交事件的默认动作,任何其他的表单方式都会导致额外的工作,例如,在没有提交者元素的情况下触发 JS 仍然提供检测提交者元素列表中 #5 所做的提交。

    由于数据不提交就不会到服务器,所以需要用JS发送数据。使用的技术称为 AJAX(异步 Javascript 和 Xml)。

    这是一个简单的普通 JS 代码示例,用于向服务器发送数据并停留在当前页面上。该代码使用XMLHttpRequest object。通常,代码放置在表单的提交事件处理程序中,如下例所示。您可以在页面上的脚本中的任何位置发出请求,如果不涉及提交事件,则不需要阻止默认操作。

    form.addEventListener('submit', e => {
      const xhr = new XMLHttpRequest();
      const data = new FormData(form);
      
      e.preventDefault();
      
      xhr.open('POST', 'action_URL');
      xhr.addEventListener('load', e => {
        if (xhr.status === 200) {
          // The request was successful
          console.log(xhr.responseText);
        } else {
          // The request failed
          console.log(xhr.status);
        }
      });
      xhr.send(data);
    });
    

    表格的类比散布在整个代码中:

    • 控制元素值包含在data 中(form 是对现有表单元素的引用)
    • method 属性是xhr.open 的第一个参数
    • action 属性是xhr.open 的第二个参数
    • enctype 属性由 FormData 构造函数创建(默认为“multipart/form-data”)

    AJAX 和表单提交的区别在于,当从服务器获取 AJAX 调用的响应时,JavaScript 会在 xhrload 处理程序中继续执行,而不是在提交表单时浏览器加载新页面.在那个load 处理程序中,您可以对作为响应发送的数据服务器执行您需要的任何操作,或者只是重定向(也可以通过超时成功)。当您不需要请求成功/失败或任何响应数据的通知时,也可以将负载处理程序排除在代码之外。

    在现代浏览器中,您还可以使用Fetch API 发出 AJAX 请求。此外,大多数常用框架(如果不是全部)和许多库(如 jQuery)都有自己的(或多或少简化的)各种基于 AJAX 的任务的实现。

    备注

    由于内部提交算法中执行的任务的顺序,验证属性在调用提交事件处理程序之前处理。如果任何验证失败,算法将被取消。这意味着,当表单验证失败时,提交事件处理程序也不会执行。

    基于验证属性的验证仅在激活提交者元素后执行,验证属性不影响FormData构造函数创建的对象,该构造函数可以随时创建,不一定需要提交事件。

    创建FormData 对象时忽略表单的target 属性。 AJAX 调用总是针对当前页面,它不能被重定向到另一个文档。

    发送FormData对象不是必须的,你可以发送任何你需要的数据,但是必须按照请求的method进行格式化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-14
      • 2015-01-16
      • 1970-01-01
      • 2022-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-07
      相关资源
      最近更新 更多