Tất cả các sự kiện jquery nên được ràng buộc với $ (tài liệu)?


163

Nơi này đến từ đâu

Khi tôi mới học jQuery, tôi thường đính kèm các sự kiện như thế này:

$('.my-widget a').click(function() {
    $(this).toggleClass('active');
});

Sau khi tìm hiểu thêm về tốc độ của bộ chọn và phân quyền sự kiện, tôi đã đọc ở một số nơi rằng "phân quyền sự kiện jQuery sẽ làm cho mã của bạn nhanh hơn". Vì vậy, tôi bắt đầu viết mã như thế này:

$('.my-widget').on('click','a',function() {
    $(this).toggleClass('active');
});

Đây cũng là cách được đề xuất để sao chép hành vi của sự kiện .live () không dùng nữa. Điều này rất quan trọng đối với tôi vì rất nhiều trang web của tôi luôn tự động thêm / xóa các widget. Mặc dù ở trên không hoạt động chính xác như .live (), vì chỉ các phần tử được thêm vào vùng chứa '.my-widget' hiện có mới có hành vi. Nếu tôi tự động thêm một khối html khác sau khi mã đó đã chạy, các phần tử đó sẽ không nhận được các sự kiện ràng buộc với chúng. Như thế này:

setTimeout(function() {
    $('body').append('<div class="my-widget"><a>Click does nothing</a></div>');
}, 1000);


Những gì tôi muốn đạt được:

  1. hành vi cũ của .live () // có nghĩa là gắn các sự kiện vào các phần tử chưa tồn tại
  2. lợi ích của .on ()
  3. hiệu suất nhanh nhất để liên kết các sự kiện
  4. Cách đơn giản để quản lý sự kiện

Bây giờ tôi đính kèm tất cả các sự kiện như thế này:

$(document).on('click.my-widget-namespace', '.my-widget a', function() {
    $(this).toggleClass('active');
});

Mà dường như đáp ứng tất cả các mục tiêu của tôi. . Không gian tên là tuyệt vời vì nó giúp dễ dàng chuyển đổi trình nghe sự kiện.

Giải pháp / câu hỏi của tôi

Vì vậy, tôi bắt đầu nghĩ rằng các sự kiện jQuery phải luôn được ràng buộc với $ (tài liệu).
Có bất kỳ lý do tại sao bạn sẽ không muốn làm điều này?
Điều này có thể được coi là một thực hành tốt nhất? Nếu không, tại sao?

Nếu bạn đã đọc toàn bộ điều này, cảm ơn bạn. Tôi đánh giá cao bất kỳ / tất cả thông tin phản hồi / hiểu biết.

Giả định:

  1. Sử dụng jQuery hỗ trợ .on()// ít nhất là phiên bản 1.7
  2. Bạn muốn sự kiện được thêm vào nội dung được thêm động

Bài đọc / Ví dụ:

  1. http://24ways.org/2011/your-jquery-now-with-less-suck
  2. http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
  3. http://www.jasonbuckboyer.com/playground/speed/speed.html
  4. http://api.jquery.com/on/

Câu trả lời:


215

Không - bạn KHÔNG nên ràng buộc tất cả các trình xử lý sự kiện được ủy quyền vào documentđối tượng. Đó có lẽ là kịch bản thực hiện tồi tệ nhất bạn có thể tạo ra.

Trước hết, ủy nhiệm sự kiện không phải lúc nào cũng làm cho mã của bạn nhanh hơn. Trong một số trường hợp, đó là lợi thế và trong một số trường hợp thì không. Bạn nên sử dụng ủy quyền sự kiện khi bạn thực sự cần ủy quyền sự kiện và khi bạn được hưởng lợi từ nó. Mặt khác, bạn nên liên kết các trình xử lý sự kiện trực tiếp với các đối tượng nơi sự kiện xảy ra vì điều này thường sẽ hiệu quả hơn.

Thứ hai, bạn KHÔNG nên ràng buộc tất cả các sự kiện được ủy quyền ở cấp độ tài liệu. Đây chính xác là lý do tại sao .live()bị phản đối vì điều này rất không hiệu quả khi bạn có rất nhiều sự kiện bị ràng buộc theo cách này. Đối với việc xử lý sự kiện được ủy nhiệm, RẤT NHIỀU để ràng buộc chúng với cha mẹ gần nhất không động.

Thứ ba, không phải tất cả các sự kiện hoạt động hoặc tất cả các vấn đề có thể được giải quyết với ủy quyền. Ví dụ: nếu bạn muốn chặn các sự kiện chính trên điều khiển đầu vào và chặn các khóa không hợp lệ được nhập vào điều khiển đầu vào, bạn không thể làm điều đó với việc xử lý sự kiện được ủy quyền bởi vì khi sự kiện nổi lên với trình xử lý được ủy quyền, thì nó đã đã được xử lý bởi điều khiển đầu vào và đã quá muộn để ảnh hưởng đến hành vi đó.

Dưới đây là những thời điểm mà sự kiện được yêu cầu hoặc thuận lợi:

  • Khi các đối tượng bạn đang chụp các sự kiện trên được tạo / xóa động và bạn vẫn muốn chụp các sự kiện trên chúng mà không cần phải xử lý lại rõ ràng các trình xử lý sự kiện mỗi khi bạn tạo một sự kiện mới.
  • Khi bạn có rất nhiều đối tượng mà tất cả đều muốn cùng một trình xử lý sự kiện (trong đó số lượng ít nhất là hàng trăm). Trong trường hợp này, có thể hiệu quả hơn tại thời điểm thiết lập để liên kết một trình xử lý sự kiện được ủy quyền thay vì hàng trăm hoặc nhiều trình xử lý sự kiện trực tiếp. Lưu ý, xử lý sự kiện được ủy nhiệm luôn kém hiệu quả hơn trong thời gian chạy so với xử lý sự kiện trực tiếp.
  • Khi bạn đang cố gắng nắm bắt (ở mức cao hơn trong tài liệu của mình) các sự kiện xảy ra trên bất kỳ yếu tố nào trong tài liệu.
  • Khi thiết kế của bạn rõ ràng bằng cách sử dụng bong bóng sự kiện và stopPropagation () để giải quyết một số vấn đề hoặc tính năng trong trang của bạn.

Để hiểu điều này nhiều hơn một chút, người ta cần hiểu cách các trình xử lý sự kiện được ủy quyền của jQuery hoạt động. Khi bạn gọi một cái gì đó như thế này:

$("#myParent").on('click', 'button.actionButton', myFn);

Nó cài đặt một trình xử lý sự kiện jQuery chung trên #myParentđối tượng. Khi một sự kiện nhấp vào bong bóng đến trình xử lý sự kiện được ủy quyền này, jQuery phải xem qua danh sách các trình xử lý sự kiện được ủy quyền gắn với đối tượng này và xem liệu phần tử khởi tạo cho sự kiện có khớp với bất kỳ bộ chọn nào trong các trình xử lý sự kiện được ủy nhiệm không.

Do các bộ chọn có thể tham gia khá nhiều, điều này có nghĩa là jQuery phải phân tích từng bộ chọn và sau đó so sánh nó với các đặc điểm của mục tiêu sự kiện ban đầu để xem nó có khớp với từng bộ chọn không. Đây không phải là một hoạt động giá rẻ. Sẽ không có vấn đề gì lớn nếu chỉ có một trong số họ, nhưng nếu bạn đặt tất cả các công cụ chọn của mình vào đối tượng tài liệu và có hàng trăm công cụ chọn để so sánh với mọi sự kiện bị bong bóng, điều này có thể nghiêm túc bắt đầu xử lý hiệu suất xử lý sự kiện.

Vì lý do này, bạn muốn thiết lập trình xử lý sự kiện được ủy quyền của mình để trình xử lý sự kiện được ủy nhiệm càng gần đối tượng mục tiêu càng thiết thực. Điều này có nghĩa là ít sự kiện sẽ bong bóng qua mỗi bộ xử lý sự kiện được ủy quyền, do đó cải thiện hiệu suất. Đặt tất cả các sự kiện được ủy nhiệm vào đối tượng tài liệu là hiệu suất tồi tệ nhất có thể vì tất cả các sự kiện bị bong bóng phải trải qua tất cả các trình xử lý sự kiện được ủy quyền và được đánh giá theo tất cả các bộ chọn sự kiện được ủy quyền có thể. Đây chính xác là lý do tại sao .live()bị phản đối bởi vì đây là những gì .live()đã làm và nó tỏ ra rất kém hiệu quả.


Vì vậy, để đạt được hiệu suất tối ưu hóa:

  1. Chỉ sử dụng xử lý sự kiện được ủy quyền khi nó thực sự cung cấp một tính năng bạn cần hoặc tăng hiệu suất. Đừng chỉ luôn sử dụng nó bởi vì nó dễ dàng bởi vì khi bạn không thực sự cần nó. Nó thực sự hoạt động kém hơn tại thời gian gửi sự kiện so với ràng buộc sự kiện trực tiếp.
  2. Đính kèm các trình xử lý sự kiện được ủy quyền cho phụ huynh gần nhất với nguồn của sự kiện càng tốt. Nếu bạn đang sử dụng xử lý sự kiện được ủy quyền vì bạn có các yếu tố động mà bạn muốn nắm bắt các sự kiện, thì hãy chọn cha mẹ gần nhất không phải là động.
  3. Sử dụng các bộ chọn dễ đánh giá cho các trình xử lý sự kiện được ủy quyền. Nếu bạn đã theo dõi cách xử lý sự kiện được ủy nhiệm hoạt động, bạn sẽ hiểu rằng trình xử lý sự kiện được ủy nhiệm phải được so sánh với nhiều đối tượng nhiều lần để chọn bộ chọn hiệu quả nhất có thể hoặc thêm các lớp đơn giản vào đối tượng của bạn để có thể sử dụng bộ chọn đơn giản hơn tăng hiệu suất xử lý sự kiện được ủy quyền.

2
Tuyệt vời. Cảm ơn bạn đã mô tả nhanh chóng và chi tiết. Điều đó có nghĩa là thời gian gửi sự kiện sẽ tăng lên bằng cách sử dụng .on () nhưng tôi nghĩ rằng tôi vẫn đang vật lộn để quyết định giữa thời gian gửi sự kiện tăng so với nhóm xử lý trang ban đầu. Tôi thường giảm thời gian trang ban đầu. Ví dụ: nếu tôi có 200 phần tử trên một trang (và nhiều hơn khi các sự kiện xảy ra), nó sẽ đắt hơn gấp 100 lần ở lần tải đầu tiên (vì nó phải thêm 100 trình lắng nghe sự kiện) thay vì nếu tôi thêm một trình lắng nghe sự kiện đơn lẻ vào liên kết
Buck

Cũng muốn nói rằng bạn đã thuyết phục tôi - Đừng ràng buộc tất cả các sự kiện với $ (tài liệu). Liên kết với cha mẹ không thay đổi gần nhất có thể. Tôi đoán tôi sẽ vẫn phải quyết định trong từng trường hợp cụ thể khi ủy nhiệm sự kiện tốt hơn là liên kết trực tiếp một sự kiện với một yếu tố. Và khi tôi cần các sự kiện gắn liền với nội dung động (không có cha mẹ không thay đổi) tôi đoán tôi sẽ tiếp tục làm những gì @Vega đề xuất bên dưới và chỉ cần "ràng buộc (các) trình xử lý sau khi nội dung được chèn vào ( ) DOM ", sử dụng tham số ngữ cảnh của jQuery trong bộ chọn của tôi.
Buck

5
@ user1394692 - nếu bạn có nhiều yếu tố gần như giống hệt nhau, thì có thể sử dụng các trình xử lý sự kiện được ủy quyền cho chúng. Tôi nói điều đó trong câu trả lời của tôi. Nếu tôi có một bảng 500 hàng khổng lồ và có cùng một nút trong mỗi hàng của một cột cụ thể, tôi sẽ sử dụng một trình xử lý sự kiện được ủy nhiệm trên bảng để phục vụ tất cả các nút. Nhưng nếu tôi có 200 phần tử và tất cả chúng đều cần trình xử lý sự kiện độc đáo của riêng chúng, cài đặt 200 trình xử lý sự kiện được ủy nhiệm không nhanh hơn cài đặt 200 trình xử lý sự kiện bị ràng buộc trực tiếp, tuy nhiên việc xử lý sự kiện được ủy nhiệm có thể chậm hơn rất nhiều khi thực hiện sự kiện.
jfriend00

Bạn thật hoàn hảo. Hoàn toàn đồng ý! Tôi có hiểu rằng đây là điều tồi tệ nhất tôi có thể làm - $(document).on('click', '[myCustomAttr]', function () { ... });?
Artem Fitiskin

Sử dụng pjax / turbolinks đặt ra một vấn đề thú vị vì tất cả các yếu tố được tạo / xóa động (ngoài tải trang đầu tiên). Vì vậy, tốt hơn là liên kết tất cả các sự kiện một lần với document, hoặc liên kết với các lựa chọn cụ thể hơn page:loadvà hủy liên kết các sự kiện này page:after-remove? Hmmm
Dom Christie

9

Phân quyền sự kiện là một kỹ thuật để viết các trình xử lý của bạn trước khi phần tử thực sự tồn tại trong DOM. Phương pháp này có nhược điểm riêng và chỉ nên được sử dụng nếu bạn có yêu cầu như vậy.

Khi nào bạn nên sử dụng đoàn sự kiện?

  1. Khi bạn liên kết một trình xử lý chung cho nhiều phần tử cần cùng chức năng. (Ví dụ: di chuột hàng bảng)
    • Trong ví dụ này, nếu bạn phải liên kết tất cả các hàng bằng liên kết trực tiếp, cuối cùng bạn sẽ tạo n handler cho n hàng trong bảng đó. Bằng cách sử dụng phương thức ủy nhiệm, bạn có thể xử lý tất cả những người trong 1 xử lý đơn giản.
  2. Khi bạn thêm nội dung động thường xuyên hơn trong DOM (Ví dụ: Thêm / xóa hàng khỏi bảng)

Tại sao bạn không nên sử dụng phái đoàn sự kiện?

  1. Sự kiện ủy nhiệm chậm hơn khi so sánh với việc ràng buộc sự kiện trực tiếp với phần tử.
    • Nó so sánh bộ chọn mục tiêu trên mỗi bong bóng mà nó chạm vào, việc so sánh sẽ tốn kém vì nó phức tạp.
  2. Không kiểm soát sự kiện sủi bọt cho đến khi nó chạm vào yếu tố mà nó bị ràng buộc.

PS: Ngay cả đối với nội dung động, bạn không phải sử dụng phương thức ủy nhiệm sự kiện nếu bạn liên kết trình xử lý sau khi nội dung được chèn vào DOM. (Nếu nội dung động được thêm vào không thường xuyên bị xóa / thêm lại)



0

Tôi muốn thêm một số nhận xét và phản biện vào câu trả lời của jfriend00. (chủ yếu chỉ là ý kiến ​​của tôi dựa trên cảm giác ruột của tôi)

Không - bạn KHÔNG nên ràng buộc tất cả các trình xử lý sự kiện được ủy quyền vào đối tượng tài liệu. Đó có lẽ là kịch bản thực hiện tồi tệ nhất bạn có thể tạo ra.

Trước hết, ủy nhiệm sự kiện không phải lúc nào cũng làm cho mã của bạn nhanh hơn. Trong một số trường hợp, đó là lợi thế và trong một số trường hợp thì không. Bạn nên sử dụng ủy quyền sự kiện khi bạn thực sự cần ủy quyền sự kiện và khi bạn được hưởng lợi từ nó. Mặt khác, bạn nên liên kết các trình xử lý sự kiện trực tiếp với các đối tượng nơi sự kiện xảy ra vì điều này thường sẽ hiệu quả hơn.

Mặc dù sự thật là hiệu suất có thể tốt hơn một chút nếu bạn chỉ đăng ký và tổ chức sự kiện cho một yếu tố duy nhất, tôi tin rằng nó không cân nhắc với lợi ích về khả năng mở rộng mà phái đoàn mang lại. Tôi cũng tin rằng các trình duyệt đang (sẽ) xử lý việc này ngày càng hiệu quả hơn, mặc dù tôi không có bằng chứng nào về việc này. Theo tôi, đoàn sự kiện là con đường để đi!

Thứ hai, bạn KHÔNG nên ràng buộc tất cả các sự kiện được ủy quyền ở cấp độ tài liệu. Đây chính xác là lý do tại sao .live () không được dùng nữa vì điều này rất không hiệu quả khi bạn có rất nhiều sự kiện bị ràng buộc theo cách này. Đối với việc xử lý sự kiện được ủy nhiệm, RẤT NHIỀU để ràng buộc chúng với cha mẹ gần nhất không động.

Tôi đồng ý về điều này. Nếu bạn chắc chắn 100% rằng một sự kiện sẽ chỉ xảy ra bên trong một container, sẽ rất hợp lý khi liên kết sự kiện với container này, nhưng tôi vẫn sẽ tranh luận trực tiếp với các sự kiện ràng buộc với yếu tố kích hoạt.

Thứ ba, không phải tất cả các sự kiện hoạt động hoặc tất cả các vấn đề có thể được giải quyết với ủy quyền. Ví dụ: nếu bạn muốn chặn các sự kiện chính trên điều khiển đầu vào và chặn các khóa không hợp lệ được nhập vào điều khiển đầu vào, bạn không thể làm điều đó với việc xử lý sự kiện được ủy quyền bởi vì khi sự kiện nổi lên với trình xử lý được ủy quyền, thì nó đã đã được xử lý bởi điều khiển đầu vào và đã quá muộn để ảnh hưởng đến hành vi đó.

Đơn giản là nó sai. Vui lòng xem mã nàyPen: https://codepen.io/pwkip/pen/jObGmjq

document.addEventListener('keypress', (e) => {
    e.preventDefault();
});

Nó minh họa cách bạn có thể ngăn người dùng gõ bằng cách đăng ký sự kiện nhấn phím trên tài liệu.

Dưới đây là những thời điểm mà sự kiện được yêu cầu hoặc thuận lợi:

Khi các đối tượng bạn đang chụp các sự kiện trên được tạo / xóa động và bạn vẫn muốn ghi lại các sự kiện trên chúng mà không cần phải xử lý lại rõ ràng các trình xử lý sự kiện mỗi khi bạn tạo một sự kiện mới. Khi bạn có rất nhiều đối tượng mà tất cả đều muốn cùng một trình xử lý sự kiện (trong đó số lượng ít nhất là hàng trăm). Trong trường hợp này, có thể hiệu quả hơn tại thời điểm thiết lập để liên kết một trình xử lý sự kiện được ủy quyền thay vì hàng trăm hoặc nhiều trình xử lý sự kiện trực tiếp. Lưu ý, xử lý sự kiện được ủy nhiệm luôn kém hiệu quả hơn trong thời gian chạy so với xử lý sự kiện trực tiếp.

Tôi muốn trả lời với trích dẫn này từ https://ehsangazar.com/optimizing-javascript-event-listpers-for-performance-e28406ad406c

Event delegation promotes binding as few DOM event handlers as possible, since each event handler requires memory. For example, let’s say that we have an HTML unordered list we want to bind event handlers to. Instead of binding a click event handler for each list item (which may be hundreds for all we know), we bind one click handler to the parent unordered list itself.

Ngoài ra, googling cho performance cost of event delegation googletrả lại nhiều kết quả có lợi cho phái đoàn sự kiện.

Khi bạn đang cố gắng nắm bắt (ở mức cao hơn trong tài liệu của mình) các sự kiện xảy ra trên bất kỳ yếu tố nào trong tài liệu. Khi thiết kế của bạn rõ ràng bằng cách sử dụng bong bóng sự kiện và stopPropagation () để giải quyết một số vấn đề hoặc tính năng trong trang của bạn. Để hiểu điều này nhiều hơn một chút, người ta cần hiểu cách các trình xử lý sự kiện được ủy quyền của jQuery hoạt động. Khi bạn gọi một cái gì đó như thế này:

$ ("# myParent"). on ('click', 'button.actionButton', myFn); Nó cài đặt một trình xử lý sự kiện jQuery chung trên đối tượng #myParent. Khi một sự kiện nhấp vào bong bóng đến trình xử lý sự kiện được ủy quyền này, jQuery phải xem qua danh sách các trình xử lý sự kiện được ủy quyền gắn với đối tượng này và xem liệu phần tử khởi tạo cho sự kiện có khớp với bất kỳ bộ chọn nào trong các trình xử lý sự kiện được ủy nhiệm không.

Do các bộ chọn có thể tham gia khá nhiều, điều này có nghĩa là jQuery phải phân tích từng bộ chọn và sau đó so sánh nó với các đặc điểm của mục tiêu sự kiện ban đầu để xem nó có khớp với từng bộ chọn không. Đây không phải là một hoạt động giá rẻ. Sẽ không có vấn đề gì lớn nếu chỉ có một trong số họ, nhưng nếu bạn đặt tất cả các công cụ chọn của mình vào đối tượng tài liệu và có hàng trăm công cụ chọn để so sánh với mọi sự kiện bị bong bóng, điều này có thể nghiêm túc bắt đầu xử lý hiệu suất xử lý sự kiện.

Vì lý do này, bạn muốn thiết lập trình xử lý sự kiện được ủy quyền của mình để trình xử lý sự kiện được ủy nhiệm càng gần đối tượng mục tiêu càng thiết thực. Điều này có nghĩa là ít sự kiện sẽ bong bóng qua mỗi bộ xử lý sự kiện được ủy quyền, do đó cải thiện hiệu suất. Đặt tất cả các sự kiện được ủy nhiệm vào đối tượng tài liệu là hiệu suất tồi tệ nhất có thể vì tất cả các sự kiện bị bong bóng phải trải qua tất cả các trình xử lý sự kiện được ủy quyền và được đánh giá theo tất cả các bộ chọn sự kiện được ủy quyền có thể. Đây chính xác là lý do tại sao .live () bị phản đối vì đây là những gì .live () đã làm và nó tỏ ra rất kém hiệu quả.

Tài liệu này ở đâu? Nếu đó là sự thật, thì jQuery dường như đang xử lý ủy quyền theo cách rất kém hiệu quả, và sau đó các đối số phản biện của tôi chỉ nên được áp dụng cho vanilla JS.

Tuy nhiên: Tôi muốn tìm một nguồn chính thức hỗ trợ cho yêu cầu này.

:: BIÊN TẬP ::

Có vẻ như jQuery thực sự đang thực hiện bong bóng sự kiện theo cách rất kém hiệu quả (vì họ hỗ trợ IE8) https://api.jquery.com/on/#event-performance

Vì vậy, hầu hết các lập luận của tôi ở đây chỉ đúng với vanilla JS và các trình duyệt hiện đại.

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.