6

编辑:在考虑回答之前,请仔细阅读整个问题。我不是在询问在生产代码中使用内联事件处理程序的可取性,也不是在询问实现我参考的文章所承诺的结果的最佳方法。这是一个关于 Javascript 语义和浏览器实现细节的问题,而不是关于最佳编码实践的问题。

听起来像是一场噩梦,对吧?

然而,我发现了一些在线建议,提倡这样做以防止重复提交表单:

<input type="submit" 
       onclick="this.disabled=true;
                this.value='Sending, please wait...';
                this.form.submit();" />

撇开任何关于内联事件处理程序弊端的讨论,我在这里看到的问题是:

  1. 标签的类型是"submit",所以提交它的包含表单是它的默认行为;
  2. onclick处理程序显式提交包含表单;
  3. onclick处理程序不会返回以false阻止默认行为(参见 1)。

直觉上,我认为单击此项目将与文章声称的完全相反——即提交表单两次,一次是作为显式submit()调用的结果,然后再次作为未抑制的默认行为"submit"类型控制。

另一方面,我编写了一个很小的 ​​PHP 脚本(如下)并在 Firefox 和 Safari(目前我唯一方便使用的浏览器)上进行了尝试,每次单击按钮时它只写入一个日志条目:

<html>
<head></head>
<body>
<?php
  if (isset($_GET['action']) && $_GET['action'] == 'submit') {
    $s = 'Got form submission at ' . time();
    error_log($s);
    echo $s;
  }
  else {
?>
  <form action="http://localhost/~hephaestus/phptests/submittest.php?action=submit" 
        method="post">
    <input type="submit" 
           onclick="this.disabled=true;
                    this.value='Sending, please wait...';
                    this.form.submit();" />
  </form>
<?php
  }
?>
</body>
</html>

那么这两个浏览器实际上是否在做“正确”的事情(其中“正确”在一些我没有看过或没有仔细阅读的文档中定义) - 这意味着我对问题的分析不完整或错误?

或者,我的分析是否正确——意味着观察到的单个提交是浏览器实现者放入特殊情况逻辑的结果,以拯救幼稚的编码人员?

更新:

为了从经验上检验@sourcecode 的说法,即该form.submit()方法是表单上的某种“第二个提交按钮”,因此受HTML4 规范第 17.13.2节中规定的规则的约束,即只能有一个“成功”的提交按钮,我在我的 PHP 测试脚本中添加了一些内容:

<?php
  if (isset($_GET['action']) && $_GET['action'] == 'submit') {
    $s = 'Got form submission at ' . time() . ', tellme = ' . $_POST['tellme'];
    error_log($s);
    echo $s;
  }
  else {
?>
  <form action="http://localhost/~hephaestus/phptests/submittest.php?action=submit" 
        method="post">
    <input type="hidden" id="tellme" name="tellme" value="0" />
    <input type="submit" 
           onclick="this.disabled=true;
                    this.value='Sending, please wait...';
                    this.form.submit();
                    document.getElementById('tellme')=1;" />
  </form>
<?php
  }
?>

在 Firefox 中,此代码生成单个错误日志条目:

在时间戳得到表单提交,tellme = 1

确实表明方法调用以某种方式被控件的内在事件驱动行为所取代。

return false;此外,如果我在将值设置为 1 之后包含一个附加项tellme(从而防止单击事件传播,从而防止控件的内在行为),则单个错误日志条目是:

在时间戳得到表单提交,tellme = 0

这意味着在这种情况下,提交是调用的结果this.form.submit()

另一方面,当我在 Safari 中尝试相同的两个变体时,它们中的一个都给了我相同的单个错误日志条目:

在时间戳得到表单提交,tellme = 0

所以在 Safari 的情况下,方法调用总是优先于事件驱动的提交——大概是因为它发生得更早。

那是。只是。惊人的。:-/

进一步更新:我有机会在 Windows NT 5.1 上的 IE 8.0 上对此进行测试。

IE 8 反映了 Safari 的行为,即form.submit()方法总是优先于偶数驱动的提交。

尽管今天几乎不相关,但我也意识到我在运行 OS X 10.4.11 的更古老的 PowerPC iMac 上有一个古老的 IE 5.22。这里有趣的历史琐事是 IE5 的工作方式与 IE8 的工作方式完全相反:事件驱动的提交总是取代form.submit()方法——即使单击事件已被return false;处理程序末尾的a禁止传播onclick!(不过,我还没有深入研究这是一个错误还是一个功能。我还没有 - 可能永远不会! - 运行测试以确定它是否破坏了事件抑制,或者试图做一些“聪明的事情” “。)

尽管如此,不管有什么不一致,两个 IE 都只进行一次提交。

我的初步(嗯,到目前为止,实际上非常确定)的结论是,“提交”类型的控件和 DOMform.submit()方法之间的精确关系没有明确定义,并且浏览器实现者,在没有任何明确指导的情况下,通常做他们认为最好的事情(例如,参见@Boris Zbarsky 的回答)。

至少在 Firefox、Safari 和 IE 的情况下,它们的实现者预见到事件和方法调用可能会竞争提交相同表单的可能性,并采取措施(尽管不同——几乎运行范围)来确保只有其中一个会成功。

(我仍然欢迎那些非常了解不同浏览器内部结构的人发表评论,或者使用我测试过的浏览器以外的浏览器加载了我的简单 PHP 脚本。)

4

4 回答 4

3

Gecko (Firefox) 肯定会检测到多个提交并在新提交时取消旧提交。请参阅http://hg.mozilla.org/mozilla-central/file/c4abfca219e5/content/html/content/src/nsHTMLFormElement.h中的 mPendingSubmisson 成员及其在http://hg.mozilla.org/中的处理mozilla-central/file/c4abfca219e5/content/html/content/src/nsHTMLFormElement.cpp(例如在nsHTMLFormElement::SubmitnsHTMLFormElement::PostHandleEvent(后者是从提交控件的默认操作内容中调用的内容)。

就规范所说的而言,我不清楚规范是否一定是理智的,但它位于http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of- controls-and-forms.html#concept-form-submit并建议两次提交都会发生,但由于“导航”算法的内部细节,后者可能会有效地取消较早的提交。我提交了https://www.w3.org/Bugs/Public/show_bug.cgi?id=20580来整理规范。

于 2013-01-06T17:39:28.113 回答
1

这是 w3 org 的建议:

  • 如果一个表单包含多个提交按钮只有激活的提交按钮是成功的

    提交点击和本地 JS form.Submit 函数也是如此,只有“提交”被激活,而不是 form.submit 函数。因为提交是主要的,JS函数是次要的..
    你可以在这里了解更多链接
于 2013-01-06T06:29:16.613 回答
0

我想您将需要使用该stopPropogation()方法。理想情况下,您会从 onclick 处理程序中调用一个函数,而不是使其全部内联。请参阅this other stackoverflow answer以获得详尽的解释

于 2013-01-06T06:09:47.800 回答
0

永远不要在点击提交时执行脚本,并且一定不要提交表单!!!此外,如果您禁用按钮,提交可能会停止!

如果文章中的代码能够正常工作,那完全是幸运的,我想许多浏览器会进入一个禁用提交按钮和单击此禁用按钮时提交事件的竞赛状态。

这是在任何浏览器中都可以使用的内联建议

<form action="..." method="post" 
 onsubmit="document.getElementById('subbut').innerHTML='Sending, please wait...'">
  <span id="subbut"><input type="submit" /></span>
</form>

同样不引人注目:

window.addEventListener('load',function() {
  document.getElementById('form1').addEventListener('submit', function(e) {
    document.getElementById('subbut').innerHTML = 'Sending, please wait...';
  })
})
<form action="..." method="post" id="form1">
  <span id="subbut"><input type="submit" /></span>
</form>

于 2013-01-06T07:02:54.093 回答