编辑:在考虑回答之前,请仔细阅读整个问题。我不是在询问在生产代码中使用内联事件处理程序的可取性,也不是在询问实现我参考的文章所承诺的结果的最佳方法。这是一个关于 Javascript 语义和浏览器实现细节的问题,而不是关于最佳编码实践的问题。
听起来像是一场噩梦,对吧?
然而,我发现了一些在线建议,提倡这样做以防止重复提交表单:
<input type="submit"
onclick="this.disabled=true;
this.value='Sending, please wait...';
this.form.submit();" />
撇开任何关于内联事件处理程序弊端的讨论,我在这里看到的问题是:
- 标签的类型是
"submit"
,所以提交它的包含表单是它的默认行为; onclick
处理程序显式提交包含表单;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 脚本。)