Hep*_*tus 6 javascript cross-browser submit dom-events
编辑:在考虑回答之前,请仔细阅读问题.我不是在询问在生产代码中使用内联事件处理程序的可行性,也不是在询问实现我引用的文章所承诺的结果的最佳方法.这是一个关于Javascript语义和浏览器实现细节的问题,而不是关于最佳编码实践的问题.
谢谢.
听起来像是一场噩梦,对吧?
然而,我发现了一些在线建议,主张做这样的事情以防止双重提交表单:
<input type="submit"
onclick="this.disabled=true;
this.value='Sending, please wait...';
this.form.submit();" />
Run Code Online (Sandbox Code Playgroud)
暂且不讨论内联事件处理程序的邪恶,我在这里看到的问题是:
"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>
Run Code Online (Sandbox Code Playgroud)
那么这两个浏览器实际上正在做"正确"的事情(在某些文档中定义"正确",我要么没有看到或者没有仔细阅读) - 这意味着我对问题的分析是不完整或错误的?
或者,我的分析是否正确 - 意味着观察到的单个提交是浏览器实现者输入特殊情况逻辑的结果,以便从他们自己中保存天真的编码器?
更新:
为了通过@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
}
?>
Run Code Online (Sandbox Code Playgroud)
在Firefox中,此代码生成单个错误日志条目:
在时间戳获得表单提交,tellme = 1
这确实表明方法调用以某种方式被控件的内部事件驱动行为所取代.
此外,如果return false;在将值设置tellme为1(从而防止单击事件传播,从而防止控件的内在行为)后包含一个附加项,则单个错误日志条目为:
在时间戳获得表单提交,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()方法- 即使click事件已经从传播抑制由return false;在年底onclick处理!(尽管如此,我还没有深入研究这是一个错误还是一个功能.我还没有 - 也许永远不会! - 运行一个测试,以确定它是否惹恼了事件抑制,或者试图做一些"智能" ".)
尽管如此,无论是否存在不一致,两个IE都只进行一次提交.
我的暂定(好吧,现在,实际上非常坚定)结论是"提交"控件和DOM form.submit()方法之间的确切关系没有明确定义,并且浏览器实现者在没有任何明确指导的情况下,通常做他们认为最好的事情(例如,参见@Boris Zbarsky的回答).
至少在Firefox,Safari和IE的情况下,他们的实现者预见到事件和方法调用可能竞争提交相同的表单,并采取步骤(尽管不同的 - 几乎运行色域)以确保只有其中一个会成功.
(我仍然欢迎那些知道不同浏览器内部以便评论的人或者使用我测试过的浏览器加载我的简单PHP脚本的人的其他答案.)
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::Submit和nsHTMLFormElement::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来整理规范。