Як запустити подію після використання event.preventDefault ()


155

Я хочу провести подію до тих пір, поки я не буду готовий розпочати це, наприклад

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

Відповіді:


164

Ні. Після того, як подія скасована, вона скасовується.

Пізніше ви можете повторно запустити подію, скориставшись прапором, щоб визначити, чи вже запустився ваш користувальницький код - ні, наприклад, цей (будь ласка, ігноруйте кричуще забруднення простору імен):

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!');
    });

4
.lve вилучено. Використовуйте .on, використаний у прикладі @Cory Danielson нижче.
nwolybug

Це знову потрапляє всередину .click, і нарешті я бачу "занадто багато рекурсії"
Himanshu Pathak

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


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', функція (e) {e.preventDefault (); setTimeout (function () {e.isDefaultPrevented = function () {return false;} $ ( '# v-pills-home-tab'). on ('показаний.bs.tab', функція () {$ ('. mainDashboard'). show (); $ ('# changePlans'). hid (); });}, 1000); $ (це) .tab ('показ');});
Surya R Praveen

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- подія дитини опрацьовано повністю. javas не робить тут "багатопотокового", що робить "зупинку" обробки подій необхідною. висновок: "призупинення" події просто для відновлення цього ж обробника не має сенсу.

якщо "багато речі" є чимось асинхронним, це також не має сенсу, оскільки це заважає асинхронним речам робити те, що вони повинні робити (асинхонові речі) і змушувати їх робити так, як все в послідовності (де ми повертаємось до мого першого абзацу )


Процес в середині асинхронний, я хочу
зафіксувати

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

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(). Це тому, що подія натискання, породжена jQuery, є лише шаром зверху на події рідного браузера. Тож запускаючи подію "клацання" на якорному тезі, не перейдіть за посиланням. Натомість ви можете використовувати бібліотеку на зразок jquery-simulate, яка дозволить запускати події в нашому веб-переглядачі.

Більш детально про це можна ознайомитись за цим посиланням


1

Іншим рішенням є використання window.setTimeout у слухачі подій та виконання коду після завершення процесу події. Щось на зразок...

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

Я використовую 0 протягом періоду, оскільки я не переймаюся очікуванням.


1

Я знаю, що ця тема стара, але я думаю, що я можу зробити свій внесок. Ви можете запустити поведінку події за певним елементом за замовчуванням будь-коли у функції обробника, якщо ви вже знаєте таку поведінку. Наприклад, коли ви запускаєте подію натискання кнопки скидання, ви фактично викликаєте функцію скидання у найближчій формі як поведінку за замовчуванням. У функції обробника після використання функції prevenDefault ви можете згадати поведінку за замовчуванням, зателефонувавши на функцію скидання в найближчій формі в будь-якому місці вашого коду обробника.


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();
            }
        );
    }
);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.