Cách kiểm tra xem sự kiện nhấp đã bị ràng buộc chưa - JQuery


130

Tôi đang ràng buộc một sự kiện nhấp chuột bằng một nút:

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

Trong một kịch bản, điều này được gọi nhiều lần, vì vậy khi tôi thực hiện, triggertôi thấy nhiều cuộc gọi ajax mà tôi muốn ngăn chặn.

Làm thế nào để tôi bindchỉ khi nó không bị ràng buộc trước.


1
Man, tôi nghĩ + Konrad Garus có anwser an toàn nhất ( stackoverflow.com/a/6930078/842768 ), xem xét thay đổi câu trả lời được chấp nhận của bạn. Chúc mừng! CẬP NHẬT: Kiểm tra + bình luận của Herbert Peters là tốt! Đó là cách tiếp cận tốt nhất.
giovannipds

Để biết các tiêu chuẩn hiện tại, hãy xem câu trả lời của @A Morel bên dưới ( stackoverflow.com/a/50097988/1163705 ). Ít mã hóa hơn và đưa tất cả các công việc đoán, thuật toán và / hoặc tìm kiếm các phần tử hiện có ra khỏi phương trình.
Xandor

Câu trả lời:


117

Cập nhật ngày 24 tháng 8 năm 12 : Trong jQuery 1.8, không thể truy cập các sự kiện của phần tử bằng cách sử dụng .data('events'). (Xem lỗi này để biết chi tiết.) Có thể truy cập cùng một dữ liệu với jQuery._data(elem, 'events'), cấu trúc dữ liệu nội bộ, không có giấy tờ và do đó không được đảm bảo 100% để duy trì ổn định. Tuy nhiên, đây không phải là một vấn đề và dòng mã plugin có liên quan ở trên có thể được thay đổi thành như sau:

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

Các sự kiện jQuery được lưu trữ trong một đối tượng dữ liệu được gọi events, vì vậy bạn có thể tìm kiếm trong này:

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

Tất nhiên, sẽ là tốt nhất nếu bạn có thể cấu trúc ứng dụng của mình để mã này chỉ được gọi một lần.


Điều này có thể được gói gọn trong một plugin:

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

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

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

Sau đó bạn có thể gọi:

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

10
+1 cho chỉnh sửa. Đọc bài này hôm nay và tôi đang sử dụng 1.8. Cám ơn.
Gigi2m02

2
Cảm ơn đã chỉnh sửa. Đây chỉ là cho các nút hoặc tôi cũng có thể sử dụng nó cho <a>? Bởi vì khi tôi thử nó jQuery._data()báo lỗi sauTypeError: a is undefined
Houman

Tôi sẽ quấn dây var data = jQuery._data(this[0], 'events')[type];trong một lần thử và trả lại sai trong lần bắt. Nếu không có sự kiện nào bị ràng buộc với điều này [0] thì một cuộc gọi đến [loại] sẽ gây ra một số biến thể của "Không thể lấy thuộc tính 'nhấp chuột' của tham chiếu không xác định hoặc null" và rõ ràng điều đó cũng cho bạn biết những gì bạn cần biết.
Robb Vandaveer

Tôi không chắc chắn nếu đối tượng _data thay đổi trong jQuery 2.0.3 nhưng tôi không thể sử dụng $ .inArray cho việc này. Hàm bạn muốn so sánh là trong một thuộc tính của từng mục dữ liệu được gọi là "handler". Tôi đã sửa đổi nó để sử dụng một câu lệnh đơn giản và kiểm tra tính tương đương của chuỗi, điều mà tôi giả sử inArray đã làm. for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
Robb Vandaveer

tại sao không chuyển cập nhật / chỉnh sửa của bạn lên đầu? ... Đó là câu trả lời đúng thực sự.
Don Cheadle

179

Một cách nữa - đánh dấu các nút như vậy bằng một lớp và bộ lọc CSS:

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

Trong các phiên bản jQuery gần đây thay thế bindbằng on:

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

4
Thông minh! Cảm ơn Konrad, điều này đánh trúng điểm ngọt ngào. Tôi yêu cách tiếp cận thanh lịch và đơn giản như thế này. Tôi khá chắc chắn rằng nó cũng có hiệu suất cao hơn, vì mọi yếu tố đơn lẻ (đã có trình xử lý sự kiện nhấp kèm theo) không phải thực hiện tìm kiếm đầy đủ trong một số nhóm sự kiện lớn so sánh từng yếu tố. Trong triển khai của tôi, tôi đã đặt tên lớp trình trợ giúp được thêm vào là "click-ràng buộc", mà tôi nghĩ là một tùy chọn dễ bảo trì hơn: $ (". List-item: not (.click-ràng buộc)"). AddClass ("click-ràng buộc") ;
Nicholas Petersen

1
Như đã nêu trong câu trả lời được chấp nhận: "Tất nhiên là tốt nhất, nếu bạn có thể cấu trúc ứng dụng của mình để mã này chỉ được gọi một lần." Điều này thực hiện được điều đó.
Jeff Dege

+1 trong tình huống của tôi, tắt / bật và liên kết / hủy liên kết đã ngăn nhiều cuộc gọi. Đây là giải pháp hiệu quả.
Jay

2
Đây là giải pháp tốt hơn cho hiệu năng vì mã có thể kiểm tra sự hiện diện của lớp CSS và ràng buộc trượt đã có. Giải pháp khác thực hiện quy trình unbind-then-rebind với (ít nhất là fr4om những gì tôi có thể nói) nhiều chi phí hơn.
Phil Nicholas

1
2 vấn đề. (khi sử dụng nó trong một trình cắm có thể bị ràng buộc với một số thành phần) Sẽ không hoạt động khi liên kết một sự kiện thay đổi kích thước với đối tượng cửa sổ. Sử dụng dữ liệu ('ràng buộc', 1) thay vì addClass ('ràng buộc') là một cách tiếp cận khác hoạt động. Khi phá hủy sự kiện, nó có thể làm như vậy trong khi các trường hợp khác phụ thuộc vào nó. Vì vậy, kiểm tra nếu các trường hợp khác vẫn đang được sử dụng là khuyến khích.
Herbert Peters

59

Nếu sử dụng jQuery 1.7+:

Bạn có thể gọi offtrước on:

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

Nếu không:

Bạn chỉ có thể hủy liên kết sự kiện đầu tiên:

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

1
Đây chính xác không phải là một giải pháp đáng yêu, nhưng nó sẽ được cải thiện chỉ bằng cách mở khóa một trình xử lý : .unbind('click', onButtonClicked). Xem hướng dẫn
lonesomeday

16
Bạn thực sự có thể đặt tên cho trình xử lý của bạn khi bạn thêm chúng, sau đó việc bỏ ràng buộc trở nên khá đơn giản. $(sel).unbind("click.myNamespace");
Marc

@lonesomeday là ví dụ của bạn sử dụng 'unbind' có nghĩa là để hiển thị bất cứ điều gì khác nhau? Không phải .unbind thực sự giống như .off nên bạn phải viết$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
Jim

1
@Jim Bạn sẽ thấy câu hỏi đã được chỉnh sửa ba năm sau khi nhận xét của tôi! (Và cũng trong vài phút ngay sau bình luận của tôi. Nội dung mà tôi đang bình luận không còn tồn tại, đó là lý do tại sao nó có vẻ không có ý nghĩa nhiều.)
lonesomeday

@lonesomeday hmmm Tôi không chắc tại sao nó lại được chỉnh sửa, bạn có nghĩ tôi nên quay lại không?
Naftali aka Neal

8

Cách tốt nhất tôi thấy là sử dụng live () hoặc ủy nhiệm () để ghi lại sự kiện trong cha mẹ chứ không phải trong mỗi phần tử con.

Nếu nút của bạn nằm trong phần tử #parent, bạn có thể thay thế:

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

bởi

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

ngay cả khi #myButton chưa tồn tại khi mã này được thực thi.


2
Các phương pháp không dùng nữa
dobs

8

Tôi đã viết một plugin rất nhỏ gọi là "một lần" để làm điều đó. Thực hiện tắt và bật trong phần tử.

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

Và đơn giản là:

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

9
.one () sẽ bỏ trình xử lý sau lần thực hiện đầu tiên.
Rodolfo Jorge Nemer Nogueira

3
ở đây "một lần" là để gắn một trình xử lý một lần. "một" trong jquery là để chạy một lần chứ không phải đính kèm một lần.
Shishir Arora

1
Tôi biết rằng tôi sẽ đến sau khi thực tế, nhưng không offloại bỏ tất cả các trình xử lý cho loại đã cho? Có nghĩa là nếu có một trình xử lý nhấp chuột riêng biệt không liên quan nhưng trên cùng một mục, liệu điều đó có bị xóa không?
Xandor

chỉ sử dụng một không gian tên trong sự kiện để nó không bỏ qua tất cả các trình xử lý như: 'keypup.test123'
SemanticZen

6

Tại sao không sử dụng cái này

unbind() trước bind()

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

1
$('#myButton').off().on('click', onButtonClicked);cũng làm việc cho tôi
Veerakran Sereerungruangkul

Làm việc cho tôi ... giải pháp gr8
imdadhusen

Xinh đẹp. Hoạt động hoàn hảo cho tôi cho một nút đã bị ràng buộc.
seanbreeden

3

Đây là phiên bản của tôi:

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

Sử dụng:

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

2

Dựa trên câu trả lời @ konrad-garus, nhưng sử dụng data, vì tôi tin rằng classnên được sử dụng chủ yếu để tạo kiểu.

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

2

Để tránh kiểm tra / ràng buộc / hủy liên kết, bạn có thể thay đổi cách tiếp cận của mình! Tại sao bạn không sử dụng Jquery .on ()?

Vì Jquery 1.7, .live (), .delegate () không được dùng nữa, nên bây giờ bạn có thể sử dụng .on () để

Đính kèm một trình xử lý sự kiện cho tất cả các yếu tố phù hợp với bộ chọn hiện tại, hiện tại và trong tương lai

Điều đó có nghĩa là bạn có thể đính kèm một sự kiện vào một phần tử cha mẹ vẫn còn tồn tại và đính kèm các phần tử con cho dù chúng có mặt hay không!

Khi bạn sử dụng .on () như thế này:

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

Bạn bắt sự kiện nhấp vào phụ huynh và nó tìm kiếm con '#myButton' nếu tồn tại ...

Vì vậy, khi bạn xóa hoặc thêm một phần tử con, bạn không phải lo lắng về việc nên thêm hoặc xóa ràng buộc sự kiện.


Đây hoàn toàn là câu trả lời tốt nhất ở đây cho các tiêu chuẩn hiện tại. Tôi không nghĩ rằng tính năng cài đặt trình xử lý này cho phụ huynh để theo dõi cho trẻ đã được quảng cáo tốt nhưng là một giải pháp khá thanh lịch. Tôi đã có một sự kiện cháy nhiều lần và mỗi lần tổng số lần nó bắn liên tục tăng. Dòng duy nhất này đã thực hiện toàn bộ thủ thuật và không sử dụng .offmà tôi tin rằng sẽ loại bỏ các trình xử lý không liên quan trong quy trình.
Xandor

1

Thử:

if (typeof($("#myButton").click) != "function") 
{
   $("#myButton").click(onButtonClicked);
}

0
if ($("#btn").data('events') != undefined && $("#btn").data('events').click != undefined) {
    //do nothing as the click event is already there
} else {
    $("#btn").click(function (e) {
        alert('test');
    });
}

0

Kể từ tháng 6 năm 2019, tôi đã cập nhật chức năng (và nó hoạt động cho những gì tôi cần)

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

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

-2

JQuery có giải pháp :

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

tương đương:

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

1
Câu trả lời của bạn không liên quan đến câu hỏi. Câu hỏi là về chức năng ràng buộc với sự kiện của phần tử chỉ một lần.
Michał Zalewski
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.