Sự kiện ràng buộc trên các yếu tố năng động được tạo ra?


1677

Tôi có một chút mã nơi tôi đang lặp qua tất cả các hộp chọn trên một trang và ràng buộc một .hoversự kiện với chúng để thực hiện một chút điều chỉnh với độ rộng của chúng trên đó mouse on/off.

Điều này xảy ra trên trang đã sẵn sàng và hoạt động tốt.

Vấn đề tôi gặp phải là bất kỳ hộp chọn nào tôi thêm qua Ajax hoặc DOM sau vòng lặp ban đầu sẽ không bị ràng buộc sự kiện.

Tôi đã tìm thấy plugin này ( jQuery Live Query Plugin ), nhưng trước khi tôi thêm 5k vào các trang của mình bằng một plugin, tôi muốn xem có ai biết cách để làm điều này không, bằng jQuery trực tiếp hoặc bằng tùy chọn khác.

Câu trả lời:


2250

Kể từ jQuery 1.7, bạn nên sử dụng jQuery.fn.on:

$(staticAncestors).on(eventName, dynamicChild, function() {});

Trước đó , cách tiếp cận được đề xuất là sử dụng live():

$(selector).live( eventName, function(){} );

Tuy nhiên, live()đã bị phản đối trong 1.7 ủng hộ on()và loại bỏ hoàn toàn trong 1.9. Các live()chữ ký:

$(selector).live( eventName, function(){} );

... có thể được thay thế bằng on()chữ ký sau :

$(document).on( eventName, selector, function(){} );

Ví dụ: nếu trang của bạn tự động tạo các phần tử với tên lớp, dosomethingbạn sẽ liên kết sự kiện với cha mẹ đã tồn tại (đây là vấn đề không phải ở đây, bạn cần một cái gì đó tồn tại để liên kết, đừng liên kết với nội dung động), đây có thể là (và tùy chọn dễ nhất) là document. Mặc dù trong tâm trí documentcó thể không phải là lựa chọn hiệu quả nhất .

$(document).on('mouseover mouseout', '.dosomething', function(){
    // what you want to happen when mouseover and mouseout 
    // occurs on elements that match '.dosomething'
});

Bất kỳ cha mẹ tồn tại tại thời điểm sự kiện bị ràng buộc là tốt. Ví dụ

$('.buttons').on('click', 'button', function(){
    // do something here
});

sẽ áp dụng cho

<div class="buttons">
    <!-- <button>s that are generated dynamically and added here -->
</div>

7
Lưu ý rằng phương thức trực tiếp chỉ hoạt động đối với một số sự kiện nhất định chứ không phải các phương thức khác như loadmetadata. (Xem phần cảnh báo trong tài liệu.)
Sam Dutton

48
Tìm hiểu thêm về ủy quyền sự kiện tại đây: learn.jquery.com/events/event-delegation .
Felix Kling

9
Bất kỳ cách nào để thực hiện điều này với js javascript / vanilla thuần túy?
Ram Patra

15
@Ramswaroop bất cứ điều gì bạn có thể làm trong jQuery có thể được thực hiện mà không cần jQuery. Đây là một ví dụ điển hình về việc ủy ​​nhiệm sự kiện mà không có jQuery
Dave

5
@dave Tôi tự hỏi tại sao câu trả lời bạn chỉ ra không được liệt kê ở đây. Eli rõ ràng đã yêu cầu một giải pháp mà không cần bất kỳ plugin nào nếu có thể.
Ram Patra

377

Có một lời giải thích tốt trong tài liệu của jQuery.fn.on.

Nói ngắn gọn:

Trình xử lý sự kiện chỉ bị ràng buộc với các yếu tố hiện được chọn; chúng phải tồn tại trên trang tại thời điểm mã của bạn thực hiện cuộc gọi đến .on().

Do đó trong ví dụ sau #dataTable tbody trphải tồn tại trước khi mã được tạo.

$("#dataTable tbody tr").on("click", function(event){
    console.log($(this).text());
});

Nếu HTML mới đang được đưa vào trang, tốt nhất nên sử dụng các sự kiện được ủy quyền để đính kèm một trình xử lý sự kiện, như được mô tả tiếp theo.

Các sự kiện được ủy quyền có lợi thế là chúng có thể xử lý các sự kiện từ các phần tử hậu duệ được thêm vào tài liệu sau đó. Ví dụ: nếu bảng tồn tại, nhưng các hàng được thêm động bằng cách sử dụng mã, sau đây sẽ xử lý nó:

$("#dataTable tbody").on("click", "tr", function(event){
    console.log($(this).text());
});

Ngoài khả năng xử lý các sự kiện trên các phần tử con cháu chưa được tạo, một lợi thế khác của các sự kiện được ủy quyền là tiềm năng của chúng cho chi phí thấp hơn nhiều khi phải theo dõi nhiều yếu tố. Trên bảng dữ liệu có 1.000 hàng tbody, ví dụ mã đầu tiên gắn trình xử lý với 1.000 phần tử.

Một cách tiếp cận sự kiện được ủy nhiệm (ví dụ mã thứ hai) đính kèm một trình xử lý sự kiện chỉ với một yếu tố tbody, và sự kiện chỉ cần tạo ra một cấp độ (từ nhấp chuột trđến tbody).

Lưu ý: Các sự kiện được ủy nhiệm không hoạt động cho SVG .


11
đây sẽ là một câu trả lời được chấp nhận tốt hơn vì sẽ nhanh hơn khi ủy thác từ bảng cụ thể thay vì tất cả các cách từ tài liệu (khu vực tìm kiếm sẽ nhỏ hơn nhiều)
msanjay

5
@msanjay: Mặc dù nhắm mục tiêu tìm kiếm gần hơn với các yếu tố được ưa thích, nhưng sự khác biệt tìm kiếm / tốc độ là rất nhỏ trong thực tế. Bạn sẽ phải nhấp 50.000 lần một giây để nhận thấy bất cứ điều gì :)
Mã hóa

2
Cách tiếp cận này có thể được áp dụng để xử lý các lần nhấp vào hộp kiểm bằng cách thay đổi trthành một bộ chọn thích hợp, như 'input[type=checkbox]', và điều này sẽ được xử lý tự động cho các hàng mới được chèn?
JoeBrockhaus

1
Cảm ơn bạn! Điều này giải quyết vấn đề của tôi. Câu lệnh đơn giản này là vấn đề của tôi, "Trình xử lý sự kiện chỉ bị ràng buộc với các yếu tố hiện được chọn; chúng phải tồn tại trên trang tại thời điểm mã của bạn thực hiện cuộc gọi đến ..."
Dennis Fazekas

216

Đây là một giải pháp JavaScript thuần túy mà không cần bất kỳ thư viện hoặc plugin nào:

document.addEventListener('click', function (e) {
    if (hasClass(e.target, 'bu')) {
        // .bu clicked
        // Do your thing
    } else if (hasClass(e.target, 'test')) {
        // .test clicked
        // Do your other thing
    }
}, false);

nơi hasClass

function hasClass(elem, className) {
    return elem.className.split(' ').indexOf(className) > -1;
}

Live demo

Tín dụng cho Dave và Sime Vidas

Sử dụng JS hiện đại hơn, hasClasscó thể được triển khai như sau:

function hasClass(elem, className) {
    return elem.classList.contains(className);
}


6
Bạn có thể sử dụng Element. ClassList thay vì chia tách
Eugen Konkov

2
@EugenKonkov Element.classListkhông được hỗ trợ trên các trình duyệt cũ hơn. Ví dụ: IE <9
Ram Patra

2
Một bài viết tốt đẹp về cách để có được những điều thực hiện sử dụng vani kịch bản thay vì jQuery - toddmotto.com/...
Ram Patra

2
Làm thế nào về bong bóng? Điều gì xảy ra nếu sự kiện nhấp chuột xảy ra trên một phần tử con mà bạn quan tâm?
Andreas Trantidis

73

Bạn có thể thêm các sự kiện vào các đối tượng khi bạn tạo chúng. Nếu bạn đang thêm cùng một sự kiện vào nhiều đối tượng vào các thời điểm khác nhau, việc tạo một hàm được đặt tên có thể là cách để thực hiện.

var mouseOverHandler = function() {
    // Do stuff
};
var mouseOutHandler = function () {
    // Do stuff
};

$(function() {
    // On the document load, apply to existing elements
    $('select').hover(mouseOverHandler, mouseOutHandler);
});

// This next part would be in the callback from your Ajax call
$("<select></select>")
    .append( /* Your <option>s */ )
    .hover(mouseOverHandler, mouseOutHandler)
    .appendTo( /* Wherever you need the select box */ )
;

49

Bạn chỉ có thể gói cuộc gọi ràng buộc sự kiện của mình vào một hàm và sau đó gọi nó hai lần: một lần trên tài liệu đã sẵn sàng và một lần sau sự kiện của bạn có thêm các thành phần DOM mới. Nếu bạn làm điều đó, bạn sẽ muốn tránh ràng buộc cùng một sự kiện hai lần với các yếu tố hiện có, do đó bạn sẽ cần hủy liên kết các sự kiện hiện có hoặc (tốt hơn) chỉ liên kết với các yếu tố DOM mới được tạo. Mã sẽ trông giống như thế này:

function addCallbacks(eles){
    eles.hover(function(){alert("gotcha!")});
}

$(document).ready(function(){
    addCallbacks($(".myEles"))
});

// ... add elements ...
addCallbacks($(".myNewElements"))

2
Bài đăng này thực sự đã giúp tôi nắm bắt được một vấn đề tôi đang tải cùng một hình thức và nhận được 1,2,4,8,16 ... bài nộp. Thay vì sử dụng .live () tôi chỉ sử dụng .bind () trong cuộc gọi lại .load () của tôi. Vấn đề được giải quyết. Cảm ơn!
Thomas McCabe

42

Cố gắng sử dụng .live()thay vì .bind(); những .live()sẽ ràng buộc .hoverđể hộp kiểm của bạn sau khi yêu cầu thực thi Ajax.


32
Phương pháp live()này không được chấp nhận trong phiên bản 1.7 có lợi onvà bị xóa trong phiên bản 1.9.
chridam

27

Liên kết sự kiện trên các yếu tố được tạo động

Phần tử đơn:

$(document.body).on('click','.element', function(e) {  });

Yếu tố trẻ em:

 $(document.body).on('click','.element *', function(e) {  });

Thông báo thêm *. Một sự kiện sẽ được kích hoạt cho tất cả trẻ em của yếu tố đó.

Tôi đã nhận thấy rằng:

$(document.body).on('click','.#element_id > element', function(e) {  });

Nó không hoạt động nữa, nhưng nó đã hoạt động trước đây. Tôi đã sử dụng jQuery từ Google CDN , nhưng tôi không biết liệu họ có thay đổi nó không.


Yeap và họ không nói (document.body) nó nói rằng tổ tiên có thể có khá nhiều thứ
MadeInDreams

25

Bạn có thể sử dụng phương thức live () để liên kết các phần tử (ngay cả những phần mới được tạo) với các sự kiện và trình xử lý, như sự kiện onclick.

Đây là một mã mẫu mà tôi đã viết, nơi bạn có thể thấy phương thức live () liên kết các phần tử được chọn, ngay cả những phần mới được tạo, với các sự kiện:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Untitled Document</title>
    </head>

    <body>
        <script src="http://code.jquery.com/jquery-latest.js"></script>
        <script src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.16/jquery-ui.min.js"></script>

        <input type="button" id="theButton" value="Click" />
        <script type="text/javascript">
            $(document).ready(function()
                {
                    $('.FOO').live("click", function (){alert("It Works!")});
                    var $dialog = $('<div></div>').html('<div id="container"><input type ="button" id="CUSTOM" value="click"/>This dialog will show every time!</div>').dialog({
                                                                                                         autoOpen: false,
                                                                                                         tite: 'Basic Dialog'
                                                                                                     });
                    $('#theButton').click(function()
                    {
                        $dialog.dialog('open');
                        return('false');
                    });
                    $('#CUSTOM').click(function(){
                        //$('#container').append('<input type="button" value="clickmee" class="FOO" /></br>');
                        var button = document.createElement("input");
                        button.setAttribute('class','FOO');
                        button.setAttribute('type','button');
                        button.setAttribute('value','CLICKMEE');
                        $('#container').append(button);
                    });
                    /* $('#FOO').click(function(){
                                                     alert("It Works!");
                                                 }); */
            });
        </script>
    </body>
</html>

23

Tôi thích sử dụng bộ chọn và tôi áp dụng nó trên tài liệu.

Điều này liên kết chính nó trên tài liệu và sẽ được áp dụng cho các yếu tố sẽ được hiển thị sau khi tải trang.

Ví dụ:

$(document).on("click", 'selector', function() {
    // Your code here
});

2
bộ chọn không nên được bao quanh bởi $, do đó định dạng đúng sẽ là $ (document) .on ("click", "selector", function () {// Mã của bạn ở đây});
lái tự động

1
Việc bao bọc một đối tượng jQuery xung quanh selectorbiến đó là vô nghĩa , khi nó phải chứa một chuỗi hoặc đối tượng Element mà bạn có thể chuyển trực tiếp đến đối số đóon()
Rory McCrossan

20

Một giải pháp khác là thêm người nghe khi tạo phần tử. Thay vì đặt người nghe vào phần thân, bạn đặt người nghe vào phần tử trong thời điểm bạn tạo nó:

var myElement = $('<button/>', {
    text: 'Go to Google!'
});

myElement.bind( 'click', goToGoogle);
myElement.append('body');


function goToGoogle(event){
    window.location.replace("http://www.google.com");
}

Mã của bạn chứa 1 lỗi: myElement.append('body');phải myElement.appendTo('body');. Mặt khác, nếu không cần sử dụng thêm biến myElementthì dễ dàng và ngắn hơn theo cách này:$('body').append($('<button/>', { text: 'Go to Google!' }).bind( 'click', goToGoogle));
ddlab

17

Hãy thử như thế này -

$(document).on( 'click', '.click-activity', function () { ... });

16

Lưu ý lớp "MAIN", phần tử được đặt, ví dụ,

<div class="container">
     <ul class="select">
         <li> First</li>
         <li>Second</li>
    </ul>
</div>

Trong kịch bản trên, đối tượng MAIN mà jQuery sẽ xem là "vùng chứa".

Sau đó, bạn sẽ cơ bản có những yếu tố tên dưới container như ul, liselect:

$(document).ready(function(e) {
    $('.container').on( 'click',".select", function(e) {
        alert("CLICKED");
    });
 });

15

Điều này được thực hiện bởi đoàn sự kiện . Sự kiện sẽ liên kết trên phần tử lớp trình bao bọc nhưng sẽ được ủy quyền cho phần tử lớp chọn. Đây là cách nó hoạt động.

$('.wrapper-class').on("click", '.selector-class', function() {
    // Your code here
});

Ghi chú:

yếu tố lớp bọc có thể là bất cứ điều gì cũ. tài liệu, cơ thể hoặc bọc của bạn. Wrapper nên đã tồn tại . Tuy nhiên, selectorkhông nhất thiết phải được trình bày tại thời điểm tải trang. Nó có thể đến sau và sự kiện sẽ liên kết selector mà không thất bại .


14

bạn đã có thể sử dụng

$('.buttons').on('click', 'button', function(){
    // your magic goes here
});

hoặc là

$('.buttons').delegate('button', 'click', function() {
    // your magic goes here
});

hai phương thức này tương đương nhưng có thứ tự tham số khác nhau.

xem: Sự kiện đại biểu jQuery


2
delegate()bây giờ không được dùng nữa Đừng sử dụng nó.
Rory McCrossan

14

Bạn có thể đính kèm sự kiện vào phần tử khi được tạo động bằng cách sử dụng jQuery(html, attributes).

Kể từ jQuery 1.8 , bất kỳ phương thức cá thể jQuery nào (một phương thức jQuery.fn) có thể được sử dụng làm thuộc tính của đối tượng được truyền cho tham số thứ hai:

function handleDynamicElementEvent(event) {
  console.log(event.type, this.value)
}
// create and attach event to dynamic element
jQuery("<select>", {
    html: $.map(Array(3), function(_, index) {
      return new Option(index, index)
    }),
    on: {
      change: handleDynamicElementEvent
    }
  })
  .appendTo("body");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>


11

Đây là lý do tại sao các yếu tố được tạo động không đáp ứng với các nhấp chuột:

var body = $("body");
var btns = $("button");
var btnB = $("<button>B</button>");
// `<button>B</button>` is not yet in the document.
// Thus, `$("button")` gives `[<button>A</button>]`.
// Only `<button>A</button>` gets a click listener.
btns.on("click", function () {
  console.log(this);
});
// Too late for `<button>B</button>`...
body.append(btnB);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

Như một giải pháp thay thế, bạn phải lắng nghe tất cả các lần nhấp và kiểm tra phần tử nguồn:

var body = $("body");
var btnB = $("<button>B</button>");
var btnC = $("<button>C</button>");
// Listen to all clicks and
// check if the source element
// is a `<button></button>`.
body.on("click", function (ev) {
  if ($(ev.target).is("button")) {
    console.log(ev.target);
  }
});
// Now you can add any number
// of `<button></button>`.
body.append(btnB);
body.append(btnC);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

Điều này được gọi là "Đoàn sự kiện". Tin tốt, đó là một tính năng dựng sẵn trong jQuery :-)

var i = 11;
var body = $("body");
body.on("click", "button", function () {
  var letter = (i++).toString(36).toUpperCase();
  body.append($("<button>" + letter + "</button>"));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>


10

Bất kỳ pent nào tồn tại tại thời điểm sự kiện bị ràng buộc và nếu trang của bạn tự động tạo các phần tử bằng nút tên lớp, bạn sẽ liên kết sự kiện với cha mẹ đã tồn tại

$(document).ready(function(){
  //Particular Parent chield click
  $(".buttons").on("click","button",function(){
    alert("Clicked");
  });  
  
  //Dynamic event bind on button class  
  $(document).on("click",".button",function(){
    alert("Dymamic Clicked");
  });
  $("input").addClass("button");  
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="buttons">
  <input type="button" value="1">
  <button>2</button>
  <input type="text">
  <button>3</button>  
  <input type="button" value="5">  
  </div>
<button>6</button>


7

Liên kết sự kiện với cha mẹ đã tồn tại:

$(document).on("click", "selector", function() {
    // Your code here
});

5

Sử dụng .on()phương pháp của jQuery http://api.jquery.com/on/ để đính kèm các trình xử lý sự kiện vào phần tử trực tiếp.

Cũng như phiên bản 1.9 .live()phương thức được loại bỏ.


3

Tôi thích có các trình lắng nghe sự kiện được triển khai theo kiểu mô đun hơn là tạo kịch bản cho documenttrình nghe sự kiện. Vì vậy, tôi làm như dưới đây. Lưu ý, bạn không thể đăng ký quá mức một yếu tố với cùng một người nghe sự kiện, vì vậy đừng lo lắng về việc gắn một người nghe nhiều lần - chỉ một gậy.

var iterations = 4;
var button;
var body = document.querySelector("body");

for (var i = 0; i < iterations; i++) {
    button = document.createElement("button");
    button.classList.add("my-button");
    button.appendChild(document.createTextNode(i));
    button.addEventListener("click", myButtonWasClicked);
    body.appendChild(button);
}

function myButtonWasClicked(e) {
    console.log(e.target); //access to this specific button
}


Tôi thích thực hiện này; Tôi chỉ cần thiết lập một cuộc gọi lại
William

3

Một giải pháp linh hoạt khác để tạo các phần tử và liên kết các sự kiện ( nguồn )

// creating a dynamic element (container div)
var $div = $("<div>", {id: 'myid1', class: 'myclass'});

//creating a dynamic button
 var $btn = $("<button>", { type: 'button', text: 'Click me', class: 'btn' });

// binding the event
 $btn.click(function () { //for mouseover--> $btn.on('mouseover', function () {
    console.log('clicked');
 });

// append dynamic button to the dynamic container
$div.append($btn);

// add the dynamically created element(s) to a static element
$("#box").append($div);

Lưu ý: Điều này sẽ tạo một phiên bản xử lý sự kiện cho từng thành phần (có thể ảnh hưởng đến hiệu suất khi được sử dụng trong các vòng lặp)


0
<html>
    <head>
        <title>HTML Document</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
    </head>

    <body>
        <div id="hover-id">
            Hello World
        </div>

        <script>
            jQuery(document).ready(function($){
                $(document).on('mouseover', '#hover-id', function(){
                    $(this).css('color','yellowgreen');
                });

                $(document).on('mouseout', '#hover-id', function(){
                    $(this).css('color','black');
                });
            });
        </script>
    </body>
</html>

3
Mặc dù đoạn mã này có thể giải quyết vấn đề, nhưng nó không giải thích tại sao hoặc làm thế nào để trả lời câu hỏi. Vui lòng bao gồm một lời giải thích cho mã của bạn, vì điều đó thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
Palec

0

Tôi đã tìm kiếm một giải pháp để có được $.bind$.unbindlàm việc mà không gặp vấn đề gì trong các yếu tố được thêm động.

Như trên () thực hiện thủ thuật để đính kèm các sự kiện, để tạo ra một liên kết giả mạo trên những người tôi đã đến:

const sendAction = function(e){ ... }
// bind the click
$('body').on('click', 'button.send', sendAction );

// unbind the click
$('body').on('click', 'button.send', function(){} );

Liên kết không hoạt động, điều này chỉ đơn giản là thêm một sự kiện khác chỉ đến một chức năng trống ...
Fabian Bigler
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.