jQuery-Funktion nach .append

89

Wie rufe ich eine Funktion auf, nachdem jQuery .appendvollständig abgeschlossen ist?

Hier ist ein Beispiel:

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

Das Problem: Wenn eine komplexe DOM-Struktur angehängt wird, ist die Berechnung der neuen Größe des Stammelements innerhalb des Rückrufs der Anhängefunktion falsch. Es wird davon ausgegangen, dass das DOM noch nicht vollständig geladen ist, sondern nur das erste untergeordnete Element des hinzugefügten komplexen DOM.

David Horák
quelle
Sie sollten feine Verkettungsereignisse sein; append()dauert sehr wenig Zeit.
Bojangles
Siehe stackoverflow.com/q/8840580/176140 (dom 'reflow' in Webkit-Browsern)
schellmax
Dies könnte hilfreich sein: stackoverflow.com/a/1489987/1375015
Konstantin Schubert

Antworten:

71

Sie haben hier viele gültige Antworten, aber keine davon sagt Ihnen wirklich, warum es so funktioniert.

In JavaScript werden Befehle einzeln synchron in der Reihenfolge ausgeführt, in der sie kommen, es sei denn, Sie weisen sie ausdrücklich an, mithilfe eines Timeouts oder Intervalls asynchron zu sein.

Dies bedeutet, dass Ihre .appendMethode ausgeführt wird und nichts anderes (ohne Berücksichtigung möglicher Zeitüberschreitungen oder Intervalle) ausgeführt wird, bis diese Methode ihren Job beendet hat.

Zusammenfassend ist kein Rückruf erforderlich, da er .appendsynchron ausgeführt wird.

Mekwall
quelle
39
Dies ist alles wahr, aber hier ist mein Problem: Ich füge ein komplexes DOM hinzu und berechne direkt nach dem Anhängen die neue Größe des Stammelements, aber hier habe ich eine falsche Zahl. Und denken Sie, das Problem ist folgendes: DOM in noch nicht vollständig geladen, nur erstes Kind (.append ("<div id =" child "> <div id =" subChild_with_SIZE "> </ div> </ div>"))
David Horák
@ JinDave, auf welche Größe beziehen Sie sich? Ist es die Anzahl der Kinder, die Größe der Eltern oder was müssen Sie berechnen?
Mekwall
1
@ marcus-ekwall jsfiddle.net/brszE/3 es ist nur Schreibmethode, kein Arbeitscode ,
David Horák
22
@ DavidHorák also warum ist das die akzeptierte Antwort? Es ist wahr, dass der Code, obwohl die Änderung stattfindet, nicht blockiert wird, bis der vollständige Reflow ziemlich überrascht ist, dass er 32 Up- Votes hat! (Oder es verursacht nicht einmal einen Reflow) so etwas wie die Antworten [hier] ( stackoverflow.com/questions/). 8840580 /… ) könnte erzrelevant sein.
Andreas
17
Ich stimme Andreas zu, diese Antwort ist nicht richtig. Das Problem ist, dass das Element, obwohl das Anhängen zurückgegeben wurde, möglicherweise immer noch nicht im DOM verfügbar ist (browserabhängig, funktioniert in einigen Browsern in anderen). Die Verwendung eines Timeouts garantiert immer noch nicht, dass sich das Element im DOM befindet.
Severun
19

Nun, ich habe genau das gleiche Problem mit der Größenberechnung und nach stundenlangen Kopfschmerzen muss ich nicht damit einverstanden sein, mich .append()streng synchron zu verhalten. Na zumindest in Google Chrome. Siehe folgenden Code.

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

Die Eigenschaft padding-left wird in Firefox korrekt abgerufen, ist jedoch in Chrome leer. Wie alle anderen CSS-Eigenschaften nehme ich an. Nach einigen Experimenten musste ich setTimeout()mich damit zufrieden geben, den CSS-Getter mit einer Verzögerung von 10 ms einzupacken, von der ich weiß, dass sie höllisch hässlich ist, aber die einzige, die in Chrome funktioniert. Wenn einer von Ihnen eine Idee hätte, wie man dieses Problem besser lösen könnte, wäre ich sehr dankbar.

Womi
quelle
15
das hat mir VIEL geholfen: stackoverflow.com/q/8840580/176140 - was bedeutet, append () ist synchron, aber das DOM-Update ist nicht
schellmax
In diesem Fall ist setTimeout nicht hässlich (nur die 10 ms sind). Jedes Mal, wenn Sie eine "dom" -Manipulation anfordern, wartet Chrome, bis Ihr Code beendet ist, bevor er ausgeführt wird. Wenn Sie also item.hide gefolgt von item.show aufrufen, wird einfach nichts unternommen. Wenn Ihre ausführende Funktion beendet ist, werden Ihre Änderungen auf das DOM angewendet. Wenn Sie auf ein "dom" -Update warten müssen, bevor Sie fortfahren, führen Sie einfach Ihre CSS / DOM-Aktion aus und rufen Sie dann settimeout (function () {was als nächstes zu tun ist}) auf. Das Settimeout verhält sich wie ein guter alter "Doevents" in vb6! Es weist den Browser an, seine Warteaufgaben auszuführen (DOM aktualisieren) und kehrt danach zu Ihrem Code zurück.
Foxontherock
Sehen Sie sich die Antwort an, die eine .ready()viel bessere und elegantere Lösung verwendet.
Mark Carpenter Jr
18

Obwohl Marcus Ekwall in Bezug auf die Synchronität des Anhängens absolut Recht hat, habe ich auch festgestellt, dass in seltsamen Situationen das DOM manchmal nicht vollständig vom Browser gerendert wird, wenn die nächste Codezeile ausgeführt wird.

In diesem Szenario sind die Shadowdiver-Lösungen - mit .ready - in der richtigen Richtung. Es ist jedoch viel aufgeräumter, den Aufruf an Ihren ursprünglichen Anhang zu ketten.

$('#root')
  .append(html)
  .ready(function () {
    // enter code here
  });
Daniel - SDS Group
quelle
Dies ist in jeder Situation absolut falsch und nutzlos. api.jquery.com/ready
Kevin B
Hallo Kevin, auf welche Weise?
Daniel - SDS Group
Es hat absolut keinen Vorteil, .ready zu verwenden, um auf die angehängten Elemente zu warten, um mehr zu „rendern“, als wenn Sie einfach ein Settimeout verwenden würden, wenn und nur wenn das Dokument noch nicht fertig ist. Wenn das Dokument bereit ist, wird der Code synchron ausgeführt, sodass keine nützlichen Auswirkungen auftreten.
Kevin B
Ich glaube nicht, dass das Ready auf das Laden des angehängten Dokuments wartet, sondern auf das Laden des gesamten DOM. Es ist ungefähr 3 Jahre her, seit ich diese Antwort geschrieben habe, aber ich glaube, ich habe den Schattentaucher oben mehr kommentiert, hatte aber zu diesem Zeitpunkt keine Möglichkeit, Kommentare abzugeben.
Daniel - SDS Group
1
Eigentlich funktioniert das besser als erwartet. Besser als jede andere Antwort hier.
Blau
8

Ich bin überrascht über alle Antworten hier ...

Versuche dies:

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

Beachten Sie das 0-Timeout. Es ist keine willkürliche Zahl ... wie ich verstehe (obwohl mein Verständnis etwas wackelig sein mag), gibt es zwei Javascript-Ereigniswarteschlangen - eine für Makroereignisse und eine für Mikroereignisse. Die Warteschlange mit "größerem" Gültigkeitsbereich enthält Aufgaben, die die Benutzeroberfläche (und das DOM) aktualisieren, während die Mikrowarteschlange Operationen vom Typ "Schnelle Aufgaben" ausführt.

Beachten Sie auch, dass das Festlegen eines Zeitlimits nicht garantiert, dass der Code genau den angegebenen Wert aufweist. Dadurch wird die Funktion im Wesentlichen in die höhere Warteschlange (die die Benutzeroberfläche / das DOM verwaltet) gestellt und nicht vor der angegebenen Zeit ausgeführt.

Dies bedeutet, dass durch das Festlegen eines Zeitlimits von 0 es in den UI / DOM-Teil der Ereigniswarteschlange von Javascript verschoben wird, um bei der nächstmöglichen Gelegenheit ausgeführt zu werden.

Dies bedeutet, dass das DOM mit allen vorherigen Warteschlangenelementen (z. B. eingefügt über) aktualisiert wird $.append(...); , und wenn Ihr Code ausgeführt wird, ist das DOM vollständig verfügbar.

(ps - Ich habe dies von Secrects of the JavaScript Ninja gelernt - ein ausgezeichnetes Buch: https://www.manning.com/books/secrets-of-the-javascript-ninja )

jleach
quelle
Ich habe jahrelang hinzugefügt, setTimeout()ohne zu wissen warum. Danke für die Erklärung!
Am
5

Ich habe eine andere Variante, die für jemanden nützlich sein kann:

$('<img src="http://example.com/someresource.jpg">').load(function() {
    $('#login').submit();
}).appendTo("body");
msangel
quelle
1
Für diejenigen, die denken, dass dies veraltet und entfernt ist, ist es richtig, aber Sie können es trotzdem wie ...on('load', function(){ ... hier verwenden: stackoverflow.com/a/37751413/2008111
caramba
5

Ich bin auf dasselbe Problem gestoßen und habe eine einfache Lösung gefunden. Fügen Sie nach dem Aufrufen der Append-Funktion ein bereites Dokument hinzu.

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

Shadowdiver
quelle
Nett. Das hat bei mir funktioniert (ich habe HTML mit Handlebars und JSON und natürlich einem regulären Dokument angehängt. Dafür passiert das schon zu früh).
Katharine Osborne
1
@KatharineOsborne Dies unterscheidet sich nicht von einem "regulären document.ready". document.ready wird nur in einem bestimmten Szenario aufgerufen. Wenn Sie es anders verwenden, funktioniert es nicht anders.
Kevin B
Nun, es ist über ein Jahr her, seit ich den Kommentar hinterlassen habe, aber IIRC, der Kontext, in dem Sie dies nennen, ist wichtig (dh in einer Funktion). Der Code wurde inzwischen an einen Kunden übergeben, sodass ich keinen Zugriff mehr darauf habe, um ihn erneut zu untersuchen.
Katharine Osborne
5

Die Verwendung MutationObserverkann wie ein Rückruf für die jQuery- appendMethode wirken:

Ich habe es in einer anderen Frage erklärt , und dieses Mal werde ich nur ein Beispiel für moderne Browser geben:

// 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
quelle
+1. Von allen Antworten passt Ihre am besten. In meinem Fall hat es jedoch nicht funktioniert, einer Tabelle verschiedene Zeilen hinzuzufügen, da es in eine Endlosschleife geht. Das Plugin, das die Tabelle sortiert, ändert das DOM so, dass der Beobachter unendlich oft aufgerufen wird.
Azevedo
@Azevedo - warum sollte es unendlich sein? Ich bin sicher, es ist endlich. Auf jeden Fall können Sie eine Sortierfunktion von einem hinzugefügten Zeilen- "Ereignis" trennen, indem Sie ein bisschen kreativ sind. Es gibt Wege.
vsync
Wenn das Sortier-Plugin die Tabelle sortiert, wird der Beobachter ausgelöst (dom geändert), wodurch die Sortierung erneut ausgelöst wird. Jetzt denke ich ... Ich könnte die Tabellenzeilen zählen und den Beobachter dazu bringen, den Sortierer nur dann aufzurufen, wenn der Zeilenzähler geändert wurde.
Azevedo
3

Ich denke, dies ist gut beantwortet, aber dies ist etwas spezifischer für die Behandlung von "subChild_with_SIZE" (wenn dies vom übergeordneten Element kommt, aber Sie können es von dort aus anpassen, wo es sich befindet).

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

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

Renatoluna
quelle
8
$.whenfunktioniert nur für diejenigen Objekte, deren Rückgabe ein Versprechen Objekt
Rifat
Es funktioniert, aber es wird ein Fehler auf der Konsole
angezeigt
1
Dies funktioniert nur, weil Sie anotherMethod()sofort aufrufen . Es wird nicht als Rückruf übergeben.
Yts
das macht keinen Sinn.
Kevin B
1

Die Jquery-Append-Funktion gibt ein jQuery-Objekt zurück, sodass Sie am Ende einfach eine Methode markieren können

$("#root").append(child).anotherJqueryMethod();
Dve
quelle
Ich muss mich benutzerdefinierte Javascript-Funktion nach .append nennen, ich muss die Größe der Wurzel neu berechnen
David Horák
Sie können die jQuery-Methode für jede Methode verwenden, aber append ist eine synchrone Methode. Sie sollten also in Ordnung sein, um in der nächsten Zeile alles zu tun, was Sie brauchen.
Dve
@ JinDave, was genau müssen Sie neu berechnen? Anzahl der Kinder, Größe der Eltern oder was bedeutet Größe ?
Mekwall
0

Für Bilder und andere Quellen können Sie Folgendes verwenden:

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

Am saubersten ist es, es Schritt für Schritt zu tun. Verwenden Sie jede Funktion, um durch jedes Element zu iterieren. Sobald dieses Element angehängt ist, übergeben Sie es an eine nachfolgende Funktion, um dieses Element zu verarbeiten.

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

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

Dieses Problem ist beim Codieren von HTML5 für mobile Geräte aufgetreten. Einige Browser- / Gerätekombinationen verursachten Fehler, da die Methode .append () die Änderungen im DOM nicht sofort widerspiegelte (was dazu führte, dass der JS fehlschlug).

Eine schnelle und schmutzige Lösung für diese Situation war:

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
quelle
Intervalle sollten niemals verwendet werden, um auf den Abschluss eines Codekonstruktors zu warten. Es ist völlig unzuverlässig.
Gabe Hiemstra
-1

Ja, Sie können jeder DOM-Einfügung eine Rückruffunktion hinzufügen :
$myDiv.append( function(index_myDiv, HTML_myDiv){ //.... return child })

Überprüfen Sie die JQuery-Dokumentation: http://api.jquery.com/append/
Und hier ist ein praktisches, ähnliches Beispiel: http://www.w3schools.com/jquery/ tryit.asp? filename = tryjquery_html_prepend_func

Pedro Ferreira
quelle
Wenn Sie am Beispiel von w3schools herumspielen, können Sie den zweiten Parameter überprüfen und die .prepend()Methode durch Folgendes ersetzen : $ ("p"). Prepend (Funktion (n, pHTML) {return "<b> Dieses p-Element </ b> (\" <i > "+ pHTML +" </ i> \ ") <b> hat den Index" + n + "</ b>."
Pedro Ferreira
1
Sie verstehen den Zweck dieses Beispiels eindeutig nicht. Es handelt sich nicht um einen Rückruf, der besagt, dass der Inhalt angehängt wurde, sondern um eine Rückruffunktion, die zurückgibt, was angehängt werden soll.
vsync
@vsync: genau. Du hast meine Antwort offensichtlich nicht verstanden. „Kind“ ist , was angehängt wurde und nicht , wenn es wurde angehängt.
Pedro Ferreira
-1

Ich bin kürzlich auf ein ähnliches Problem gestoßen. Die Lösung für mich war, die Sammlung neu zu erstellen. Lassen Sie mich versuchen zu demonstrieren:

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

Hoffe das hilft!

kralyk
quelle
Es hat mir nicht geholfen. :(
Pal
-1

In jquery können Sie direkt nach dem Anhängen verwenden

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

$ () ruft eine Funktion auf, die entweder einen DOM-fähigen Rückruf registriert (wenn eine Funktion an sie übergeben wird) oder Elemente aus dem DOM zurückgibt (wenn eine Auswahlzeichenfolge oder ein Element an sie übergeben wird).

Weitere
Unterschiede zwischen $ und $ () finden Sie hier in jQuery
http://api.jquery.com/ready/

AJchandu
quelle
-2

Ich weiß, dass es nicht die beste Lösung ist, aber die beste Vorgehensweise:

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

setTimeout(function(){
  // Action after append
},100);
Manvel
quelle
8
Ich denke nicht, dass dies eine gute Praxis ist. 100 ist eine beliebige Zahl und ein Ereignis kann länger dauern.
JasTonAChair
1
Falscher Weg. Es könnte nicht immer 100ms dauern
axelvnk
Siehe meine Antwort hier für eine Erklärung, warum dies funktioniert (die Zahl könnte / sollte 0 sein, nicht 100): stackoverflow.com/questions/6068955/…
jleach
1
Es ist zumindest besser als die Antworten hier mit .ready.
Kevin B
-4
$('#root').append(child);
// do your work here

Anhängen hat keine Rückrufe, und dies ist Code, der synchron ausgeführt wird - es besteht kein Risiko, dass er NICHT ausgeführt wird

David Fells
quelle
3
Ich sehe zu viele Kommentare wie diese und sie sind nicht hilfreich. @david fällt, ja, das JS ist synchron, aber das DOM braucht Zeit zum Aktualisieren. Wenn Sie Ihre angehängten DOM-Elemente bearbeiten müssen, die Elemente jedoch noch nicht in Ihrem DOM gerendert wurden, funktioniert Ihre neue Bearbeitung NICHT, selbst wenn das Anhängen abgeschlossen ist. (Zum Beispiel: $ ('# element'). On ().
NoobishPro
-4
$('#root').append(child).anotherMethod();
Vivek
quelle
1
Ich muss mich Javascript-Funktion nennen, nicht jQuery-Funktion
David Horák