如何正确捕获并重新触发表单提交事件,保证



这可能不是您通常的"如何捕获表单提交事件?"问题。

我试图准确地理解jQuery,vanilla Javascript和浏览器(IE/FF/Chrome/Safari/Opera)如何处理表单提交事件 - 以及它们之间的关系。(请参阅我的另一个问题。经过几个小时的谷歌搜索和实验,我仍然无法得出结论,无论是因为不和谐还是含糊不清。

我正在完成一个与网站表单集成的脚本,以便在 AJAX 请求返回之前无法提交表单。

理想:

  1. 用户填写表单
  2. 表单已提交 - 但没有调用以前绑定的事件处理程序,除了我的
  3. 我的处理程序发出 API 请求(当然是异步的)
  4. 用户确认 API 响应的验证结果
  5. 表单提交继续正常、自动,调用之前被禁止的其他处理程序

目前的理解是:(这些可能是错误的,如果是这样,请纠正我)

  • jQuery 将表单提交事件处理程序绑定到提交按钮click事件
  • 直接在提交元素上执行事件处理程序click事件(无论是在标记中,如onclick=""还是使用 jQuery 绑定)首先执行
  • 接下来执行表单submit事件上的事件处理程序(无论是在标记中,如onsubmit=""还是使用 jQuery 绑定)
  • 调用$('[type=submit]').click()不会调用窗体的submit事件,因此不会调用其处理程序
  • 调用$('form').submit()不会调用提交按钮的click事件,因此不会调用其处理程序
  • 然而,不知何故,单击提交按钮的用户最终会调用绑定到表单submit事件的处理程序......(但如上所述,调用单击提交按钮不会执行相同的操作)
  • 事实上,无论用户以何种方式提交表单(通过提交按钮或按 Enter 键),都会调用与 jQuery 绑定到表单submit事件的处理程序......

现在,我是:

  1. 解除与 jQuery 绑定的处理程序到提交按钮的click事件的绑定,同时保留对它们的引用
  2. 将我自己的处理程序绑定到提交按钮的click事件,以便它首先执行
  3. 使用onclick=""onsubmit=""(在它们各自的元素上)获取标记中绑定的任何处理程序,并使用 jQuery 重新绑定它们(因此它们在我的之后执行),然后将属性设置为null
  4. 重新绑定其处理程序(从步骤 1 开始),以便它们最后执行

实际上,这在我自己的测试中非常有效,因此我的事件处理程序首先触发(必需)。

问题,以及我的问题:

我的处理程序首先触发,正如预期的那样(到目前为止)。问题是我的处理程序是异步的,所以我必须抑制(防止默认/停止传播/等)表单submit或提交按钮click调用它的事件......直到 API 请求完成。然后,当 API 请求返回并且一切正常时,我需要重新调用表单提交自动。但是由于我上面的观察,我如何确保所有事件处理程序都像自然形式提交一样被触发?

获取所有事件处理程序,将我的放在第一位,然后重新调用表单提交以便以正确的顺序调用所有内容的最干净方法是什么?

$('form').submit()$('form')[0].submit()之间有什么区别(如果有的话)?($('[type=submit]').click()$('[type=submit]')[0].click()也是如此)

tl;dr,关于Javascript/jQuery/browser form-submit-event-handling的规范、清晰、一刀切的文档是什么?(我不是在寻找书籍推荐。


一些解释:我试图弥补购物车结帐页面中的许多Javascript,有时只有当用户单击页面底部的按钮(不是提交按钮)时才会提交表单,或者还有其他棘手的情况。到目前为止,它已经相当成功了,它只是重新调用提交,这才是真正的问题所在。

使用 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 的提交处理程序的情况下进行表单提交。

这两个函数可以帮助您在 jquery 队列的前面绑定事件处理程序。你仍然需要去除内联事件处理程序(onclickonsubmit)并使用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
}
});

必须有很多方法可以解决这个问题 - 这是一个。

它使 ajax 函数 (A) 与所有其他函数(B、C、D 等)分开,只需将 A 放在标准的"提交"队列中,将 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');
});
});
});

演示(使用模拟的 ajax)

作为额外的奖励,自定义队列中的任何处理程序都可以抑制任何/所有后续处理程序,和/或禁止表单提交。只需根据需要选择合适的模式:

模式 1:

仅当前面的所有处理程序均未拒绝 def. 时执行其操作,并且可以禁止模式 1 和模式 2 的所有后续处理程序。

$("#myForm").on(formSubmitQueue, function(e, def) {
if(def.state() !== 'rejected') {
//actions as required here
if (expression) {
def.reject();
}
}
});

模式 2:

仅当前面的所有处理程序未拒绝 def. 但不禁止后续处理程序时,才执行其操作。

$("#myForm").on(formSubmitQueue, function(e, def) {
if(def.state() !== 'rejected') {
//actions as required here
}
});

模式 3:

无条件执行其操作,但仍可以抑制模式 1 和模式 2 的所有后续处理程序。

$("#myForm").on(formSubmitQueue, function(e, def) {
//actions as required here
if (expression) {
def.reject();
}
});

模式 4:

无条件地执行其操作,并且不禁止以下处理程序。

$("#myForm").on(formSubmitQueue, function(e, def) {
//actions as required here
});

笔记:

  • 可以在这些处理程序中解析延迟,以便立即提交表单,而无需处理队列的其余部分。但一般来说,延迟将由在触发队列之前动态添加到队列中的".last"处理程序解析(返回到处理程序 A 中)。
  • 在演示中,所有处理程序都属于模式 1。

以下是我最终这样做的方式,到目前为止,它在众多测试用例中都非常成功。我学到了很多关于事件的知识,特别是表单提交事件,与jQuery有关。我没有时间发布我收集的所有信息的综合百科全书,但现在这就足够了:

这是针对SmartyStreets LiveAddress API jQuery插件的,该插件在用户离开页面之前验证地址。

最成功的方法是抓取提交按钮的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);

相关内容

  • 没有找到相关文章

最新更新