这可能不是您通常的“如何捕获表单提交事件?”问题
我正在尝试理解正是如何通过jQuery,vanilla Javascript和浏览器(IE / FF / Chrome / Safari / Opera)处理表单提交事件 - 以及它们之间的关系。 (See my other question.)经过数小时的谷歌搜索和实验,我仍然无法得出结论,无论是因为不和谐还是含糊不清。
我正在完成一个与网站表单集成的脚本,以便在AJAX请求返回之前无法提交表单。
理想情况下:
我目前的理解是:(这些可能有误,如果有,请纠正我)
click
events click
事件上的事件处理程序(无论是在标记中如onclick=""
还是使用jQuery绑定)都会先执行submit
个事件上的事件处理程序(无论是在onsubmit=""
标记中还是使用jQuery绑定)$('[type=submit]').click()
不会调用表单的submit
事件,因此不会调用其处理程序$('form').submit()
不会调用提交按钮的click
事件,因此不会调用其处理程序submit
事件的处理程序...(但如上所述,调用单击提交按钮不会执行相同操作)< / LI>
submit
形式事件的处理程序被调用... 现在,我是:
click
事件,同时保留对它们的引用click
事件,因此它首先执行onclick=""
和onsubmit=""
(在各自的元素上)绑定标记中的任何处理程序并使用jQuery重新绑定它们(因此它们在我的后面执行),然后将属性设置为{{ 1}} 实际上,这在我自己的测试中非常有效,所以我的事件处理程序首先触发(必要)。
问题和我的问题:
我的处理程序首先触发,就像预期的那样(到目前为止)。问题是我的处理程序是异步的,所以我必须禁止(preventDefault / stopPropagation / etc)表单null
或提交调用它的按钮submit
...直到API请求完成。然后,当API请求返回时,一切都是A-OK,我需要自动重新调用表单提交。但是由于我上面的观察,我如何确保所有事件处理程序被触发,就好像它是一个自然的表单提交一样?
获取所有事件处理程序的最简洁方法是什么,先放入我的,然后重新调用表单提交,以便以正确的顺序调用所有内容?
click
和$('form').submit()
之间有什么区别? ($('form')[0].submit()
和$('[type=submit]').click()
)
tl; dr ,关于Javascript / jQuery / browser form-submit-event-handling的规范,清晰,一刀切的文档是什么? (我不是在寻找书籍推荐。)
一些解释:我正在尝试补偿购物车结帐页面中的大量Javascript,有时只有当用户点击页面底部的按钮(不是提交按钮)时才会提交表单,或者还有其他棘手的情况。到目前为止,它已经相当成功,它只是重新调用提交,这确实是问题。
答案 0 :(得分:6)
使用jQuery绑定到表单的提交处理程序并阻止默认操作,然后,当您要提交表单时,直接在表单节点上触发它。
$("#formid").submit(function(e){
// prevent submit
e.preventDefault();
// validate and do whatever else
// ...
// Now when you want to submit the form and bypass the jQuery-bound event, use
$("#formid")[0].submit();
// or this.submit(); if `this` is the form node.
});
通过调用表单节点的submit
方法,浏览器执行表单提交而不触发jQuery的提交处理程序。
答案 1 :(得分:4)
这两个函数可以帮助您绑定jquery队列前面的事件处理程序。您仍然需要删除内联事件处理程序(onclick
,onsubmit
)并使用jQuery重新绑定它们。
// prepends an event handler to the callback queue
$.fn.bindBefore = function(type, fn) {
type = type.split(/\s+/);
this.each(function() {
var len = type.length;
while( len-- ) {
$(this).bind(type[len], fn);
var evt = $.data(this, 'events')[type[len]];
evt.splice(0, 0, evt.pop());
}
});
};
// prepends an event handler to the callback queue
// self-destructs after it's called the first time (see jQuery's .one())
$.fn.oneBefore = function(type, fn) {
type = type.split(/\s+/);
this.each(function() {
var len = type.length;
while( len-- ) {
$(this).one(type[len], fn);
var evt = $.data(this, 'events')[type[len]];
evt.splice(0, 0, evt.pop());
}
});
};
绑定执行ajax调用的提交处理程序:
$form.bindBefore('submit', function(event) {
if (!$form.hasClass('allow-submit')) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
// perform your ajax call to validate/whatever
var deferred = $.ajax(...);
deferred.done(function() {
$form.addClass('allow-submit');
});
return false;
} else {
// the submit event will proceed normally
}
});
绑定一个单独的处理程序来阻止[type="submit"]
上的点击事件,直到您准备好:
$form.find('[type="submit"]').bindBefore('click', function(event) {
if (!$form.hasClass('allow-submit')) {
// block all handlers in this queue
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
return false;
} else {
// the click event will proceed normally
}
});
答案 2 :(得分:2)
必须有很多方法来解决这个问题 - 这是一个。
它将ajax函数(A)与所有其他函数(B,C,D等)分开,方法是将A放在标准的“submit”队列中,将B,C,D等放在自定义事件队列中。这避免了制作B,C,D等所需的棘手的诡计,这取决于A的异步响应。
$(function(){
var formSubmitQueue = 'formSubmitQueue';
//Here's a worker function that performs the ajax.
//It's coded like this to reduce bulk in the main supervisor Handler A.
//Make sure to return the jqXHR object that's returned by $.ajax().
function myAjaxHandler() {
return $.ajax({
//various ajax options here
success: function(data, textStatus, jqXHR) {
//do whatever is necessary with the response here
},
error: function(jqXHR, textStatus, errorThrown) {
//do whatever is necessary on ajax error here
}
});
}
//Now build a queue of other functions to be executed on ajax success.
//These are just dummy functions involving a confirm(), which allows us to reject the master deferred passed into these handlers as a formal variable.
$("#myForm").on(formSubmitQueue, function(e, def) {
if(def.state() !== 'rejected') {
if (!confirm('Handler B')) {
def.reject();
}
}
}).on(formSubmitQueue, function(e, def) {
if(def.state() !== 'rejected') {
if (!confirm('Handler C')) {
def.reject();
}
}
}).on(formSubmitQueue, function(e, def) {
if(def.state() !== 'rejected') {
if (!confirm('Handler D')) {
def.reject();
}
}
});
$("#myForm").on('submit', function(e) {
var $form = $(this);
e.preventDefault();
alert('Handler A');
myAjaxHandler().done(function() {
//alert('ajax success');
var def = $.Deferred().done(function() {
$form.get(0).submit();
}).fail(function() {
alert('A handler in the custom queue suppressed form submission');
});
//add extra custom handler to resolve the Deferred.
$form.off(formSubmitQueue+'.last').on(formSubmitQueue+'.last', function(e, def) {
def.resolve();
});
$form.trigger(formSubmitQueue, def);
}).fail(function() {
//alert('ajax failed');
});
});
});
DEMO (使用模拟ajax)
作为额外奖励,可以使自定义队列中的任何处理程序抑制任何/所有后续处理程序,和/或禁止表单提交。只需根据需要选择合适的模式:
仅当所有前面的处理程序都没有拒绝def时才执行其操作。并且可以抑制模式1和模式2的所有后续处理程序。
$("#myForm").on(formSubmitQueue, function(e, def) {
if(def.state() !== 'rejected') {
//actions as required here
if (expression) {
def.reject();
}
}
});
仅当所有前面的处理程序都没有拒绝def时才执行其操作。但不会压制以下处理程序。
$("#myForm").on(formSubmitQueue, function(e, def) {
if(def.state() !== 'rejected') {
//actions as required here
}
});
无条件地执行其操作,但仍然可以抑制模式1和模式2的所有后续处理程序。
$("#myForm").on(formSubmitQueue, function(e, def) {
//actions as required here
if (expression) {
def.reject();
}
});
无条件执行其操作,并且不会抑制后续处理程序。
$("#myForm").on(formSubmitQueue, function(e, def) {
//actions as required here
});
注意:
答案 3 :(得分:1)
以下是我最终如何做到这一点,到目前为止,它在很多测试案例中都非常成功。我在jQuery方面学到了很多关于事件的知识,特别是表单提交事件。我没有时间发布我收集的所有信息的综合百科全书,但现在就足够了:
这是SmartyStreets LiveAddress API jQuery Plugin,用于在用户离开页面之前验证地址。
最成功的方法是抓住提交按钮的click
事件。下面的代码段位于jquery.liveaddress.js
文件中。它引用了尽可能多的事件处理程序(jQuery,onclick ---首先触发onclick),将它们连根拔起,自己(submitHandler
),并将其他事件分层。它在TortugaRumCakes.com(结账)和MedicalCareAlert.com(主页和结账)以及许多其他网站上成功运作。
完整代码位于GitHub。此特定段用于提交按钮上的“单击”,但类似的代码也用于处理表单提交。 jQuery的submit()
函数似乎是相当专有的......但这种处理都确保即使在jQuery对象上以编程方式调用.submit()
时也会调用它。
var oldHandlers, eventsRef = $._data(this, 'events');
// If there are previously-bound-event-handlers (from jQuery), get those.
if (eventsRef && eventsRef.click && eventsRef.click.length > 0)
{
// Get a reference to the old handlers previously bound by jQuery
oldHandlers = $.extend(true, [], eventsRef.click);
}
// Unbind them...
$(this).unbind('click');
// ... then bind ours first ...
$(this).click({ form: f, invoke: this }, submitHandler);
// ... then bind theirs last:
// First bind their onclick="..." handles...
if (typeof this.onclick === 'function')
{
var temp = this.onclick;
this.onclick = null;
$(this).click(temp);
}
// ... then finish up with their old jQuery handles.
if (oldHandlers)
for (var j = 0; j < oldHandlers.length; j++)
$(this).click(oldHandlers[j].data, oldHandlers[j].handler);