Mengikat acara pada elemen yang dibuat secara dinamis?

1677

Saya memiliki sedikit kode di mana saya mengulangi semua kotak pilih pada halaman dan mengikat suatu .hoverperistiwa kepada mereka untuk melakukan sedikit twiddling dengan lebar mereka mouse on/off.

Ini terjadi pada halaman siap dan berfungsi dengan baik.

Masalah yang saya miliki adalah bahwa setiap kotak pilih yang saya tambahkan melalui Ajax atau DOM setelah loop awal tidak akan memiliki acara terikat.

Saya telah menemukan plugin ini ( jQuery Live Query Plugin ), tetapi sebelum saya menambahkan 5k ke halaman saya dengan sebuah plugin, saya ingin melihat apakah ada yang tahu cara untuk melakukan ini, baik dengan jQuery secara langsung atau dengan opsi lain.

Eli
sumber

Jawaban:

2250

Pada jQuery 1.7 Anda harus menggunakan jQuery.fn.on:

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

Sebelum ini , pendekatan yang disarankan adalah menggunakan live():

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

Namun, live()tidak digunakan lagi di 1.7 dan mendukung on(), dan sepenuhnya dihapus di 1.9. Tanda live()tangan:

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

... dapat diganti dengan on()tanda tangan berikut :

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

Misalnya, jika halaman Anda secara dinamis membuat elemen dengan nama kelas dosomethingAnda akan mengikat acara ke orangtua yang sudah ada (ini adalah inti dari masalah di sini, Anda perlu sesuatu yang ada untuk diikat, jangan mengikat ke konten dinamis), ini bisa (dan opsi paling mudah) adalah document. Meskipun diingat documentmungkin bukan pilihan yang paling efisien .

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

Setiap orang tua yang ada pada saat acara terikat baik-baik saja. Sebagai contoh

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

akan berlaku untuk

<div class="buttons">
    <!-- <button>s that are generated dynamically and added here -->
</div>
Popnoodles
sumber
7
Perhatikan bahwa metode langsung hanya berfungsi untuk acara tertentu, dan bukan yang lain seperti loadedmetadata. (Lihat bagian peringatan dalam dokumentasi.)
Sam Dutton
48
Pelajari lebih lanjut tentang delegasi acara di sini: learn.jquery.com/events/event-delegation .
Felix Kling
9
Adakah cara untuk mencapai ini dengan javascript / vanilla js murni?
Ram Patra
15
@Ramswaroop apa pun yang dapat Anda lakukan di jQuery dapat diselesaikan tanpa jQuery. Berikut adalah contoh yang baik dari delegasi acara tanpa jQuery
Dave
5
@ saya ingin tahu mengapa jawaban yang Anda tunjukkan tidak tercantum di sini. Eli jelas meminta solusi tanpa plugin apa pun jika memungkinkan.
Ram Patra
377

Ada penjelasan yang bagus dalam dokumentasi jQuery.fn.on.

Pendeknya:

Penangan acara hanya terikat pada elemen yang dipilih saat ini; mereka harus ada di halaman pada saat kode Anda membuat panggilan ke .on().

Jadi dalam contoh berikut #dataTable tbody trharus ada sebelum kode dihasilkan.

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

Jika HTML baru sedang disuntikkan ke halaman, lebih baik menggunakan acara yang didelegasikan untuk melampirkan pengendali acara, seperti yang dijelaskan selanjutnya.

Peristiwa yang didelegasikan memiliki keuntungan bahwa mereka dapat memproses peristiwa dari elemen turunan yang ditambahkan ke dokumen di lain waktu. Misalnya, jika tabel ada, tetapi baris ditambahkan secara dinamis menggunakan kode, berikut ini akan menanganinya:

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

Selain kemampuan mereka untuk menangani acara pada elemen turunan yang belum dibuat, keuntungan lain dari acara yang didelegasikan adalah potensi mereka untuk overhead yang jauh lebih rendah ketika banyak elemen harus dipantau. Pada tabel data dengan 1.000 baris di dalamnya tbody, contoh kode pertama menempel handler ke 1.000 elemen.

Pendekatan peristiwa yang didelegasikan (contoh kode kedua) melampirkan pengendali acara ke hanya satu elemen tbody, dan, dan acara hanya perlu menggembungkan satu tingkat (dari yang diklik trke tbody).

Catatan: Acara yang didelegasikan tidak berfungsi untuk SVG .

Ronen Rabinovici
sumber
11
ini akan menjadi jawaban yang diterima lebih baik karena akan lebih cepat untuk mendelegasikan dari tabel tertentu daripada jauh-jauh dari dokumen (area pencarian akan jauh lebih kecil)
msanjay
5
@msanjay: Meskipun menargetkan pencarian lebih dekat ke elemen lebih disukai, perbedaan pencarian / kecepatan sangat kecil dalam praktiknya. Anda harus mengklik 50.000 kali per detik untuk melihat apa pun :)
Hilang Coding
2
Dapatkah pendekatan ini diterapkan untuk menangani klik kotak centang dalam satu baris dengan mengubah trke pemilih yang sesuai, seperti 'input[type=checkbox]', dan ini akan ditangani secara otomatis untuk baris yang baru dimasukkan?
JoeBrockhaus
1
Terima kasih! Ini menyelesaikan masalah saya. Pernyataan sederhana ini adalah masalah saya, "Penangan acara terikat hanya pada elemen yang dipilih saat ini; mereka harus ada pada halaman saat kode Anda membuat panggilan ke ..."
Dennis Fazekas
216

Ini adalah solusi JavaScript murni tanpa perpustakaan atau plugin:

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

di mana hasClassadalah

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

Live demo

Penghargaan untuk Dave dan Sime Vidas

Menggunakan JS yang lebih modern, hasClassdapat diimplementasikan sebagai:

function hasClass(elem, className) {
    return elem.classList.contains(className);
}
Ram Patra
sumber
6
Anda dapat menggunakan Element.classList alih-alih membelah
Eugen Konkov
2
@EugenKonkov Element.classListtidak didukung didukung pada peramban yang lebih lama. Misalnya, IE <9.
Ram Patra
2
Sebuah artikel yang bagus tentang bagaimana menyelesaikan sesuatu menggunakan skrip vanilla alih-alih jQuery - toddmotto.com/...
Ram Patra
2
bagaimana kalau menggelegak? Bagaimana jika peristiwa klik terjadi pada anak dari elemen yang Anda minati?
Andreas Trantidis
73

Anda dapat menambahkan acara ke objek saat Anda membuatnya. Jika Anda menambahkan acara yang sama ke beberapa objek pada waktu yang berbeda, membuat fungsi bernama mungkin merupakan cara yang harus dilakukan.

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 */ )
;
nickf
sumber
49

Anda bisa dengan mudah membungkus panggilan acara mengikat Anda ke dalam suatu fungsi dan kemudian memanggilnya dua kali: sekali pada dokumen siap dan sekali setelah acara Anda yang menambahkan elemen DOM baru. Jika Anda melakukannya, Anda ingin menghindari mengikat acara yang sama dua kali pada elemen yang ada sehingga Anda harus melepaskan ikatan peristiwa yang ada atau (lebih baik) hanya mengikat ke elemen DOM yang baru dibuat. Kode akan terlihat seperti ini:

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

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

// ... add elements ...
addCallbacks($(".myNewElements"))
Greg Borenstein
sumber
2
Posting ini benar-benar membantu saya memahami masalah yang saya alami memuat formulir yang sama dan mendapatkan 1,2,4,8,16 ... kiriman. Alih-alih menggunakan .live () saya hanya menggunakan .bind () di callback .load () saya. Masalah terpecahkan. Terima kasih!
Thomas McCabe
42

Cobalah untuk menggunakannya .live()daripada .bind(); yang .live()akan mengikat .hoverke kotak centang Anda setelah permintaan mengeksekusi Ajax.

pengguna670265
sumber
32
Metode live()ini ditinggalkan dalam versi 1.7 yang mendukung ondan dihapus dalam versi 1.9.
chridam
27

Mengikat acara pada elemen yang dibuat secara dinamis

Elemen tunggal:

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

Elemen anak:

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

Perhatikan yang ditambahkan *. Suatu peristiwa akan dipicu untuk semua anak dari elemen itu.

Saya perhatikan bahwa:

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

Itu tidak berfungsi lagi, tetapi berhasil sebelumnya. Saya telah menggunakan jQuery dari Google CDN , tetapi saya tidak tahu apakah mereka mengubahnya.

MadeInDreams
sumber
Yeap dan mereka tidak mengatakan (dokumen.barang) yang mengatakan leluhur yang bisa menjadi apa saja
MadeInDreams
25

Anda dapat menggunakan metode live () untuk mengikat elemen (bahkan yang baru dibuat) ke acara dan penangan, seperti acara onclick.

Berikut adalah contoh kode yang saya tulis, di mana Anda dapat melihat bagaimana metode live () mengikat elemen yang dipilih, bahkan yang baru dibuat, ke acara:

<!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>
Fazi
sumber
23

Saya lebih suka menggunakan pemilih dan saya menerapkannya pada dokumen.

Ini mengikat dirinya pada dokumen dan akan berlaku untuk elemen yang akan diberikan setelah pemuatan halaman.

Sebagai contoh:

$(document).on("click", 'selector', function() {
    // Your code here
});
Vatsal
sumber
2
pemilih tidak boleh dilampirkan oleh $, sehingga format yang benar adalah $ (dokumen) .on ("klik", "pemilih", fungsi () {// Kode Anda di sini});
autopilot
1
Juga tidak ada gunanya untuk membungkus objek jQuery di sekitar selectorvariabel, ketika itu harus berisi string atau objek Elemen yang Anda bisa langsung lewati ke argumenon()
Rory McCrossan
20

Solusi lain adalah menambahkan pendengar saat membuat elemen. Alih-alih memasukkan pendengar ke dalam tubuh, Anda menempatkan pendengar dalam elemen pada saat Anda membuatnya:

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

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


function goToGoogle(event){
    window.location.replace("http://www.google.com");
}
Martin Da Rosa
sumber
Kode Anda mengandung 1 kesalahan: myElement.append('body');harus myElement.appendTo('body');. Di sisi lain, jika tidak perlu untuk penggunaan lebih lanjut dari variabel myElementlebih mudah dan lebih pendek dengan cara ini:$('body').append($('<button/>', { text: 'Go to Google!' }).bind( 'click', goToGoogle));
ddlab
17

Coba seperti ini -

$(document).on( 'click', '.click-activity', function () { ... });
Rohit Suthar
sumber
16

Perhatikan kelas "MAIN" elemen yang ditempatkan, misalnya,

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

Dalam skenario di atas, objek UTAMA yang akan ditonton jQuery adalah "wadah".

Kemudian Anda pada dasarnya akan memiliki nama elemen di bawah wadah seperti ul, li, dan select:

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

Ini dilakukan oleh delegasi acara . Acara akan mengikat elemen kelas pembungkus tetapi akan didelegasikan ke elemen kelas pemilih. Begini Cara kerjanya.

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

catatan:

elemen kelas wrapper bisa apa saja ex. dokumen, tubuh atau bungkus Anda. Wrapper seharusnya sudah ada . Namun, selectortidak perlu disajikan pada waktu pemuatan halaman. Itu mungkin datang kemudian dan acara akan mengikatselector tanpa gagal .

Mustkeem K
sumber
14

Anda bisa menggunakannya

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

atau

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

kedua metode ini setara tetapi memiliki urutan parameter yang berbeda.

lihat: Acara Delegasi jQuery

Mensur Grišević
sumber
2
delegate()sekarang sudah ditinggalkan. Jangan gunakan itu.
Rory McCrossan
14

Anda dapat melampirkan acara ke elemen saat dibuat secara dinamis menggunakan jQuery(html, attributes).

Pada jQuery 1.8 , setiap metode instance jQuery (metode jQuery.fn) dapat digunakan sebagai properti objek yang diteruskan ke parameter kedua:

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>

guest271314
sumber
11

Inilah mengapa elemen yang dibuat secara dinamis tidak merespons klik:

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>

Sebagai solusinya, Anda harus mendengarkan semua klik dan memeriksa elemen sumber:

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>

Ini disebut "Delegasi Acara". Berita bagus, ini fitur bawaan di 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>

daun
sumber
10

Setiap p arent yang ada pada saat acara terikat dan jika halaman Anda secara dinamis membuat elemen dengan tombol nama kelas Anda akan mengikat acara ke induk yang sudah ada

$(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>

Ankit Kathiriya
sumber
7

Bind event ke induk yang sudah ada:

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

Gunakan .on()metode jQuery http://api.jquery.com/on/ untuk melampirkan penangan acara ke elemen langsung.

Juga pada .live()metode versi 1.9 dihapus.

Kalpesh Patel
sumber
3

Saya lebih suka memiliki pendengar acara yang ditempatkan dalam mode fungsi modular daripada membuat skrip documentpendengar acara tingkat. Jadi, saya suka di bawah ini. Catatan, Anda tidak bisa terlalu banyak berlangganan elemen dengan pendengar acara yang sama jadi jangan khawatir melampirkan pendengar lebih dari sekali - hanya satu batang.

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
}

Ronnie Royston
sumber
Saya lebih suka implementasi ini; Saya hanya perlu menelepon kembali
William
3

Solusi fleksibel lain untuk membuat elemen dan mengikat acara ( sumber )

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

Catatan: Ini akan membuat instance handler event untuk setiap elemen (dapat memengaruhi kinerja saat digunakan dalam loop)

Prasad De Silva
sumber
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>
Fakhrul Hasan
sumber
3
Meskipun potongan kode ini dapat menyelesaikan masalah, itu tidak menjelaskan mengapa atau bagaimana ia menjawab pertanyaan. Harap sertakan penjelasan untuk kode Anda, karena itu sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda.
Palec
0

Saya sedang mencari solusi untuk mendapatkan $.binddan$.unbind bekerja tanpa masalah dalam elemen yang ditambahkan secara dinamis.

Seperti pada () buat trik untuk melampirkan acara, untuk membuat ikatan yang tidak mengikat pada yang saya datangi:

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

// unbind the click
$('body').on('click', 'button.send', function(){} );
Evhz
sumber
Tidak mengikat tidak berfungsi, ini hanya menambah acara lain yang menunjuk ke fungsi kosong ...
Fabian Bigler