วิธีทริกเกอร์เหตุการณ์หลังจากใช้ event.preventDefault ()


156

ฉันต้องการจัดกิจกรรมจนกว่าฉันจะพร้อมยิงเช่น

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

   e.preventDefault(); 

   // do lots of stuff

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

}

มีrun()ฟังก์ชั่นที่เทียบเท่ากับที่อธิบายข้างต้นหรือไม่?


พฤติกรรมเริ่มต้นจะเกิดขึ้นหลังจากที่ตัวจัดการของคุณกลับมาแล้ว มันมีเหตุผลเล็กน้อยที่จะป้องกันไม่ให้เกิดพฤติกรรมดังกล่าวเพียงเพื่อให้สามารถใช้งานได้ในภายหลังในตัวจัดการของคุณ
Frédéric Hamidi

7
@ FrédéricHamidiน่าเสียดายที่ async stuff ($ .ajax, callbacks และอื่น ๆ ) จะอนุญาตให้มีพฤติกรรมเริ่มต้น
vzwick

คำตอบ:


165

Nope เมื่อกิจกรรมถูกยกเลิกจะถูกยกเลิก

คุณสามารถดำเนินการเหตุการณ์อีกครั้งในภายหลังโดยใช้การตั้งค่าสถานะเพื่อกำหนดว่ารหัสที่กำหนดเองของคุณได้ทำงานอยู่แล้วหรือไม่ - เช่นนี้ (โปรดละเว้นมลพิษเนมสเปซที่โจ่งแจ้ง):

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

ตัวแปรที่มีความเป็นทั่วไปมากขึ้น (ด้วยประโยชน์เพิ่มเติมของการหลีกเลี่ยงมลภาวะของ namespace ทั่วโลก) อาจเป็น:

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
.live เป็น depricated ใช้. on ที่ใช้ในตัวอย่าง @Cory Danielson ด้านล่าง
nwolybug

นี่คือการเข้าไปข้างในอีกครั้งคลิกและในที่สุดฉันเห็น "การเรียกซ้ำมากเกินไป"
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
"แทนที่จะขุดผ่านรหัสโพสต์รูปแบบแบ็คเอนด์" ... ในความเป็นจริงคุณต้องทำต่อไปคุณไม่สามารถพึ่งพาการตรวจสอบฝั่งไคลเอ็นต์เพียงอย่างเดียว
Diego V

18

คุณสามารถทำสิ่งที่ชอบ

$(this).unbind('click').click();

นี้เป็นทางออกที่ดีจริงๆ - แต่ดูเหมือนจะไม่ทำงานใน IE10 / 11 (
JonB

47
ทำไมคุณถึงตรวจสอบคำว่า "ความเจ็บปวด"?
Sean Kendle

คุณเรียกใช้การคลิก แต่คุณสามารถคลิกอีกครั้งได้หรือไม่
Tom Anderson

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

IMHO นี่เป็นวิธีที่สมบูรณ์แบบที่สุดในการเรียกเหตุการณ์ใหม่ด้วยข้อมูลเดียวกัน


eไม่ได้กำหนด evt.preventDefault()ควรจะเป็น ฉันพยายามที่จะแก้ไข แต่การแก้ไขของฉันต้องมี> 6 ตัวอักษรและฉันเพิ่งเพิ่ม 2 :(
kevnk

3
@kevnk ฉันมักจะมีคำอธิบายสั้น ๆ ของการแก้ไขในรูปแบบของความคิดเห็นบรรทัด สิ่งนี้ควรเพิ่มจำนวนตัวอักษรที่ส่ง
recurse

ไม่ทราบว่าเพราะเหตุใดคำตอบนี้จึงไม่ถูกยกระดับขึ้นอีกนี่เป็นประโยชน์จริงๆ event.isPropagationStopped = function(){ return false; };ทำงานร่วมกับการขยายพันธุ์หยุดเกินไปกับ ฉันยังเพิ่มคุณสมบัติแบบกำหนดเองให้กับเหตุการณ์เพื่อให้ฉันสามารถตรวจจับในตัวจัดการได้หากการตรวจสอบที่ป้องกันไม่ให้มีการดำเนินการจึงไม่เกิดขึ้นอีก ที่ดี!
Kaddath

ฉันใช้ Bootstrap 4 Tabs มันใช้งานได้ดีอย่างสมบูรณ์ ขอบคุณมาก. $ ('# v-pills-tab a'). on ('คลิก', ฟังก์ชั่น (e) {e.preventDefault (); setTimeout (ฟังก์ชั่น () {e.isDefaultPrevented = function () {return false;} $ ( '# v-pills-home-tab'). on ('แสดงผลของ. t.tab', function () {$ ('. mainDashboard'). show (); $ ('# changePlans'). ซ่อน (); });}, 1,000); $ (นี่) .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();หรือทำตามเงื่อนไข

คุณไม่สามารถเปลี่ยนแปลงได้อย่างแน่นอนเมื่อมีการกระทำของกิจกรรมดั้งเดิม

หากคุณต้องการ "สร้าง" เหตุการณ์ UI ดั้งเดิมในภายหลัง (กล่าวในการติดต่อกลับสำหรับคำขอ AJAX) คุณจะต้องปลอมมันด้วยวิธีอื่น (เช่นในคำตอบของ vzwick) ... แม้ว่าฉันจะ คำถามการใช้งานของวิธีการดังกล่าว



3

ตราบใดที่ "สิ่งต่าง ๆ มากมาย" ไม่ได้ทำอะไรแบบอะซิงโครนัสนี่คือสิ่งที่ไม่จำเป็นอย่างยิ่ง - เหตุการณ์จะเรียกผู้จัดการทุกคนตามลำดับดังนั้นถ้ามีเหตุการณ์ onklick ในองค์ประกอบหลักสิ่งนี้จะเกิดขึ้นหลังจาก onclik- เหตุการณ์ของเด็กได้ดำเนินการอย่างสมบูรณ์ จาวาสคริปต์ไม่ทำ "มัลติเธรด" ที่นี่ซึ่งทำให้ "หยุด" การประมวลผลเหตุการณ์ที่จำเป็น บทสรุป: "หยุดชั่วคราว" เหตุการณ์ที่เกิดขึ้นเพียงเพื่อดำเนินการต่อในตัวจัดการเดียวกันไม่มีเหตุผลใด ๆ

ถ้า "สิ่งต่าง ๆ มากมาย" เป็นสิ่งที่ไม่ตรงกันสิ่งนี้ก็ไม่สมเหตุสมผลเพราะมันป้องกันไม่ให้สิ่งต่าง ๆ แบบ asynchonous ทำในสิ่งที่พวกเขาควรทำ (สิ่ง asynchonous) และทำให้พวกเขามีความสุขเหมือนทุกอย่างในลำดับ (ที่เรากลับมาที่ย่อหน้าแรกของฉัน )


กระบวนการที่อยู่ตรงกลางไม่ตรงกันผมต้องการที่จะยิงผลในการเรียกกลับอาแจ็กซ์ที่ ...
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()ในกรณีนี้คุณจะไม่สามารถที่จะคลิกลิงก์อีกครั้งหลังจากเรียก นั่นเป็นเพราะกิจกรรมคลิกที่สร้างโดย jQuery เป็นเพียงชั้นบนสุดของกิจกรรมเบราว์เซอร์ดั้งเดิม ดังนั้นการทริกเกอร์เหตุการณ์ 'คลิก' บนแท็กจุดยึดจะไม่ติดตามลิงก์ แต่คุณสามารถใช้ห้องสมุดเช่นjquery-simulateที่จะช่วยให้คุณสามารถเปิดใช้งานเหตุการณ์เบราว์เซอร์ดั้งเดิม

รายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนี้สามารถพบได้ในลิงค์นี้


1

อีกวิธีคือใช้ window.setTimeout ใน Event listenerและรันโค้ดหลังจากกระบวนการของเหตุการณ์เสร็จสิ้นแล้ว สิ่งที่ต้องการ...

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

ฉันใช้0เป็นระยะเวลาเพราะฉันไม่สนใจที่จะรอ


1

ฉันรู้ว่าหัวข้อนี้เก่า แต่ฉันคิดว่าฉันสามารถมีส่วนร่วมได้ คุณสามารถทริกเกอร์พฤติกรรมเริ่มต้นของเหตุการณ์ในองค์ประกอบที่ระบุได้ตลอดเวลาในฟังก์ชั่นการจัดการของคุณถ้าคุณรู้แล้วว่าพฤติกรรมดังกล่าว ตัวอย่างเช่นเมื่อคุณทริกเกอร์เหตุการณ์คลิกที่ปุ่มรีเซ็ตคุณจะเรียกใช้ฟังก์ชั่นรีเซ็ตในรูปแบบที่ใกล้เคียงที่สุดซึ่งเป็นพฤติกรรมเริ่มต้น ในฟังก์ชั่นจัดการของคุณหลังจากใช้ฟังก์ชั่น PreventDefault คุณสามารถเรียกคืนพฤติกรรมเริ่มต้นได้โดยการเรียกใช้ฟังก์ชั่นรีเซ็ตในรูปแบบที่ใกล้เคียงที่สุดทุกที่ในรหัสตัวจัดการของคุณ


0

หากตัวอย่างนี้สามารถช่วยเพิ่ม "ป๊อปยืนยันที่กำหนดเอง" ในบางลิงก์ (ฉันเก็บรหัส "$ .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();
            }
        );
    }
);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.