简体   繁体   English

了解 HTML 表单元素行为

[英]Understanding HTML Form Element behavior

I've a form with few textfields and a submit button.我有一个只有几个文本字段和一个提交按钮的表单。 A submit handler is attached to the form in order to validate the form before submission.提交处理程序附加到表单,以便在提交之前验证表单。 Additionally, the handler is supposed to show an OK message to the user, and finally to redirect to the front page.此外,处理程序应该向用户显示一条 OK 消息,并最终重定向到首页。

Validation works fine, but when the validation succeeds, the OK message is shown only briefly or not at all, and the page is refreshed instead of redirection.验证工作正常,但当验证成功时,OK 消息只会短暂显示或根本不会显示,并且页面会刷新而不是重定向。

Here is the form:这是表格:

<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>

And the related JS:和相关的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);
});

I've also tried to redirect without the timeout, but the result is the same, the page is only refreshed, it never navigates to the front page.我也试过在没有超时的情况下重定向,但结果是一样的,页面只是刷新,它永远不会导航到首页。 What can I do to get my form posted and still able to see the message and do the redirection?我该怎么做才能发布我的表单并仍然能够看到消息并进行重定向?

TL;DR;长话短说;博士; Showing the message and redirecting later provides the document location to stay on the current page.稍后显示消息和重定向提供文档位置以保留在当前页面上。 You can't submit a form and stay on the current page.您不能提交表单并停留在当前页面上。 Use AJAX aka XMLHttpRequest to send the data to your server instead of submitting a form.使用 AJAX aka XMLHttpRequest 将数据发送到您的服务器,而不是提交表单。

How HTMLFormElement works HTMLFormElement 的工作原理

On early days, before JavaScript existed, form element was purposed to send data from a page to a server, then the server handled the data, and responded with a new page, which was loaded by the browser.在早期,在 JavaScript 出现之前,表单元素的目的是将数据从页面发送到服务器,然后服务器处理数据,并响应一个新的页面,由浏览器加载。 Even today, browsers are doing exactly this same when a form is submitted.即使在今天,浏览器在提交表单时也会做同样的事情。

The standard says: 该标准说:

" The form element represents a hyperlink that can be manipulated through a collection of form-associated elements " (emphasis mine). 表单元素代表一个超链接,可以通过表单相关元素的集合来操作”(强调我的)。

Now, if you're not interested in a deeper explanation of how the form submission works under the hood, you can scroll down to the All this in practice chapter.现在,如果您对更深入地了解表单提交的工作原理不感兴趣,您可以向下滚动到所有这些实践章节。

But what actually happens when a submit button is clicked?但是当提交按钮被点击时实际发生了什么? The standard defines a Form submission algorithm , which is very deeply detailed, and is a bit hard to follow.该标准定义了一个Form 提交算法,非常详细,并且有点难以遵循。 Here are the strongly simplified main steps:以下是大大简化的主要步骤:

  1. Browser checks whether the form can be submitted (document fully active, sandboxing etc.), failing the check cancels the algorithm浏览器检查表单是否可以提交(文档完全激活,沙箱等),检查失败取消算法
  2. Validation defined by the elements' attributes is executed, if validation fails the algorithm is cancelled执行由元素属性定义的验证,如果验证失败则算法被取消
  3. The attached submit event(s) are executed (attached event(s) can fire only once)执行附加的提交事件(附加事件只能触发一次)
  4. If any of the submit handlers called event.preventDefault the algorithm is cancelled如果任何提交处理程序调用event.preventDefault算法被取消
  5. The encoding of the form is set设置表单的编码
  6. An entry list of the form control elements is created创建表单控件元素的条目列表
  7. Various attributes of the form are parsed (ex. action defaults to the URL of the form's document if the attribute is not set)解析表单的各种属性(例如,如果未设置该属性,则操作默认为表单文档的 URL)
  8. The internal sending method is selected based on method attribute of the form (defaults to GET) and the schema of the URL in action attribute内部发送方式根据表单的method属性(默认为GET)和action属性中URL的schema来选择
  9. A navigation plan is created (a task) and send to the event queue (includes the deformed form data)创建导航计划(任务)并发送到事件队列(包括变形数据)
  10. The task in the event queue is executed, and navigation to a new page takes place执行事件队列中的任务,并导航到新页面

In the real algorithm, a lot more of actions are executed, ex.在真正的算法中,执行了更多的动作,例如。 step 1 is executed between many of the steps, and the algorithm prevents concurrent submissions.步骤 1 在许多步骤之间执行,算法防止并发提交。 Also, the described algorithm stands only when an actual submit button is activated.此外,所描述的算法仅在实际提交按钮被激活时有效。 If the submission is called via JS by form.submit method, steps 2 - 4 are jumped over.如果通过JS调用form.submit方法提交,则跳过2-4步。

The above algorithm explains how JS form validation in submit event handler can cancel the submission (#4).上面的算法解释了提交事件处理程序中的 JS 表单验证如何取消提交(#4)。 The unload document process (#10) explains how the timeout is broken when a new page is about to load (all the pending timers and events are aborted). 卸载文档过程(#10) 解释了当新页面即将加载时超时是如何中断的(所有挂起的计时器和事件都被中止)。 But, any of these doesn't actually explain, how location.href in a submit event handler is ignored without the timeout.但是,这些都没有真正解释提交事件处理程序中的location.href如何在没有超时的情况下被忽略。

The answer to this question is hidden in the definition of the form: It " represents a hyperlink ".这个问题的答案隐藏在表单的定义中:它“代表一个超链接”。 Activating a hyperlink sets a specific internal navigation event, and an internal "ongoing-navigation" flag.激活超链接会设置一个特定的内部导航事件和一个内部“ongoing-navigation”标志。 Any later activation of any hyperlink checks that flag, and is cancelled if the flag was set.以后任何超链接的激活都会检查该标志,如果设置了标志,则取消。 Setting location.href is not actually a hyperlink, but it uses the same navigation mechanisms, hence it's also cancelled, if a pending navigation is detected.设置location.href实际上不是超链接,但它使用相同的导航机制,因此如果检测到挂起的导航,它也会被取消。 It's notable, that calling event.preventDefault in a submit handler unsets the "ongoing-navigation" flag immediately, which makes setting location.href to work again later in the handler.值得注意的是,在提交处理程序中调用event.preventDefault会立即取消设置“ongoing-navigation”标志,这使得设置location.href稍后在处理程序中再次起作用。

(The navigation behavior described above is heavily simplified. To fully understand how the browser navigation works, provides deep knowledge of the event queues and navigation process, which are described in the standard (details being even partially implementation-dependent), but these are not the objective of this answer.) (上面描述的导航行为被大大简化了。为了充分理解浏览器导航的工作原理,提供了对标准中描述的事件队列和导航过程的深入了解(细节甚至部分依赖于实现),但这些不是这个答案的目的。)

All this in practice这一切在实践中

As the most of the previously described actions of HTML Form are executed under the hood, how can this all be applied in the point of the view of a programmer?由于前面描述的 HTML 表单的大部分操作都是在幕后执行的,所以从程序员的角度来看,这一切如何应用? Let's make a synthesis (without specific rationales).让我们做一个综合(没有具体的理由)。

  • <form> is a link which can send more information to the server than a regular link like <a> <form>是一个链接,它可以向服务器发送比<a>这样的常规链接更多的信息
  • action attribute of a form is roughly equal to href attribute of a regular link表单的action属性大致等于常规链接的href属性
  • Submitting a form always loads a new page, this is the default action of the submission提交表单总是加载一个新页面,这是提交的默认动作
  • Any attempt to navigate to another page (including clicking links or submitting other forms, or setting location.href in JS) is prevented while a submit event is pending在提交事件挂起时,阻止任何导航到另一个页面的尝试(包括单击链接或提交其他 forms,或在 JS 中设置location.href
  • Programmer can interfere form submission by setting a submit event listener to the form程序员可以通过为表单设置提交事件监听器来干扰表单提交
  • If attribute-based form validation fails, attached submit event handler(s) is/are not executed如果基于属性的表单验证失败,则不执行附加的提交事件处理程序
  • A submit event handler can modify the current page and the data to submit, and it also can cancel the form submission提交事件处理程序可以修改当前页面和要提交的数据,也可以取消表单提交
  • Nevertheless what is done in a submit event handler, the form is submitted when the handler execution is finished , unless the handler cancels the default action of the submit event尽管如此,在提交事件处理程序中所做的是,当处理程序执行完成时提交表单,除非处理程序取消提交事件的默认操作

What triggers a form submission then?那么是什么触发了表单提交呢? There are multiple ways to start a form submission.有多种方法可以开始表单提交。 All the elements below are so called submitter elements, those have "submit the form" as their default action:下面的所有元素都是所谓的提交者元素,它们的默认操作是“提交表单”:

  1. <input type="submit" value="Submit">
  2. <input type="image" alt="Submit">
  3. <button type="submit">Submit</button>
  4. <button>Submit</button>
  5. Hitting ENTER on active <input type="date/number/password/search/tel/text/time/url/week">在活动<input type="date/number/password/search/tel/text/time/url/week">上按ENTER
  6. Calling form.submit method in JS在 JS 中调用form.submit方法

Notice the typeless <button> element, it's also a submit button by default, when placed inside a form.注意无类型的<button>元素,当放置在表单中时,它默认也是一个提交按钮。

Preventing the default action of an event阻止事件的默认操作

Some events have a default action, which is executed after the event handler function has finished.一些事件有一个默认动作,在事件处理程序 function 完成后执行。 For example, the default action of submit event is to submit the form triggered the event.例如, submit触发该事件的表单。 You can prevent the default action to be executed in the event handler by calling preventDefault method of the event object:您可以通过调用事件 object 的preventDefault方法来阻止在事件处理程序中执行默认操作:

event.preventDefault();

event is either the object passed in the arguments of the handler, or the global event object. event是 handler 的 arguments 中传入的 object,或者是全局event object。

Returning false from an event handler function doesn't prevent the form submission.事件处理程序 function返回false不会阻止表单提交。 This works with inline listeners only, when return false;return false; is written in an onsubmit attribute.写在onsubmit属性中。

The first three elements in the submitter element list above are "stronger" than the other elements.上面提交者元素列表中的前三个元素比其他元素“更强”。 If you've attached a click listener to an input type of submit/image or a button type of submit, preventing the default action of the (click) event doesn't prevent the form submission.如果您已将点击侦听器附加到提交/图像输入类型或提交按钮类型,则阻止 (click) 事件的默认操作不会阻止表单提交。 Other elements can do that also within a click listener.其他元素也可以在click侦听器中执行此操作。 To prevent the submission in all cases, you've to listen to submit event on the form instead of clicks on the submitter elements.为了在所有情况下都阻止提交,您必须监听表单上的submit事件,而不是点击提交者元素。

And again: form.submit method called in any script, will jump over all form validations and won't fire any submit events, it can't be cancelled via JS.再说一次:在任何脚本中调用form.submit方法,将跳过所有表单验证并且不会触发任何提交事件,它不能通过 JS 取消。 It's notable, that this stands for the native submit method only, ex.值得注意的是,这仅代表本机submit方法,例如。 jQuery's .submit isn't native, and it calls the attached submit handlers. jQuery 的.submit不是原生的,它会调用附加的提交处理程序。

Send data and stay on the current page发送数据并停留在当前页面

To send data and still stay on the current page can be achieved only by not submitting the form.要发送数据并停留在当前页面只能通过不提交表单来实现。 There are multiple ways to prevent the submission.有多种方法可以防止提交。 The simplest way is to not include a form in the markup at all, that makes a lot of extra work with data handling in JS, though.最简单的方法是根本不在标记中包含表单,不过这会在 JS 中处理数据时做很多额外的工作。 The best way is to prevent the default action of the submit event, any other way with a form would cause extra work, for example, triggering JS without a submitter element still provides detecting submissions made by #5 in the submitter elements list.最好的方法是防止提交事件的默认操作,任何其他方式使用表单都会导致额外的工作,例如,在没有提交者元素的情况下触发 JS 仍然提供检测提交者元素列表中#5 所做的提交。

As the data won't go to a server without submitting, you need to send the data with JS.由于不提交数据不会go到服务器,所以需要用JS发送数据。 The technique to use is called AJAX (Asynchronous Javascript And Xml).使用的技术称为 AJAX(异步 Javascript 和 Xml)。

Here's a simple vanilla JS code example of sending data to the server, and staying on the current page.这是一个向服务器发送数据并停留在当前页面的简单 vanilla JS 代码示例。 The code utilizesXMLHttpRequest object .该代码使用XMLHttpRequest object Typically the code is placed in a submit event handler of a form, as it is in the example below.通常,代码放置在表单的提交事件处理程序中,如下例所示。 You can make a request anywhere in the scripts on a page, and if a submit event is not involved, preventing the default action is not needed.您可以在页面上的脚本中的任何位置发出请求,如果不涉及提交事件,则不需要阻止默认操作。

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);
});

The analogy to a form is scattered allover the code:表单的类比散布在整个代码中:

  • control element values are included in data ( form being a reference to an existing form element)控制元素值包含在data中( form是对现有表单元素的引用)
  • method attribute is the first argument of xhr.open method属性是xhr.open的第一个参数
  • action attribute is the second argument of xhr.open action属性是xhr.open的第二个参数
  • enctype attribute is created byFormData constructor (defaults to "multipart/form-data") enctype属性由FormData构造函数创建(默认为“multipart/form-data”)

The difference between AJAX and form submission is, that when getting a response for an AJAX call from the server, the JavaScript execution continues in the load handler of xhr instead of browser loading a new page when submitting a form. AJAX 和表单提交的区别在于,当从服务器获得对 AJAX 调用的响应时,JavaScript 的执行在xhrload处理程序中继续执行,而不是浏览器在提交表单时加载新页面。 In that load handler you can do what ever you need with the data server sent as the response, or just redirect (also succesfully with a timeout).在该load处理程序中,您可以使用作为响应发送的数据服务器做任何您需要的事情,或者只是重定向(也可以成功超时)。 It's also possible to leave the load handler out of the code, when you don't need a notify of the request success/failure or any response data.当您不需要请求成功/失败或任何响应数据的通知时,也可以将负载处理程序留在代码之外。

In modern browsers you can also use Fetch API to make an AJAX request.在现代浏览器中,您还可以使用Fetch API发出 AJAX 请求。 Also, most of the commonly used frameworks (if not all) and many libraries (like jQuery), have their own (more or less simplified) implementations for various AJAX-based tasks.此外,大多数常用框架(如果不是全部)和许多库(如 jQuery)都有自己的(或多或少简化的)实现,用于各种基于 AJAX 的任务。

Remarks评论

Due to the order of the tasks executed in the internal submit algorithm, the validation attributes are handled before calling the submit event handler(s).由于在内部提交算法中执行任务的顺序,在调用提交事件处理程序之前处理验证属性。 If any of the validations fails, the algorithm is cancelled.如果任何验证失败,则算法被取消。 This means, that when the form validation fails, also the submit event handler(s) are not executed.这意味着,当表单验证失败时,也不会执行提交事件处理程序。

Validation based on the validation attributes is executed only after activating a submitter element, validation attributes are not affecting to an object created by FormData constructor, which can be created at any time, a submit event is not necessarily needed.基于验证属性的验证只在激活提交者元素后执行,验证属性不影响由FormData构造函数创建的 object,可以随时创建,不一定需要提交事件。

The target attribute of the form is ignored when creating a FormData object. An AJAX call always targets to the current page, it can't be redirected to another document.创建FormData object 时将忽略表单的target属性。AJAX 调用始终指向当前页面,不能重定向到另一个文档。

It's not mandatory to send a FormData object, you can send any data you need, but it has to be formatted according to the method of the request.发送一个FormData object不是必须的,你可以发送你需要的任何数据,但必须根据请求的method进行格式化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM