Как вызвать событие после использования event.preventDefault ()

156

Я хочу провести мероприятие до тех пор, пока я не буду готов его запустить, например

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

   e.preventDefault(); 

   // do lots of stuff

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

}

Есть ли эквивалент run()функции, описанной выше?

масатекского
источник
Поведение по умолчанию происходит только после возврата вашего обработчика. Не имеет смысла предотвращать такое поведение, только чтобы разрешить его позже в вашем обработчике.
Фредерик Хамиди
7
@ FrédéricHamidi К сожалению, асинхронные операции ($ .ajax, обратные вызовы и т. Д.) Позволяют выполнять поведение по умолчанию.
vzwick

Ответы:

165

Нет. Как только событие было отменено, оно отменяется.

Впрочем, вы можете повторно запустить событие позже, используя флаг, чтобы определить, уже запущен ваш пользовательский код или нет - например, это (пожалуйста, игнорируйте явное загрязнение пространства имен):

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));

Бонусный супер-минималистичный плагин jQuery с Promiseподдержкой:

(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!');
    });
vzwick
источник
4
.live является деприцированным. Используйте .on, используемый в примере @Cory Danielson ниже.
nwolybug
Это снова становится внутри .click и, наконец, я вижу «слишком много рекурсии»
Химаншу Патхак
5
@HimanshuPathak - вы, вероятно, забыли установить lots_of_stuff_already_done = true;флаг - в противном случае функция не сможет продолжить работу.
vzwick
73

Более свежая версия принятого ответа.

Краткая версия:

$('#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.

Для этого вы можете реализовать код, подобный тому, что я написал здесь:

$('#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();

    }

});
Кори Дэниелсон
источник
1
«Вместо того, чтобы копаться в почтовом коде серверной формы» ... На самом деле вы все равно должны это сделать, вы не можете полагаться только на проверку на стороне клиента.
Диего V
18

Вы можете сделать что-то вроде

$(this).unbind('click').click();
Рафаэль Оливейра
источник
Это действительно хорошее решение - но, похоже, не работает на IE10 / 11; (
JonB
47
Почему вы подвергли цензуре слово «боль»?
Шон Кендл
Вы нажали кнопку, но можете ли вы нажать еще раз?
Том Андерсон
16

Переопределите свойство 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, я обычно включаю краткое описание редактирования в виде строкового комментария. Это должно увеличить количество представленных символов.
рекурсивный
Не знаю, почему за этот ответ больше не проголосовали, это действительно полезно. Работает с распространением, останавливается тоже с event.isPropagationStopped = function(){ return false; };. Я также добавил пользовательское свойство к событию, чтобы в обработчике я мог определить, была ли выполнена проверка, препятствующая выполнению действия, чтобы оно не было выполнено снова. Большой!
Каддат
Я использовал для Bootstrap 4 Tabs, он работал отлично. Большое спасибо. $ ('# v-pills-tab a'). on ('click', function (e) {e.preventDefault (); setTimeout (function () {e.isDefaultPrevented = function () {return false;} $ ( '# v-pills-home-tab'). on ('seen.bs.tab', function () {$ ('. mainDashboard'). show (); $ ('# changePlans'). hide (); });}, 1000); $ (this) .tab ('show');});
Сурья Р Правин
8

Можно использовать currentTargetиз event. Пример показывает, как приступить к отправке формы. Точно так же вы можете получить функцию из onclickатрибута и т. Д.

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

  // code

  event.currentTarget.submit();
});
Сальвис Блузма
источник
отправить не является действительной функцией
Devaffair
если вы вызываете один и submit()тот же элемент, не вернетесь ли вы к своему коду `` $ ('form'). on ('submit') и повторите его снова и снова?
Fanie Void
7

Просто не выполняйте e.preventDefault();, или выполняйте это условно.

Вы, конечно, не можете изменить, когда происходит исходное событие.

Если вы хотите «воссоздать» исходное событие пользовательского интерфейса через некоторое время (скажем, в обратном вызове для запроса AJAX), вам просто нужно будет подделать его другим способом (как в ответе vzwick) ... хотя я бы Вопрос в юзабилити такого подхода.

Гонки легкости на орбите
источник
3

до тех пор, пока "много вещей" не делает что-то асинхронное, это абсолютно ненужно - событие будет вызывать каждый обработчик по пути в последовательности, поэтому, если на родительском элементе происходит событие onklick, оно срабатывает после onclik- Событие ребенка обработано полностью. javascript здесь не выполняет какую-то «многопоточность», что делает «остановку» обработки событий необходимой. Вывод: «приостанавливать» событие, просто чтобы возобновить его в том же обработчике, не имеет никакого смысла.

если «много вещей» является чем-то асинхронным, это также не имеет смысла, поскольку мешает асинхронным вещам делать то, что они должны делать (асинхронные вещи), и заставляет их вести себя так, как будто все в порядке (где мы возвращаемся к моему первому абзацу) )

oezi
источник
Процесс в середине асинхронный, я хочу запустить результат в обратном вызове ajax ...
Mazatec
1
если вам нужно ждать ajax-запроса, сделайте его синхронным (для jquery есть async-fag: api.jquery.com/jQuery.ajax ) ... но создание синхронного ajax-запроса - плохая идея почти во всех случаях, так что было бы лучше найти другое решение.
oezi
3

Подход, который я использую, заключается в следующем:

$('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
});
Хокусай
источник
2

Принятое решение не будет работать, если вы работаете с тегом привязки. В этом случае вы не сможете снова щелкнуть ссылку после звонка e.preventDefault(). Это потому, что событие click, сгенерированное jQuery, это просто слой поверх событий собственного браузера. Таким образом, запуск события click для тега привязки не приведет к переходу по ссылке. Вместо этого вы можете использовать библиотеку типа jquery-simulate , которая позволит вам запускать собственные события браузера.

Подробнее об этом можно узнать по этой ссылке

Мигель Карвахаль
источник
1

Другое решение - использовать window.setTimeout в прослушивателе событий и выполнить код после завершения процесса события. Что-то вроде...

window.setTimeout(function() {
  // do your thing
}, 0);

Я использую 0 для периода, так как меня не волнует ожидание.

Αλέκος
источник
1

Я знаю, что эта тема старая, но я думаю, что могу внести свой вклад. Вы можете в любое время запустить поведение по умолчанию для определенного элемента в своей функции-обработчике, если вы уже знаете это поведение. Например, когда вы запускаете событие click для кнопки сброса, вы фактически вызываете функцию сброса в ближайшей форме в качестве поведения по умолчанию. В вашей функции обработчика после использования функции protectDefault вы можете вызвать поведение по умолчанию, вызвав функцию сброса в ближайшей форме в любом месте кода вашего обработчика.

Patrik
источник
0

Если этот пример может помочь, добавьте «пользовательское подтверждение popin» на некоторые ссылки (я сохраняю код «$ .ui.Modal.confirm», это просто пример обратного вызова, который выполняет исходное действие):

//Register "custom confirm popin" on click on specific links
$(document).on(
    "click", 
    "A.confirm", 
    function(event){
        //prevent default click action
        event.preventDefault();
        //show "custom confirm popin"
        $.ui.Modal.confirm(
            //popin text
            "Do you confirm ?", 
            //action on click 'ok'
            function() {
                //Unregister handler (prevent loop)
                $(document).off("click", "A.confirm");
                //Do default click action
                $(event.target)[0].click();
            }
        );
    }
);
Ронан Кердуду
источник