如何在使用event.preventDefault()后触发事件

200

我想要等到准备好之后再触发事件,例如:

$('.button').live('click', function(e){

   e.preventDefault(); 

   // do lots of stuff

   e.run() //this proceeds with the normal event    

}

是否有与上述描述的 run() 函数相等的函数?


默认行为是在处理程序返回后才发生的。阻止该行为,然后在处理程序中稍后允许它发生,这样做没有多大意义。 - Frédéric Hamidi
9
很抱歉,异步内容(例如 $.ajax、回调函数等)将允许默认行为发生。 - vzwick
17个回答

202

不行。一旦事件被取消,它就被取消了。

但是你可以稍后重新触发事件,使用一个标志来确定你的自定义代码是否已经运行 - 例如这样(请忽略明显的命名空间污染):

var lots_of_stuff_already_done = false;

$('.button').on('click', function(e) {
    if (lots_of_stuff_already_done) {
        lots_of_stuff_already_done = false; // reset flag
        return; // let the event bubble away
    }

    e.preventDefault();

    // do lots of stuff

    lots_of_stuff_already_done = true; // set flag
    $(this).trigger('click');
});

一个更通用的变体(具有避免全局命名空间污染的额外好处)可能是:

function onWithPrecondition(callback) {
    var isDone = false;

    return function(e) {
        if (isDone === true)
        {
            isDone = false;
            return;
        }

        e.preventDefault();

        callback.apply(this, arguments);

        isDone = true;
        $(this).trigger(e.type);
    }
}

使用方法:

var someThingsThatNeedToBeDoneFirst = function() { /* ... */ } // do whatever you need
$('.button').on('click', onWithPrecondition(someThingsThatNeedToBeDoneFirst));

带有 Promise 支持的奖励级别的极简jQuery插件:

(function( $ ) {
    $.fn.onButFirst = function(eventName,         /* the name of the event to bind to, e.g. 'click' */
                               workToBeDoneFirst, /* callback that must complete before the event is re-fired */
                               workDoneCallback   /* optional callback to execute before the event is left to bubble away */) {
        var isDone = false;

        this.on(eventName, function(e) {
            if (isDone === true) {
                isDone = false;
                workDoneCallback && workDoneCallback.apply(this, arguments);
                return;
            }

            e.preventDefault();

            // capture target to re-fire event at
            var $target = $(this);

            // set up callback for when workToBeDoneFirst has completed
            var successfullyCompleted = function() {
                isDone = true;
                $target.trigger(e.type);
            };

            // execute workToBeDoneFirst callback
            var workResult = workToBeDoneFirst.apply(this, arguments);

            // check if workToBeDoneFirst returned a promise
            if (workResult && $.isFunction(workResult.then))
            {
                workResult.then(successfullyCompleted);
            }
            else
            {
                successfullyCompleted();
            }
        });

        return this;
    };
}(jQuery));

用法:

$('.button').onButFirst('click',
    function(){
        console.log('doing lots of work!');
    },
    function(){
        console.log('done lots of work!');
    });

4
.live已过时,请使用在@Cory Danielson示例中使用的.on。 - nwolybug
这又进入了 .click 函数,最后我看到了“递归过深”的错误。 - Himanshu Pathak
5
@HimanshuPathak - 你可能忘记设置 lots_of_stuff_already_done = true; 标志了 - 否则函数就无法继续递归。 - vzwick
1
但是,如果selector匹配多个元素,则闭包将为每个元素重复使用isDone - Top-Master

77

被接受答案的更近期版本。

简要版本:

$('#form').on('submit', function(e, options) {
    options = options || {};

    if ( !options.lots_of_stuff_done ) {
        e.preventDefault();
        $.ajax({
            /* do lots of stuff */
        }).then(function() {
            // retrigger the submit event with lots_of_stuff_done set to true
            $(e.currentTarget).trigger('submit', { 'lots_of_stuff_done': true });
        });
    } else {
        /* allow default behavior to happen */
    }

});



这种情况下使用这样的东西的一个好例子是,当您可能有一些旧的表单代码,它能够正常工作,但是您被要求在提交表单之前添加类似于电子邮件地址验证的内容以增强表单。您可以编写一个API,然后更新前端代码,让它首先调用该API,然后再允许表单进行传统的POST提交,而无需深入挖掘后端表单POST代码。

为了实现这一点,您可以实现类似我在这里编写的代码:

$('#signup_form').on('submit', function(e, options) {
    options = options || {};

    if ( !options.email_check_complete ) {

        e.preventDefault(); // Prevent form from submitting.
        $.ajax({
            url: '/api/check_email'
            type: 'get',
            contentType: 'application/json',
            data: { 
                'email_address': $('email').val() 
            }
        })
        .then(function() {
            // e.type === 'submit', if you want this to be more dynamic
            $(e.currentTarget).trigger(e.type, { 'email_check_complete': true });
        })
        .fail(function() {
            alert('Email address is not valid. Please fix and try again.');
        })

    } else {

        /**
             Do traditional <form> post.
             This code will be hit on the second pass through this handler because
             the 'email_check_complete' option was passed in with the event.
         */

        $('#notifications').html('Saving your personal settings...').fadeIn();

    }

});

2
不要只依靠客户端验证,实际上你还必须查找后端表单提交代码。 - Diego V

21
你可以这样做
$(this).unbind('click').click();

这是一个非常好的解决方案 - 但似乎在IE10/11上无法工作 ;( - JonB
50
你为什么要审查「痛」这个词? - Sean Kendle
你触发了点击事件,但你能再次点击吗? - Tom Anderson

19

像这样覆盖属性isDefaultPrevented:

$('a').click(function(evt){
  evt.preventDefault();

  // in async handler (ajax/timer) do these actions:
  setTimeout(function(){
    // override prevented flag to prevent jquery from discarding event
    evt.isDefaultPrevented = function(){ return false; }
    // retrigger with the exactly same event data
    $(this).trigger(evt);
  }, 1000);
}

个人认为,这是重新触发事件并将完全相同的数据推回的最完整方法。


e 未定义。应该是 evt.preventDefault()。我尝试进行编辑,但我的编辑必须大于6个字符,而我只添加了2个 :( - kevnk
3
@kevnk,通常我会在一行注释的形式中包含对编辑内容的简要描述。这将增加提交的字符数。 - recurse
1
不知道为什么这个答案没有得到更多的赞,它真的很有用。使用 event.isPropagationStopped = function(){ return false; }; 也可以停止事件传播。我还向事件添加了一个自定义属性,以便在处理程序中检测是否已经执行了防止操作的检查,这样就不会再执行一次了。太棒了! - Kaddath
我使用了Bootstrap 4标签页,它完美地运行了。非常感谢。$('#v-pills-tab a').on('click', function (e) { e.preventDefault(); setTimeout(function(){ e.isDefaultPrevented = function(){return false;} $('#v-pills-home-tab').on('shown.bs.tab', function(){ $('.mainDashboard').show(); $('#changePlans').hide();}); }, 1000); $(this).tab('show'); }); - Surya R Praveen
1
这不会进入循环中吗?点击事件基本上会再次触发。 - sajanyamaha
原始的JavaScript事件仍将被阻止。 - Josué Zatarain

12

10

可以使用eventcurrentTarget。示例展示了如何提交表单。同样地,你可以从onclick属性中获取函数等。

$('form').on('submit', function(event) {
  event.preventDefault();

  // code

  event.currentTarget.submit();
});

提交不是一个有效的函数。 - Devaffair
1
如果在同一元素上调用“submit()”,那么您不会返回到您的“$('form').on('submit')”代码并重复执行它吗? - Fanie Void

8

不要执行e.preventDefault();, 或者有条件地执行。

你当然不能改变原始事件的触发时间。

如果你想在稍后的某个时刻(比如 AJAX 请求的回调函数中)"重新创建"原始的 UI 事件,那么你只能用其他方式来模拟它(就像 vzwick 的回答中所提到的)... 不过我会质疑这种方法的可用性。


4
我使用的方法是这样的:
$('a').on('click', function(event){
    if (yourCondition === true) { //Put here the condition you want
        event.preventDefault(); // Here triggering stops
        // Here you can put code relevant when event stops;
        return;
    }
    // Here your event works as expected and continue triggering
    // Here you can put code you want before triggering
});

3
另一种解决方案是在事件监听器中使用 window.setTimeout,并在事件处理完成后执行代码。类似于...
window.setTimeout(function() {
  // do your thing
}, 0);

我使用0表示时间段,因为我不在意等待。

1
我喜欢这个解决方案。没有jQuery的东西。 - fsevenm

3

您可以使用计时器或不使用计时器。

const form = document.querySelector('#form');

form.addEventListener('submit', (x) => {

    x.preventDefault()

    // Ajax or nay Code

    setTimeout(() => {
        x.target.submit();
    }, 1000)

})

未捕获的类型错误:x.target.submit不是一个函数,这个无法工作。 - Sunil Kumar Nerella
这里的 x 是我们的表单,使用 'target' 我们可以访问它,使用 'submit()' 我们可以提交我们的函数,因此 'x.target.submit();' 是正确的。 - Mahdi Saneipour

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接