วิธีตรวจสอบว่าเหตุการณ์การคลิกถูกผูกไว้แล้วหรือไม่ - JQuery


130

ฉันผูกเหตุการณ์การคลิกด้วยปุ่ม:

$('#myButton').bind('click',  onButtonClicked);

ในสถานการณ์หนึ่งสิ่งนี้ถูกเรียกหลายครั้งดังนั้นเมื่อฉันทำtriggerฉันเห็นการโทรของ ajax หลายครั้งซึ่งฉันต้องการป้องกัน

ฉันจะทำอย่างไรbindถ้ามันไม่เคยผูกพันมาก่อน


1
ฉันคิดว่า + Konrad Garus มี anwser ที่ปลอดภัยที่สุด ( stackoverflow.com/a/6930078/842768 ) ลองเปลี่ยนคำตอบที่คุณยอมรับ ไชโย! UPDATE: ตรวจสอบ + ความคิดเห็นของ Herbert Peters ด้วย! นั่นเป็นแนวทางที่ดีที่สุด
giovannipds

สำหรับมาตรฐานปัจจุบันโปรดดูคำตอบของ @A Morel ด้านล่าง ( stackoverflow.com/a/50097988/1163705 ) เขียนโค้ดน้อยลงและใช้เวลาเดาอัลกอริทึมและ / หรือค้นหาองค์ประกอบที่มีอยู่ทั้งหมดออกจากสมการ
Xandor

คำตอบ:


118

อัปเดต 24 ส.ค. 55 : ใน jQuery 1.8 จะไม่สามารถเข้าถึงเหตุการณ์ขององค์ประกอบโดยใช้.data('events')ไฟล์. (ดูรายละเอียดจุดบกพร่องนี้ ) เป็นไปได้ที่จะเข้าถึงข้อมูลเดียวกันด้วยjQuery._data(elem, 'events')โครงสร้างข้อมูลภายในซึ่งไม่เป็นเอกสารดังนั้นจึงไม่รับประกัน 100% ว่าจะยังคงเสถียร อย่างไรก็ตามสิ่งนี้ไม่ควรเป็นปัญหาและสามารถเปลี่ยนบรรทัดที่เกี่ยวข้องของโค้ดปลั๊กอินด้านบนเป็นดังต่อไปนี้:

var data = jQuery._data(this[0], 'events')[type];

เหตุการณ์ jQuery ถูกเก็บไว้ในวัตถุข้อมูลที่เรียกว่าeventsดังนั้นคุณสามารถค้นหาในสิ่งนี้:

var button = $('#myButton');
if (-1 !== $.inArray(onButtonClicked, button.data('events').click)) {
    button.click(onButtonClicked);
}

แน่นอนว่าจะเป็นการดีที่สุดหากคุณสามารถจัดโครงสร้างแอปพลิเคชันของคุณเพื่อให้มีการเรียกรหัสนี้เพียงครั้งเดียว


สิ่งนี้สามารถห่อหุ้มไว้ในปลั๊กอิน:

$.fn.isBound = function(type, fn) {
    var data = this.data('events')[type];

    if (data === undefined || data.length === 0) {
        return false;
    }

    return (-1 !== $.inArray(fn, data));
};

จากนั้นคุณสามารถโทร:

var button = $('#myButton');
if (!button.isBound('click', onButtonClicked)) {
    button.click(onButtonClicked);
}

10
+1 สำหรับการแก้ไข อ่านโพสต์นี้วันนี้และฉันใช้ 1.8 ขอบคุณ.
Gigi2m02

2
ขอบคุณสำหรับการแก้ไข ปุ่มนี้ใช้สำหรับปุ่มเท่านั้นหรือใช้เพื่อ<a>อะไร เพราะเมื่อฉันลองมันบอกjQuery._data()ข้อผิดพลาดต่อไปนี้TypeError: a is undefined
Houman

ฉันจะตัดบรรทัดvar data = jQuery._data(this[0], 'events')[type];ในการลองจับและส่งคืนเท็จในการจับ หากไม่มีเหตุการณ์ใดผูกมัดกับ [0] นี้นอกจากการเรียกไปที่ [type] จะทำให้เกิดการเปลี่ยนแปลงบางอย่างของ "ไม่สามารถรับคุณสมบัติ 'คลิก' ของการอ้างอิงที่ไม่ได้กำหนดหรือเป็นค่าว่าง" และเห็นได้ชัดว่านั่นจะบอกสิ่งที่คุณต้องรู้ด้วย
Robb Vandaveer

ฉันไม่แน่ใจว่าวัตถุ _data เปลี่ยนแปลงใน jQuery 2.0.3 หรือไม่ แต่ฉันไม่สามารถใช้ $ .inArray สำหรับสิ่งนี้ ฟังก์ชันที่คุณต้องการเปรียบเทียบอยู่ในคุณสมบัติของรายการข้อมูลแต่ละรายการที่เรียกว่า "ตัวจัดการ" ฉันแก้ไขให้ใช้คำสั่งง่ายๆและตรวจสอบความเท่าเทียมกันของสตริงสิ่งที่ฉันคิดว่า inArray ทำ for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
Robb Vandaveer

ทำไมไม่ย้ายการอัปเดต / แก้ไขไปด้านบนล่ะ ... เป็นคำตอบที่ถูกต้องจริงๆ
Don Cheadle

180

อีกวิธีหนึ่ง - ทำเครื่องหมายปุ่มดังกล่าวด้วยคลาส CSS และตัวกรอง:

$('#myButton:not(.bound)').addClass('bound').bind('click',  onButtonClicked);

ใน jQuery เวอร์ชันล่าสุดแทนที่bindด้วยon:

$('#myButton:not(.bound)').addClass('bound').on('click',  onButtonClicked);

4
ยอดเยี่ยม ขอบคุณ Konrad สิ่งนี้ยอดเยี่ยม ฉันชอบแนวทางที่เรียบง่ายและสง่างามเช่นนี้ ฉันค่อนข้างแน่ใจว่ามันมีประสิทธิภาพมากกว่าเช่นกันเนื่องจากทุกองค์ประกอบ (ซึ่งมีตัวจัดการเหตุการณ์คลิกแนบอยู่แล้ว) ไม่จำเป็นต้องทำการค้นหาแบบเต็มในที่เก็บข้อมูลขนาดใหญ่บางเหตุการณ์โดยเปรียบเทียบแต่ละองค์ประกอบ ในการใช้งานของฉันฉันตั้งชื่อคลาสตัวช่วยที่เพิ่มเข้ามาว่า "click-bound" ซึ่งฉันคิดว่าเป็นตัวเลือกที่ดูแลรักษาได้มากกว่า: $ (". list-item: not (.click-bound)"). addClass ("click-bound") ;
Nicholas Petersen

1
ตามที่ระบุไว้ในคำตอบที่ยอมรับ: "แน่นอนว่าจะเป็นการดีที่สุดหากคุณสามารถจัดโครงสร้างแอปพลิเคชันของคุณเพื่อให้มีการเรียกรหัสนี้เพียงครั้งเดียว" สิ่งนี้ทำให้สำเร็จ
Jeff Dege

+1 ในสถานการณ์ของฉันการปิด / เปิดและผูก / เลิกผูกไม่ได้ป้องกันการโทรหลายครั้ง นี่เป็นวิธีแก้ปัญหาที่ได้ผล
เจ

2
นี่เป็นทางออกที่ดีกว่าสำหรับประสิทธิภาพเนื่องจากโค้ดสามารถตรวจสอบการมีอยู่ของคลาส CSS และการผูกสลิปที่มีอยู่แล้ว วิธีแก้ปัญหาอื่น ๆ ดำเนินการขั้นตอนการยกเลิกการผูกมัดแล้ว rebind ด้วย (อย่างน้อยก็ fr4om สิ่งที่ฉันสามารถบอกได้) ค่าใช้จ่ายเพิ่มเติม
Phil Nicholas

1
2 ประเด็น. (เมื่อใช้ในปลั๊กอินที่สามารถผูกไว้กับหลายองค์ประกอบ) จะไม่ทำงานเมื่อผูกเหตุการณ์การปรับขนาดกับวัตถุหน้าต่าง ใช้ data ('bound', 1) แทน addClass ('bound') เป็นอีกวิธีที่ใช้ได้ผล เมื่อทำลายเหตุการณ์นั้นอาจทำได้ในขณะที่อินสแตนซ์อื่นขึ้นอยู่กับเหตุการณ์นั้น ดังนั้นควรตรวจสอบว่ายังคงใช้อินสแตนซ์อื่นอยู่หรือไม่
Herbert Peters

59

หากใช้ jQuery 1.7+:

โทรoffมาก่อนได้ที่on:

$('#myButton').off('click', onButtonClicked) // remove handler
              .on('click', onButtonClicked); // add handler

ถ้าไม่:

คุณสามารถเลิกผูกเหตุการณ์แรกได้:

$('#myButton').unbind('click', onButtonClicked) //remove handler
              .bind('click', onButtonClicked);  //add handler

1
นี้ไม่ว่าวิธีการแก้ปัญหาที่น่ารัก แต่ก็จะได้รับการปรับปรุงให้ดีขึ้นโดยเฉพาะการ unbinding .unbind('click', onButtonClicked)หนึ่งจัดการ: ดูคู่มือ
lonesomeday

16
จริงๆแล้วคุณสามารถเนมสเปซตัวจัดการของคุณได้เมื่อคุณเพิ่มจากนั้นการคลายการผูกจะกลายเป็นเรื่องง่าย $(sel).unbind("click.myNamespace");
Marc

@lonesomeday เป็นตัวอย่างของคุณที่ใช้ 'unbind' เพื่อแสดงอะไรที่แตกต่าง? ไม่ผูกมัดอย่างมีประสิทธิภาพเหมือนกับ. off ดังนั้นคุณต้องเขียน$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
จิม

1
@ จิมคุณจะเห็นว่าคำถามถูกแก้ไขสามปีหลังจากความคิดเห็นของฉัน! (และในไม่กี่นาทีหลังจากความคิดเห็นของฉันเนื้อหาที่ฉันแสดงความคิดเห็นก็ไม่มีอยู่อีกต่อไปซึ่งเป็นเหตุผลว่าทำไมมันจึงดูไม่สมเหตุสมผลเท่าไหร่)
lonesomeday

@lonesomeday อืมฉันไม่แน่ใจว่าทำไมถึงถูกแก้ไขคุณคิดว่าฉันควรย้อนกลับหรือไม่?
Naftali aka Neal

8

วิธีที่ดีที่สุดที่ฉันเห็นคือใช้ live () หรือ delegate () เพื่อจับภาพเหตุการณ์ในพาเรนต์ไม่ใช่ในแต่ละองค์ประกอบย่อย

หากปุ่มของคุณอยู่ในองค์ประกอบ #parent คุณสามารถแทนที่:

$('#myButton').bind('click', onButtonClicked);

โดย

$('#parent').delegate('#myButton', 'click', onButtonClicked);

แม้ว่า #myButton จะยังไม่มีเมื่อเรียกใช้รหัสนี้


2
เลิกใช้วิธี
dobs

8

ฉันเขียนปลั๊กอินเล็ก ๆ ที่เรียกว่า "ครั้งเดียว" ซึ่งทำเช่นนั้น ดำเนินการปิดและเปิดในองค์ประกอบ

$.fn.once = function(a, b) {
    return this.each(function() {
        $(this).off(a).on(a,b);
    });
};

และง่ายๆ:

$(element).once('click', function(){
});

9
.one () จะทิ้งตัวจัดการหลังจากการดำเนินการครั้งแรก
Rodolfo Jorge Nemer Nogueira

3
ที่นี่ "ครั้งเดียว" สำหรับการติดตั้งตัวจัดการหนึ่งครั้ง "one" ใน jquery ใช้สำหรับรันครั้งเดียวไม่ติดครั้งเดียว
Shishir Arora

1
ฉันรู้ว่าฉันกำลังเข้ามาหลังจากความจริง แต่ไม่ได้offลบตัวจัดการทั้งหมดสำหรับประเภทที่ระบุ? หมายความว่าถ้ามีตัวจัดการการคลิกแยกต่างหากที่ไม่เกี่ยวข้อง แต่อยู่ในรายการเดียวกันจะไม่ถูกลบออกไปด้วยหรือ
Xandor

เพียงแค่ใช้เนมสเปซในกิจกรรมเพื่อไม่ให้ตัวจัดการทั้งหมดเช่น 'keypup.test123'
SemanticZen

6

ทำไมไม่ใช้สิ่งนี้

unbind() ก่อน bind()

$('#myButton').unbind().bind('click',  onButtonClicked);

1
$('#myButton').off().on('click', onButtonClicked);ยังใช้ได้กับฉัน
วีระกรัณย์เสรีรุ่งเรืองกุล

ใช้ได้ผลสำหรับฉัน ... โซลูชัน gr8
imdadhusen

สวย. ทำงานได้ดีสำหรับฉันสำหรับปุ่มที่ผูกไว้แล้ว
seanbreeden

3

นี่คือเวอร์ชันของฉัน:

Utils.eventBoundToFunction = function (element, eventType, fCallback) {
    if (!element || !element.data('events') || !element.data('events')[eventType] || !fCallback) {
        return false;
    }

    for (runner in element.data('events')[eventType]) {
        if (element.data('events')[eventType][runner].handler == fCallback) {
            return true;
        }

    }

    return false;
};

การใช้งาน:

Utils.eventBoundToFunction(okButton, 'click', handleOkButtonFunction)

2

อ้างอิงจากคำตอบของ @ konrad-garus แต่การใช้dataเนื่องจากฉันเชื่อว่าclassควรใช้เป็นส่วนใหญ่ในการจัดแต่งทรงผม

if (!el.data("bound")) {
  el.data("bound", true);
  el.on("event", function(e) { ... });
}

2

เพื่อหลีกเลี่ยงการตรวจสอบ / ผูก / เลิกผูกคุณสามารถเปลี่ยนแนวทางของคุณได้! ทำไมคุณไม่ใช้ Jquery .on ()?

เนื่องจาก Jquery 1.7, .live (), .delegate () เลิกใช้งานแล้วตอนนี้คุณสามารถใช้. on () ถึง

แนบตัวจัดการเหตุการณ์สำหรับองค์ประกอบทั้งหมดที่ตรงกับตัวเลือกปัจจุบันในตอนนี้และในอนาคต

หมายความว่าคุณสามารถแนบเหตุการณ์กับองค์ประกอบหลักที่ยังคงมีอยู่และแนบองค์ประกอบลูกไม่ว่าจะมีอยู่หรือไม่ก็ตาม!

เมื่อคุณใช้. on () เช่นนี้:

$('#Parent').on('click', '#myButton'  onButtonClicked);

คุณจับเหตุการณ์คลิกที่ผู้ปกครองและค้นหาลูก '#myButton' ถ้ามี ...

ดังนั้นเมื่อคุณลบหรือเพิ่มองค์ประกอบลูกคุณไม่ต้องกังวลว่าจะเพิ่มหรือลบการเชื่อมโยงเหตุการณ์หรือไม่


นี่คือคำตอบที่ดีที่สุดสำหรับมาตรฐานปัจจุบัน ฉันไม่คิดว่าคุณลักษณะของการตั้งค่าตัวจัดการบนผู้ปกครองเพื่อดูเด็กนั้นได้รับการโฆษณาอย่างดี แต่เป็นวิธีที่ค่อนข้างหรูหรา ฉันมีเหตุการณ์ที่เกิดขึ้นหลายครั้งและแต่ละครั้งจำนวนครั้งทั้งหมดที่ยิงก็เพิ่มขึ้นเรื่อย ๆ บรรทัดเดียวนี้ใช้เคล็ดลับทั้งหมดและไม่ใช้.offซึ่งฉันเชื่อว่าจะลบตัวจัดการที่ไม่เกี่ยวข้องออกไปในกระบวนการ
Xandor



0

เมื่อเดือนมิถุนายน 2019 ฉันได้อัปเดตฟังก์ชันแล้ว (และใช้ได้กับสิ่งที่ฉันต้องการ)

$.fn.isBound = function (type) {
    var data = $._data($(this)[0], 'events');

    if (data[type] === undefined || data.length === 0) {
        return false;
    }
    return true;
};

-2

JQuery มีวิธีแก้ไข :

$( "#foo" ).one( "click", function() {
  alert( "This will be displayed only once." );
}); 

เทียบเท่า:

$( "#foo" ).on( "click", function( event ) {
  alert( "This will be displayed only once." );
  $( this ).off( event );
});

1
คำตอบของคุณไม่เกี่ยวข้องกับคำถาม คำถามเกี่ยวกับฟังก์ชันการผูกเข้ากับเหตุการณ์ขององค์ประกอบเพียงครั้งเดียว
Michał Zalewski
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.