Funzione jQuery dopo .append


90

Come chiamare una funzione dopo jQuery .append è stato completamente fatto?

Ecco un esempio:

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

Problema: quando viene aggiunta una struttura DOM complessa, il calcolo della nuova dimensione dell'elemento radice all'interno del callback della funzione di aggiunta è sbagliato. I presupposti sono che il DOM non è ancora completamente caricato, solo il primo figlio del DOM complesso aggiunto lo è.


Dovresti essere bravo a concatenare gli eventi; append()richiede pochissimo tempo.
Bojangles

vedi stackoverflow.com/q/8840580/176140 (dom 'reflow' sui browser
webkit

Risposte:


70

Hai molte risposte valide qui, ma nessuna di esse ti dice davvero perché funziona come funziona.

In JavaScript i comandi vengono eseguiti uno alla volta, in modo sincrono nell'ordine in cui vengono, a meno che non si dica esplicitamente che siano asincroni utilizzando un timeout o un intervallo.

Ciò significa che il tuo .appendmetodo verrà eseguito e nient'altro (a prescindere da eventuali timeout o intervalli che potrebbero esistere) verrà eseguito fino a quando il metodo non avrà terminato il suo lavoro.

Per riassumere, non è necessario un callback poiché .appendverrà eseguito in modo sincrono.


39
Questo è tutto vero, ma ecco il mio problema: aggiungo DOM complesso e subito dopo l'aggiunta, calcolo la nuova dimensione dell'elemento radice, ma qui ho sbagliato numero. E pensa che il problema è questo: DOM non è ancora completamente caricato, solo il primo figlio (.append ("<div id =" child "> <div id =" subChild_with_SIZE "> </div> </div>"))
David Horák

@JinDave, a che taglia ti riferisci? È la quantità di figli, l'altezza del genitore o cosa devi calcolare?
mekwall

1
@ marcus-ekwall jsfiddle.net/brszE/3 è solo un metodo di scrittura, non funziona il codice,
David Horák,

22
@ DavidHorák quindi perché questa è la risposta accettata? È vero che anche se la modifica avviene il codice non si bloccherà fino a quando il reflow completo non avrà abbastanza sorpreso questo ha 32 voti positivi! (O non è nemmeno causa di reflow) qualcosa come le risposte [qui] ( stackoverflow.com/questions/ 8840580 /… ) potrebbe essere rilevante.
Andreas

17
Sono d'accordo con Andreas, questa risposta non è corretta. Il problema è che anche se append è tornato, l'elemento potrebbe non essere ancora disponibile nel DOM (dipendente dal browser, funziona in alcuni browser interruzioni in altri). L'uso di un timeout non garantisce ancora che l'elemento sarà nel DOM.
Severun

19

Beh, ho esattamente lo stesso problema con il ricalcolo delle dimensioni e dopo ore di mal di testa devo essere in disaccordo con il .append()comportamento strettamente sincrono. Almeno in Google Chrome. Vedere il codice seguente.

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

La proprietà padding-left viene recuperata correttamente in Firefox ma è vuota in Chrome. Come tutte le altre proprietà CSS, suppongo. Dopo alcuni esperimenti ho dovuto accontentarmi di avvolgere il "getter" CSS setTimeout()con un ritardo di 10 ms che so essere BRUTTO da morire ma l'unico che funziona in Chrome. Se qualcuno di voi avesse un'idea di come risolvere meglio questo problema, sarei molto grato.


15
questo mi ha aiutato molto: stackoverflow.com/q/8840580/176140 - significato, append () è sincrono, ma l'aggiornamento DOM non è
schellmax

In tal caso, setTimeout non è brutto (solo i 10 ms lo sono). Ogni volta che richiedi la manipolazione "dom", chrome aspetterà la fine del codice prima di eseguirlo. Quindi, se chiami item.hide seguito da item.show, non farà nulla. Quando la funzione in esecuzione è terminata, applica le modifiche al DOM. Se devi attendere un aggiornamento "dom" prima di continuare, esegui semplicemente l'azione CSS / DOM, quindi chiama settimeout (function () {what to do next}) ;. Il settimeout si comporta come un buon vecchio "doevents" in vb6! Dice al browser di svolgere le sue attività di attesa (aggiornamento DOM) e torna al codice dopo.
foxontherock

Vedi la risposta che fa uso di .ready()una soluzione molto migliore e più elegante.
Mark Carpenter Jr

19

Sebbene Marcus Ekwall abbia assolutamente ragione sulla sincronicità di append, ho anche scoperto che in situazioni strane a volte il DOM non viene visualizzato completamente dal browser quando viene eseguita la riga di codice successiva.

In questo scenario, le soluzioni shadowdiver seguono le linee corrette, con l'utilizzo di .ready, tuttavia è molto più ordinato concatenare la chiamata all'append originale.

$('#root')
  .append(html)
  .ready(function () {
    // enter code here
  });

Questo è assolutamente falso e inutile in ogni situazione. api.jquery.com/ready
Kevin B

Ciao Kevin, in che modo?
Daniel - SDS Group,

non c'è assolutamente alcun vantaggio nell'usare .ready per attendere gli elementi aggiunti per "renderizzare" più di quanto si otterrebbe usando semplicemente un settimeout, se e solo se il documento non è già pronto. se il documento è pronto, il codice verrà eseguito in modo sincrono, senza alcun impatto utile.
Kevin B

Non penso che il pronto aspetterà il caricamento del documento aggiunto, ma invece attenderà il caricamento dell'intero DOM. Sono passati circa 3 anni da quando ho scritto questa risposta, ma penso che stavo commentando di più su shadow diver sopra, ma non avevo la possibilità di commentare in quel momento.
Daniel - SDS Group

1
In realtà, funziona meglio del previsto. Meglio di qualsiasi altra risposta qui.
Blu

8

Sono sorpreso di tutte le risposte qui ...

Prova questo:

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

Notare il timeout 0. Non è un numero arbitrario ... a quanto ho capito (anche se la mia comprensione potrebbe essere un po 'traballante), ci sono due code di eventi javascript: una per gli eventi macro e una per i micro eventi. La coda con ambito "più grande" contiene attività che aggiornano l'interfaccia utente (e DOM), mentre la coda micro esegue operazioni di tipo attività rapida.

Renditi conto anche che l'impostazione di un timeout non garantisce che il codice funzioni esattamente al valore specificato. Ciò che fa è essenzialmente mette la funzione nella coda più alta (quella che gestisce l'interfaccia utente / DOM) e non la esegue prima del tempo specificato.

Ciò significa che l'impostazione di un timeout di 0 lo inserisce nella porzione UI / DOM della coda degli eventi di javascript, da eseguire alla prossima possibilità possibile.

Ciò significa che il DOM viene aggiornato con tutti gli elementi della coda precedenti (come quelli inseriti tramite $.append(...);, e quando il codice viene eseguito, il DOM è completamente disponibile.

(ps - l'ho imparato da Secrects of the JavaScript Ninja - un libro eccellente: https://www.manning.com/books/secrets-of-the-javascript-ninja )


Aggiungo setTimeout()da anni senza sapere perché. Grazie per la spiegazione!
potenza a vapore il

5

Ho un'altra variante che può essere utile per qualcuno:

$('<img src="http://example.com/someresource.jpg">').load(function() {
    $('#login').submit();
}).appendTo("body");

1
Per quelli che pensano questo è deprecato e rimosso, è giusto, ma è comunque possibile utilizzarlo come ...on('load', function(){ ... vedi qui: stackoverflow.com/a/37751413/2008111
Caramba

5

Mi sono imbattuto nello stesso problema e ho trovato una soluzione semplice. Aggiungi dopo aver chiamato la funzione di accodamento un documento pronto.

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


Bello. Questo ha funzionato per me (stavo aggiungendo HTML usando Handlebars e JSON e, naturalmente, un normale documento. Accade già troppo presto per tutto questo).
Katharine Osborne

1
@KatharineOsborne Questo non è diverso da un "normale document.ready". document.ready viene chiamato solo in uno scenario specifico, utilizzarlo in modo diverso non lo fa funzionare in modo diverso.
Kevin B

È passato più di un anno da quando ho lasciato il commento, ma IIRC, il contesto in cui lo chiami è importante (cioè in una funzione). Da allora il codice è stato passato a un client, quindi non ho più accesso ad esso per scavare di nuovo.
Katharine Osborne

5

L'utilizzo MutationObserverpuò agire come un callback per il appendmetodo jQuery :

L'ho spiegato in un'altra domanda e questa volta darò solo un esempio per i browser moderni:

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

+1. Di tutte le risposte la tua si adatta meglio. Tuttavia non ha funzionato nel mio caso, aggiungendo varie righe a una tabella, perché entra in un ciclo infinito. Il plugin che ordina la tabella cambierà il DOM così chiamerà l'osservatore infinite volte.
Azevedo

@ Azevedo - perché dovrebbe essere infinito? sono sicuro che sia finito. in ogni caso, puoi separare una funzionalità di ordinamento da un "evento" di riga aggiunto essendo un po 'creativo. ci sono modi.
vsync

Quando il plug-in di ordinamento ordina la tabella, attiva l'osservatore (dom modificato), che attiverà nuovamente l'ordinamento. Ora sto pensando ... potrei contare le righe della tabella e fare in modo che l'osservatore chiami il selezionatore solo se il contatore delle righe è stato modificato.
Azevedo

3

Penso che questa sia una buona risposta ma questo è un po 'più specifico per la gestione di "subChild_with_SIZE" (se proviene dal genitore, ma puoi adattarlo da dove potrebbe essere)

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

2

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


8
$.whenfunziona solo per quegli oggetti che restituiscono un oggetto promesso
Rifat

funziona ma dà un errore sulla console .. "allora non è una funzione"
Karan Datwani

1
Funziona solo perché lo stai invocando anotherMethod()subito .. Non viene passato come callback.
YTS

questo non ha senso.
Kevin B

1

la funzione Jquery append restituisce un oggetto jQuery in modo da poter taggare semplicemente un metodo alla fine

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

Devo chiamarmi funzione javascript personalizzata dopo .append, ho bisogno di ricalcolare la dimensione della radice
David Horák

è possibile utilizzare jQuery ogni metodo, ma append è un metodo sincrono, quindi dovresti essere a posto solo per fare tutto ciò di cui hai bisogno nella riga successiva.
Dve

@JinDave, cosa devi ricalcolare esattamente? Numero di figli, altezza del genitore o cosa significa taglia ?
mekwall

0

Per le immagini e altre fonti puoi usare quello:

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

0

Il modo più pulito è farlo passo dopo passo. Usa una funzione each per scorrere ogni elemento. Non appena quell'elemento viene aggiunto, passalo a una funzione successiva per elaborare quell'elemento.

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

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

-1

Ho riscontrato questo problema durante la codifica di HTML5 per dispositivi mobili. Alcune combinazioni browser / dispositivo hanno causato errori perché il metodo .append () non rifletteva immediatamente le modifiche nel DOM (causando il fallimento del JS).

Per soluzione rapida e sporca per questa situazione era:

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

Gli intervalli non devono mai essere utilizzati per attendere il completamento di un costruttore di codice. È totalmente inaffidabile.
Gabe Hiemstra

-1

Sì, puoi aggiungere una funzione di callback a qualsiasi inserimento DOM:
$myDiv.append( function(index_myDiv, HTML_myDiv){ //.... return child })

controlla la documentazione di JQuery: http://api.jquery.com/append/
Ed ecco un esempio pratico e simile: http://www.w3schools.com/jquery/ tryit.asp? filename = tryjquery_html_prepend_func


Giocherellando con l'esempio di w3schools, puoi controllare il secondo parametro, sostituendo il .prepend()metodo con: $ ("p"). Prepend (function (n, pHTML) {return "<b> Questo elemento p </b> (\" <i > "+ pHTML +" </i> \ ") <b> has index" + n + "</b>.";
Pedro Ferreira

1
Chiaramente non capisci lo scopo di quell'esempio ... non è un callback che dice che il contenuto è stato aggiunto, ma piuttosto una funzione di callback che restituisce ciò che deve essere aggiunto.
vsync

@vsync: eactly. Chiaramente non hai capito la mia risposta. "figlio" è ciò che è stato aggiunto e non quando è stato aggiunto.
Pedro Ferreira

-1

Di recente ho riscontrato un problema simile. La soluzione per me era ricreare la raccolta. Lasciami provare a dimostrare:

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

Spero che questo ti aiuti!


Non mi ha aiutato. :(
Pal

-1

In jquery potresti usare subito dopo il tuo append

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

$ () chiama una funzione che registra un callback pronto per DOM (se le viene passata una funzione) o restituisce elementi dal DOM (se gli viene passata una stringa di selettore o un elemento)

Puoi trovare più qui la
differenza tra $ e $ () in jQuery
http://api.jquery.com/ready/


-2

So che non è la soluzione migliore, ma la migliore pratica:

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

setTimeout(function(){
  // Action after append
},100);

8
Non credo che questa sia una buona pratica. 100 è un numero arbitrario e un evento potrebbe richiedere più tempo.
JasTonAChair

1
Modo sbagliato di procedere. Potrebbe non richiedere sempre 100 ms
axelvnk

Vedere la mia risposta qui per una spiegazione del perché questo funziona (il numero potrebbe / dovrebbe essere 0, non 100): stackoverflow.com/questions/6068955/...
jleach

1
è almeno una pratica migliore rispetto alle risposte qui usando .ready.
Kevin B

-4
$('#root').append(child);
// do your work here

Append non ha callback e questo è codice che viene eseguito in modo sincrono - non c'è rischio che NON venga eseguito


3
Vedo troppi commenti come questo e non sono utili. @david fells, sì, il JS è sincronizzato, ma il DOM ha bisogno di tempo per aggiornarsi. Se è necessario manipolare gli elementi aggiunti al DOM ma gli elementi non sono ancora stati visualizzati nel DOM, anche se l'appendice è terminata, la nuova manipolazione NON funzionerà. (Ad esempio: $ ('# element'). On ().
NoobishPro

-4
$('#root').append(child).anotherMethod();

1
Devo chiamarmi funzione javascript, non funzione jQuery
David Horák
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.