fungsi jQuery setelah .append

92

Bagaimana cara memanggil fungsi setelah jQuery .appendselesai?

Berikut contohnya:

$("#root").append(child, function(){
   // Action after append is completly done
});

Masalahnya: Saat struktur DOM yang kompleks ditambahkan, perhitungan ukuran baru dari elemen root dalam callback fungsi penambahan menjadi salah. Asumsinya adalah bahwa DOM masih belum dimuat sepenuhnya, hanya anak pertama dari DOM kompleks yang ditambahkan yang.

David Horák
sumber
Anda harus menjadi acara perangkaian yang bagus; append()hanya membutuhkan sedikit waktu.
Bojangles
lihat stackoverflow.com/q/8840580/176140 (dom 'reflow' di browser webkit)
schellmax
Ini mungkin berguna: stackoverflow.com/a/1489987/1375015
Konstantin Schubert

Jawaban:

71

Anda memiliki banyak jawaban yang valid di sini, tetapi tidak ada yang benar-benar memberi tahu Anda mengapa ini berfungsi seperti itu.

Dalam JavaScript, perintah dijalankan satu per satu, secara sinkron sesuai urutannya, kecuali Anda secara eksplisit memberi tahu mereka untuk menjadi asinkron dengan menggunakan waktu tunggu atau interval.

Ini berarti bahwa .appendmetode Anda akan dieksekusi dan tidak ada yang lain (dengan mengabaikan potensi waktu tunggu atau interval yang mungkin ada) yang akan dijalankan hingga metode tersebut menyelesaikan tugasnya.

Untuk meringkas, tidak perlu callback karena .appendakan dijalankan secara sinkron.

mekwall.dll
sumber
40
Ini semua benar, tetapi inilah masalah saya: Saya menambahkan DOM kompleks, dan tepat setelah menambahkan, saya menghitung ukuran baru elemen root, tetapi di sini saya mendapat nomor yang salah. Dan pikirkan bahwa, masalahnya adalah ini: DOM masih belum dimuat sepenuhnya, hanya anak pertama (.append ("<div id =" child "> <div id =" subChild_with_SIZE "> </div> </div>"))
David Horák
@JinDave, Apa ukuran yang Anda maksud ini? Apakah jumlah anak, tinggi orang tua atau berapa yang perlu Anda hitung?
mekwall
1
@ marcus-ekwall jsfiddle.net/brszE/3 itu hanya metode penulisan, bukan kode yang berfungsi,
David Horák
22
@ DavidHorák jadi mengapa ini jawaban yang diterima? Memang benar bahwa meskipun perubahan terjadi, kode tidak akan memblokir hingga alur ulang lengkap cukup terkejut, ini memiliki 32 suara positif! (Atau bahkan saya tidak menyebabkan perubahan suara) sesuatu seperti jawaban [di sini] ( stackoverflow.com/questions/ 8840580 /… ) bisa jadi relevan.
Andreas
17
Saya setuju dengan Andreas, jawaban ini tidak benar. Masalahnya adalah meskipun append telah dikembalikan, elemen tersebut mungkin masih tidak tersedia di DOM (tergantung browser, berfungsi di beberapa jeda browser di browser lain). Menggunakan waktu tunggu tetap tidak menjamin bahwa elemen tersebut akan berada di DOM.
Severun
21

Meskipun Marcus Ekwall benar tentang sinkronisitas append, saya juga menemukan bahwa dalam situasi yang aneh terkadang DOM tidak dirender sepenuhnya oleh browser saat baris kode berikutnya dijalankan.

Dalam skenario ini, solusi shadowdiver berada di jalur yang benar - dengan menggunakan .ready - namun jauh lebih rapi untuk merangkai panggilan ke lampiran asli Anda.

$('#root')
  .append(html)
  .ready(function () {
    // enter code here
  });
Daniel - Grup SDS
sumber
1
Ini benar-benar salah dan tidak berguna dalam setiap situasi. api.jquery.com/ready
Kevin B
Hai Kevin, dengan cara apa?
Daniel - Grup SDS
sama sekali tidak ada manfaatnya menggunakan .ready untuk menunggu elemen yang ditambahkan untuk "merender" lebih banyak daripada yang akan Anda dapatkan dari hanya menggunakan settimeout, jika dan hanya jika dokumen belum siap. jika dokumen sudah siap, kode akan dijalankan secara sinkron, sehingga tidak memiliki dampak yang berguna.
Kevin B
Saya tidak berpikir siap akan menunggu dokumen yang ditambahkan dimuat, melainkan menunggu seluruh DOM dimuat. Sudah sekitar 3 tahun sejak saya menulis jawaban ini tetapi saya rasa saya lebih banyak mengomentari shadow diver di atas, tetapi tidak memiliki fasilitas untuk berkomentar saat itu.
Daniel - Grup SDS
1
Sebenarnya, ini bekerja lebih baik dari yang diharapkan. Lebih baik dari jawaban lain di sini.
Biru
20

Saya punya masalah yang persis sama dengan penghitungan ulang ukuran dan setelah berjam-jam sakit kepala, saya harus tidak setuju dengan .append()berperilaku sangat sinkron. Setidaknya di Google Chrome. Lihat kode berikut.

var input = $( '<input />' );
input.append( arbitraryElement );
input.css( 'padding-left' );

Properti padding-left diambil dengan benar di Firefox tapi kosong di Chrome. Seperti semua properti CSS lainnya, saya kira. Setelah beberapa percobaan saya harus puas membungkus 'getter' CSS setTimeout()dengan penundaan 10 ms yang saya tahu JELEK sekali tapi satu-satunya yang bekerja di Chrome. Jika ada di antara Anda yang memiliki ide bagaimana mengatasi masalah ini dengan lebih baik, saya akan sangat berterima kasih.

Womi
sumber
15
ini membantu saya BANYAK: stackoverflow.com/q/8840580/176140 - artinya, append () IS sinkron, tetapi pembaruan DOM tidak
schellmax
Dalam hal ini, setTimeout tidak jelek (hanya 10 md). Setiap kali Anda meminta manipulasi "dom", chrome akan menunggu kode Anda berakhir sebelum menjalankannya. Jadi, jika Anda memanggil item.hide diikuti oleh item.show, itu tidak akan melakukan apa-apa. Saat fungsi eksekusi Anda selesai, perubahan Anda akan diterapkan ke DOM. Jika Anda perlu menunggu pembaruan "dom" sebelum melanjutkan, cukup lakukan tindakan CSS / DOM Anda, lalu panggil settimeout (function () {what to do next}) ;. Settimeout bertindak seperti "doevents" tua yang baik di vb6! Ini memberi tahu browser untuk melakukan tugas menunggu (memperbarui DOM), dan kembali ke kode Anda setelahnya.
foxontherock
Lihat jawaban yang memanfaatkan .ready()solusi yang jauh lebih baik dan lebih elegan.
Mark Carpenter Jr
8

Saya terkejut dengan semua jawaban di sini ...

Coba ini:

window.setTimeout(function() { /* your stuff */ }, 0);

Catat 0 batas waktu. Ini bukan angka yang sewenang-wenang ... seperti yang saya mengerti (meskipun pemahaman saya mungkin agak goyah), ada dua antrian acara javascript - satu untuk acara makro dan satu untuk acara mikro. Antrean cakupan "lebih besar" menampung tugas-tugas yang memperbarui UI (dan DOM), sedangkan antrian mikro melakukan operasi jenis tugas cepat.

Sadari juga bahwa menyetel waktu tunggu tidak menjamin bahwa kode bekerja tepat pada nilai yang ditentukan. Apa yang dilakukan ini pada dasarnya adalah menempatkan fungsi ke antrean yang lebih tinggi (yang menangani UI / DOM), dan tidak menjalankannya sebelum waktu yang ditentukan.

Ini berarti bahwa menyetel waktu tunggu 0 akan menempatkannya di bagian UI / DOM dari antrean peristiwa javascript, untuk dijalankan pada kesempatan berikutnya yang memungkinkan.

Ini berarti DOM diperbarui dengan semua item antrian sebelumnya (seperti dimasukkan lewat $.append(...);, dan saat kode Anda berjalan, DOM tersedia sepenuhnya.

(ps - Saya mempelajarinya dari Secrects of the JavaScript Ninja - sebuah buku yang sangat bagus: https://www.manning.com/books/secrets-of-the-javascript-ninja )

jleach
sumber
Saya telah menambahkan setTimeout()selama bertahun-tahun tanpa mengetahui alasannya. Terima kasih untuk penjelasannya!
steampowered
5

Saya memiliki varian lain yang mungkin berguna untuk seseorang:

$('<img src="http://example.com/someresource.jpg">').load(function() {
    $('#login').submit();
}).appendTo("body");
msangel
sumber
1
Bagi mereka yang berpikir ini usang dan dihapus, itu benar tetapi Anda masih dapat menggunakannya seperti ...on('load', function(){ ... lihat di sini: stackoverflow.com/a/37751413/2008111
caramba
5

Saya menemukan masalah yang sama dan telah menemukan solusi sederhana. Tambahkan setelah memanggil fungsi append dokumen siap.

$("#root").append(child);
$(document).ready(function () {
    // Action after append is completly done
});

shadowdiver
sumber
Bagus. Ini berhasil untuk saya (saya menambahkan HTML menggunakan Handlebars dan JSON dan tentu saja dokumen biasa. Sudah terjadi terlalu dini untuk semua itu).
Katharine Osborne
1
@KarineOsborne Ini tidak berbeda dengan "document.ready" biasa. document.ready hanya dipanggil dalam satu skenario tertentu, menggunakannya secara berbeda tidak membuatnya berfungsi secara berbeda.
Kevin B
Sudah lebih dari setahun sejak saya meninggalkan komentar, tetapi IIRC, konteks di mana Anda menyebutnya penting (yaitu dalam suatu fungsi). Kode tersebut telah diteruskan ke klien jadi saya tidak memiliki akses ke sana lagi untuk menggali kembali.
Katharine Osborne
5

Menggunakan MutationObservercan bertindak seperti callback untuk appendmetode jQuery :

Saya sudah menjelaskannya di pertanyaan lain , dan kali ini saya hanya akan memberikan contoh untuk browser modern:

// Somewhere in your app:
var observeDOM = (() => {
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

    return function(obj, callback){
        if( MutationObserver ){
            // define a new observer
            var obs = new MutationObserver(function(mutations, observer){
                if( mutations[0].addedNodes.length || mutations[0].removedNodes.length )
                    callback(mutations);
            });
            // have the observer observe foo for changes in children
            obs.observe( obj, { childList:true, subtree:true });

            return obs;
        }
    }
})();

//////////////////
// Your code:

// setup the DOM observer (on the appended content's parent) before appending anything
observeDOM( document.body, ()=>{
    // something was added/removed
}).disconnect(); // don't listen to any more changes

// append something
$('body').append('<p>foo</p>');
vsync
sumber
+1. Dari semua jawaban Anda yang paling cocok. Namun itu tidak berhasil dalam kasus saya, menambahkan berbagai baris ke tabel, karena itu masuk ke loop tak terbatas. Plugin yang mengurutkan tabel akan mengubah DOM sehingga akan memanggil pengamat dalam waktu tak terbatas.
Azevedo
@ Azevedo - mengapa itu tidak terbatas? saya yakin itu terbatas. bagaimanapun, Anda dapat memisahkan fungsionalitas penyortiran dari baris "peristiwa" yang ditambahkan dengan menjadi sedikit kreatif. ada cara.
vsync
Ketika plugin sortir mengurutkan tabel, ia memicu pengamat (dom berubah), yang akan memicu pengurutan lagi. Sekarang saya berpikir ... Saya dapat menghitung baris tabel dan membuat pengamat memanggil penyortir hanya jika penghitung baris diubah.
Azevedo
3

Saya pikir ini dijawab dengan baik tetapi ini sedikit lebih spesifik untuk menangani "subChild_with_SIZE" (jika itu berasal dari induk, tetapi Anda dapat menyesuaikannya dari tempatnya)

$("#root").append(
    $('<div />',{
        'id': 'child'
    })
)
.children()
.last()
.each(function() {
    $(this).append(
        $('<div />',{
            'id': $(this).parent().width()
        })
    );
});
James Jones
sumber
2

$.when($('#root').append(child)).then(anotherMethod());

renatoluna
sumber
8
$.whenhanya bekerja untuk objek yang mengembalikan objek janji
Rifat
itu berfungsi tetapi memberikan kesalahan pada konsol .. "maka bukan fungsi"
Karan Datwani
1
Ini hanya berfungsi karena Anda anotherMethod()langsung memanggil .. Ini tidak diteruskan sebagai panggilan balik.
yts
ini tidak masuk akal.
Kevin B
1

fungsi penambahan Jquery mengembalikan objek jQuery sehingga Anda bisa memberi tag pada metode di akhir

$("#root").append(child).anotherJqueryMethod();
Dve
sumber
Saya perlu memanggil saya fungsi javascript khusus setelah .append, saya perlu menghitung ulang ukuran root
David Horák
Anda dapat menggunakan jQuery setiap metode, tetapi menambahkan adalah metode sinkron sehingga Anda akan baik-baik saja untuk melakukan apa pun yang Anda butuhkan di baris berikutnya.
Dve
@JinDave, tepatnya apa yang perlu Anda hitung ulang? Jumlah anak, tinggi orang tua, atau apa artinya ukuran ?
mekwall
0

Untuk gambar dan sumber lain, Anda dapat menggunakan itu:

$(el).one('load', function(){
    // completed
}).each(function() {
    if (this.complete)
        $(this).load();
});
Mycelin
sumber
0

Cara terbersih adalah melakukannya selangkah demi selangkah. Gunakan setiap fungsi untuk mengulang setiap elemen. Segera setelah elemen itu ditambahkan, teruskan ke fungsi selanjutnya untuk memproses elemen itu.

    function processAppended(el){
        //process appended element
    }

    var remove = '<a href="#">remove</a>' ;
    $('li').each(function(){
        $(this).append(remove);   
        processAppended(this);    
    });​
hitwill
sumber
-1

Saya mengalami masalah ini saat mengkode HTML5 untuk perangkat seluler. Beberapa kombinasi browser / perangkat menyebabkan kesalahan karena metode .append () tidak mencerminkan perubahan di DOM secara langsung (menyebabkan JS gagal).

Solusi cepat dan kotor untuk situasi ini adalah:

var appint = setInterval(function(){
    if ( $('#foobar').length > 0 ) {
        //now you can be sure append is ready
        //$('#foobar').remove(); if elem is just for checking
        //clearInterval(appint)
    }
}, 100);
$(body).append('<div>...</div><div id="foobar"></div>');
Pyry Liukas
sumber
Interval tidak boleh digunakan untuk menunggu konstruktor kode selesai. Ini sama sekali tidak bisa diandalkan.
Gabe Hiemstra
-1

Ya, Anda dapat menambahkan fungsi panggilan balik ke setiap penyisipan DOM:
$myDiv.append( function(index_myDiv, HTML_myDiv){ //.... return child })

Periksa dokumentasi JQuery: http://api.jquery.com/append/
Dan inilah contoh praktis dan serupa: http://www.w3schools.com/jquery/ tryit.asp? filename = tryjquery_html_prepend_func

Pedro Ferreira
sumber
Mengotak-atik contoh w3schools, Anda dapat memeriksa parameter ke-2, mengganti .prepend()metode dengan: $ ("p"). Prepend (function (n, pHTML) {return "<b> Elemen p ini </b> (\" <i > "+ pHTML +" </i> \ ") <b> memiliki indeks" + n + "</b>.";
Pedro Ferreira
1
Anda jelas tidak memahami tujuan dari contoh itu..itu bukan panggilan balik yang mengatakan bahwa konten telah ditambahkan, melainkan fungsi panggilan balik yang mengembalikan apa yang akan ditambahkan.
vsync
@vsync: tepat. Anda jelas tidak mengerti jawaban saya. "anak" adalah apa yang telah ditambahkan dan bukan ketika ditambahkan.
Pedro Ferreira
-1

Saya mengalami masalah serupa baru-baru ini. Solusi bagi saya adalah membuat ulang colletion. Izinkan saya mencoba menunjukkan:

var $element = $(selector);
$element.append(content);

// This Doesn't work, because $element still contains old structure:
$element.fooBar();    

// This should work: the collection is re-created with the new content:
$(selector).fooBar();

Semoga ini membantu!

kralyk.dll
sumber
Itu tidak membantu saya. :(
Pal
-1

Di jquery, Anda bisa menggunakan setelah menambahkan

$(function(){
//code that needs to be executed when DOM is ready, after manipulation
});

$ () memanggil fungsi yang mendaftarkan callback siap DOM (jika fungsi diteruskan ke sana) atau mengembalikan elemen dari DOM (jika string atau elemen pemilih diteruskan ke sana)

Anda dapat menemukan lebih banyak di sini
perbedaan antara $ dan $ () di jQuery
http://api.jquery.com/ready/

AJchandu
sumber
-2

Saya tahu ini bukan solusi terbaik, tetapi praktik terbaik:

$("#root").append(child);

setTimeout(function(){
  // Action after append
},100);
Manvel
sumber
8
Saya rasa ini bukan praktik yang baik. 100 adalah angka arbitrer, dan sebuah acara bisa memakan waktu lebih lama.
JasTonAChair
1
Salah jalan. Mungkin tidak selalu memakan waktu 100ms
axelvnk
Lihat jawaban saya di sini untuk penjelasan mengapa ini berhasil (jumlahnya bisa / harus 0, bukan 100): stackoverflow.com/questions/6068955/…
jleach
1
setidaknya ini adalah praktik yang lebih baik daripada jawaban di sini menggunakan .ready.
Kevin B
-4
$('#root').append(child);
// do your work here

Append tidak memiliki callback, dan ini adalah kode yang dijalankan secara sinkron - tidak ada risiko TIDAK dilakukan

David Fells
sumber
3
Saya melihat terlalu banyak komentar seperti ini, dan itu tidak membantu. @david fells, ya, JS IS sinkron, tetapi DOM perlu waktu untuk memperbarui. Jika Anda perlu memanipulasi elemen yang ditambahkan DOM Anda tetapi elemen tersebut belum dirender ke DOM Anda, bahkan jika penambahan selesai, manipulasi baru Anda TIDAK akan berfungsi. (Misalnya: $ ('# element']. On ().
NoobishPro
-4
$('#root').append(child).anotherMethod();
Vivek
sumber
1
Saya perlu memanggil saya fungsi javascript, bukan fungsi jQuery
David Horák