Fonction jQuery après .append

90

Comment appeler une fonction une fois jQuery .appendterminé?

Voici un exemple:

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

Le problème: lorsqu'une structure DOM complexe est ajoutée, le calcul de la nouvelle taille de l'élément racine dans le rappel de la fonction d'ajout est erroné. Les hypothèses sont que le DOM n'est toujours pas complètement chargé, seul le premier enfant du DOM complexe ajouté l'est.

David Horák
la source
Vous devriez bien enchaîner les événements; append()prend très peu de temps.
Bojangles
voir stackoverflow.com/q/8840580/176140 (dom 'reflow' sur les navigateurs
webkit
Cela pourrait être utile: stackoverflow.com/a/1489987/1375015
Konstantin Schubert

Réponses:

70

Vous avez de nombreuses réponses valides ici, mais aucune d'entre elles ne vous dit vraiment pourquoi cela fonctionne comme cela.

En JavaScript, les commandes sont exécutées une par une, de manière synchrone dans l'ordre dans lequel elles arrivent, à moins que vous ne leur disiez explicitement qu'elles sont asynchrones en utilisant un délai d'expiration ou un intervalle.

Cela signifie que votre .appendméthode sera exécutée et que rien d'autre (sans tenir compte des délais ou intervalles potentiels qui peuvent exister) ne s'exécutera jusqu'à ce que cette méthode ait terminé son travail.

Pour résumer, aucun rappel n'est nécessaire car .appendil sera exécuté de manière synchrone.

mekwall
la source
39
Tout cela est vrai, mais voici mon problème: j'ajoute un DOM complexe, et juste après l'ajout, je calcule la nouvelle taille de l'élément racine, mais ici je me suis trompé de numéro. Et pensez que, le problème est le suivant: DOM n'est toujours pas complètement chargé, seulement le premier enfant (.append ("<div id =" child "> <div id =" subChild_with_SIZE "> </div> </div>"))
David Horák
@JinDave, De quelle taille parlez-vous? Est-ce le nombre d'enfants, la taille du parent ou qu'est-ce que vous devez calculer?
mekwall
1
@ marcus-ekwall jsfiddle.net/brszE/3 c'est seulement une méthode d'écriture, pas de code fonctionnel,
David Horák
22
@ DavidHorák alors pourquoi est-ce la réponse acceptée? Il est vrai que même si le changement se produit, le code ne se bloquera pas tant que la redistribution complète n'aura pas été surprise, cela a 32 votes positifs! (Ou cela ne cause même pas de redistribution) quelque chose comme les réponses [ici] ( stackoverflow.com/questions/ 8840580 /… ) pourrait être plus pertinent.
Andreas
17
Je suis d'accord avec Andreas, cette réponse n'est pas correcte. Le problème est que même si append est retourné, l'élément peut toujours ne pas être disponible dans le DOM (dépend du navigateur, fonctionne dans certains navigateurs, se brise dans d'autres). L'utilisation d'un délai d'expiration ne garantit toujours pas que l'élément sera dans le DOM.
Severun
19

Eh bien, j'ai exactement le même problème avec le recalcul de la taille et après des heures de maux de tête, je ne suis pas d'accord avec un .append()comportement strictement synchrone. Eh bien au moins dans Google Chrome. Voir le code suivant.

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

La propriété padding-left est correctement récupérée dans Firefox mais elle est vide dans Chrome. Comme toutes les autres propriétés CSS, je suppose. Après quelques expériences, j'ai dû me contenter d'envelopper le `` getter '' CSS setTimeout()avec un délai de 10 ms, ce qui, je le sais, est laid comme l'enfer, mais le seul fonctionnant dans Chrome. Si l'un de vous avait une idée de la meilleure façon de résoudre ce problème, je vous serais très reconnaissant.

Womi
la source
15
cela m'a beaucoup aidé: stackoverflow.com/q/8840580/176140 - ce qui signifie, append () EST synchrone, mais la mise à jour DOM ne l'est pas
schellmax
Dans ce cas, setTimeout n'est pas moche (seul le 10 ms l'est). Chaque fois que vous demandez une manipulation "dom", Chrome attendra la fin de votre code avant de l'exécuter. Donc, si vous appelez item.hide suivi de item.show, cela ne fera rien. Lorsque votre fonction d'exécution est terminée, alors, il applique vos modifications au DOM. Si vous devez attendre une mise à jour "dom" avant de continuer, faites simplement votre action CSS / DOM, puis appelez settimeout (function () {que faire ensuite}) ;. Le settimeout agit comme un bon vieux "doevents" en vb6! Il dit au navigateur de faire ses tâches d'attente (mise à jour du DOM), et revient à votre code après.
foxontherock
Découvrez la réponse qui utilise .ready()une solution bien meilleure et plus élégante.
Mark Carpenter Jr
19

Bien que Marcus Ekwall ait tout à fait raison sur la synchronicité de l'ajout, j'ai également constaté que dans des situations étranges, le DOM n'est parfois pas complètement rendu par le navigateur lorsque la ligne de code suivante s'exécute.

Dans ce scénario, les solutions shadowdiver vont dans le bon sens - avec l'utilisation de .ready - mais il est beaucoup plus ordonné de chaîner l'appel à votre append d'origine.

$('#root')
  .append(html)
  .ready(function () {
    // enter code here
  });
Daniel - Groupe SDS
la source
C'est absolument faux et inutile dans toutes les situations. api.jquery.com/ready
Kevin B
Salut Kevin, de quelle manière?
Daniel - SDS Group
il n'y a absolument aucun avantage à utiliser .ready to wait sur les éléments ajoutés pour «rendre» plus que ce que vous obtiendriez simplement en utilisant un settimeout, si et seulement si le document n'est pas déjà prêt. si le document EST prêt, le code sera exécuté de manière synchrone, n'ayant donc aucun impact utile.
Kevin B
Je ne pense pas que le prêt attendra le chargement du document ajouté, mais attendra plutôt que le DOM entier se charge. Cela faisait environ 3 ans que j'avais écrit cette réponse mais je pense que je commentais davantage le plongeur d'ombre ci-dessus, mais je n'avais pas la possibilité de commenter à l'époque.
Daniel - SDS Group
1
En fait, cela fonctionne mieux que prévu. Mieux que toute autre réponse ici.
Bleu
8

Je suis surpris de toutes les réponses ici ...

Essaye ça:

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

Notez le délai d'expiration 0. Ce n'est pas un nombre arbitraire ... si je comprends bien (bien que ma compréhension puisse être un peu fragile), il y a deux files d'attente d'événements javascript - une pour les événements macro et une pour les micro événements. La file d'attente à portée "plus grande" contient les tâches qui mettent à jour l'interface utilisateur (et le DOM), tandis que la micro file d'attente effectue des opérations de type tâche rapide.

Sachez également que définir un délai d'expiration ne garantit pas que le code fonctionne exactement à cette valeur spécifiée. Cela place essentiellement la fonction dans la file d'attente supérieure (celle qui gère l'interface utilisateur / DOM) et ne l'exécute pas avant l'heure spécifiée.

Cela signifie que la définition d'un délai d'expiration de 0 le place dans la partie UI / DOM de la file d'attente d'événements de javascript, pour être exécuté à la prochaine occasion.

Cela signifie que le DOM est mis à jour avec tous les éléments de file d'attente précédents (tels que l'insertion via $.append(...); , et lorsque votre code s'exécute, le DOM est entièrement disponible.

(ps - j'ai appris cela de Secrects of the JavaScript Ninja - un excellent livre: https://www.manning.com/books/secrets-of-the-javascript-ninja )

jleach
la source
J'ajoute depuis setTimeout()des années sans savoir pourquoi. Merci pour l'explication!
vapeur le
5

J'ai une autre variante qui peut être utile pour quelqu'un:

$('<img src="http://example.com/someresource.jpg">').load(function() {
    $('#login').submit();
}).appendTo("body");
msangel
la source
1
Pour ceux qui pensent que c'est obsolète et supprimé, c'est vrai mais vous pouvez toujours l'utiliser comme ...on('load', function(){ ... voir ici: stackoverflow.com/a/37751413/2008111
caramba
5

Je suis tombé sur le même problème et j'ai trouvé une solution simple. Ajoutez après avoir appelé la fonction d'ajout un document prêt.

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

shadowdiver
la source
Agréable. Cela a fonctionné pour moi (j'ajoutais du HTML à l'aide de Handlebars et JSON et bien sûr un document régulier se produit déjà trop tôt pour tout cela).
Katharine Osborne
1
@KatharineOsborne Ce n'est pas différent d'un "document standard.ready". document.ready n'est appelé que dans un scénario spécifique, l'utiliser différemment ne le fait pas fonctionner différemment.
Kevin B
Eh bien, cela fait plus d'un an que j'ai laissé le commentaire, mais IIRC, le contexte dans lequel vous appelez cela est important (c'est-à-dire dans une fonction). Le code a depuis été transmis à un client, donc je n'y ai plus accès pour creuser.
Katharine Osborne
5

L'utilisation MutationObserverpeut agir comme un rappel pour la appendméthode jQuery :

Je l'ai expliqué dans une autre question , et cette fois je ne donnerai qu'un exemple pour les navigateurs modernes:

// 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
la source
+1. De toutes les réponses, la vôtre convient le mieux. Cependant, cela n'a pas fonctionné dans mon cas, l'ajout de différentes lignes à une table, car il va dans une boucle infinie. Le plugin qui trie la table changera le DOM afin qu'il appellera l'observateur des temps infinis.
Azevedo
@Azevedo - pourquoi serait-il infini? je suis sûr que c'est fini. de toute façon, vous pouvez séparer une fonctionnalité de tri d'un "événement" de ligne ajouté en étant un peu créatif. il y a des moyens.
vsync
Lorsque le plugin de tri trie la table, il déclenche l'observateur (dom changé), qui déclenchera à nouveau le tri. Maintenant, je pense ... Je pourrais compter les lignes de la table et demander à l'observateur d'appeler le trieur uniquement si le compteur de lignes a été modifié.
Azevedo
3

Je pense que c'est bien répondu mais c'est un peu plus spécifique à la gestion du "subChild_with_SIZE" (si cela vient du parent, mais vous pouvez l'adapter d'où il peut être)

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

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

renatoluna
la source
8
$.whenne fonctionne que pour les objets dont le retour est un objet de promesse
Rifat
ça marche mais donne une erreur sur la console .. "alors n'est pas une fonction"
Karan Datwani
1
Cela ne fonctionne que parce que vous l'appelez anotherMethod()tout de suite. Il n'est pas transmis en tant que rappel.
yts
Cela n'a aucun sens.
Kevin B
1

la fonction jquery append renvoie un objet jQuery afin que vous puissiez simplement marquer une méthode à la fin

$("#root").append(child).anotherJqueryMethod();
Dve
la source
J'ai besoin de m'appeler la fonction javascript personnalisée après .append, j'ai besoin de recalculer la taille de la racine
David Horák
vous pouvez utiliser la méthode jQuery pour chaque méthode, mais append est une méthode synchrone, vous devriez donc être d'accord pour faire tout ce dont vous avez besoin sur la ligne suivante.
Dve
@JinDave, que devez-vous recalculer exactement? Nombre d'enfants, taille du parent ou que signifie la taille ?
mekwall
0

Pour les images et autres sources, vous pouvez utiliser cela:

$(el).one('load', function(){
    // completed
}).each(function() {
    if (this.complete)
        $(this).load();
});
Mycéline
la source
0

Le moyen le plus propre est de le faire étape par étape. Utilisez chaque fonction pour parcourir chaque élément. Dès que cet élément est ajouté, passez-le à une fonction suivante pour traiter cet élément.

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

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

J'ai rencontré ce problème lors du codage HTML5 pour les appareils mobiles. Certaines combinaisons navigateur / périphérique ont provoqué des erreurs car la méthode .append () ne reflétait pas immédiatement les changements dans le DOM (provoquant l'échec du JS).

Par solution rapide et sale pour cette situation était:

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
la source
Les intervalles ne doivent jamais être utilisés pour attendre la fin d'un constructeur de code. C'est totalement peu fiable.
Gabe Hiemstra
-1

Oui, vous pouvez ajouter une fonction de rappel à n'importe quelle insertion DOM:
$myDiv.append( function(index_myDiv, HTML_myDiv){ //.... return child })

Consultez la documentation de JQuery: http://api.jquery.com/append/
Et voici un exemple pratique similaire: http://www.w3schools.com/jquery/ tryit.asp? filename = tryjquery_html_prepend_func

Pedro Ferreira
la source
En jouant avec l'exemple w3schools, vous pouvez vérifier le 2ème paramètre, en remplaçant la .prepend()méthode par: $ ("p"). Prepend (function (n, pHTML) {return "<b> Cet élément p </b> (\" <i > "+ pHTML +" </i> \ ") <b> a l'index" + n + "</b>.";
Pedro Ferreira
1
Vous ne comprenez clairement pas le but de cet exemple ... ce n'est pas un rappel indiquant que le contenu a été ajouté, mais plutôt une fonction de rappel qui renvoie ce qui doit être ajouté.
vsync
@vsync: eactly. Vous n'avez clairement pas compris ma réponse. "enfant" est ce qui a été ajouté et non quand il a été ajouté.
Pedro Ferreira
-1

J'ai récemment rencontré un problème similaire. La solution pour moi était de recréer la colletion. Laissez-moi essayer de démontrer:

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

J'espère que cela t'aides!

Kralyk
la source
Cela ne m'a pas aidé. :(
Pal
-1

Dans jquery, vous pouvez utiliser juste après votre ajout

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

$ () appelle une fonction qui enregistre un rappel prêt pour le DOM (si une fonction lui est transmise) ou renvoie des éléments du DOM (si une chaîne de sélection ou un élément lui est passé)

Vous pouvez trouver plus ici la
différence entre $ et $ () dans jQuery
http://api.jquery.com/ready/

AJchandu
la source
-2

Je sais que ce n'est pas la meilleure solution, mais la meilleure pratique:

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

setTimeout(function(){
  // Action after append
},100);
Manvel
la source
8
Je ne pense pas que ce soit une bonne pratique. 100 est un nombre arbitraire et un événement peut prendre plus de temps.
JasTonAChair
1
Mauvaise façon de procéder. Cela ne prend peut-être pas toujours
100 ms
Voir ma réponse ici pour une explication de pourquoi cela fonctionne (le nombre pourrait / devrait être 0, pas 100): stackoverflow.com/questions/6068955/…
jleach
1
c'est au moins une meilleure pratique que les réponses ici utilisant .ready.
Kevin B
-4
$('#root').append(child);
// do your work here

Append n'a pas de callback, et c'est du code qui s'exécute de manière synchrone - il n'y a aucun risque que cela ne soit PAS fait

David Fells
la source
3
Je vois trop de commentaires comme celui-ci et ils ne sont pas utiles. @david tombe, oui, le JS est synchrone, mais le DOM a besoin de temps pour se mettre à jour. Si vous avez besoin de manipuler vos éléments ajoutés DOM mais que les éléments n'ont pas encore été rendus dans votre DOM, même si l'ajout est terminé, votre nouvelle manipulation ne fonctionnera PAS. (Par exemple: $ ('# element'). On ().
NoobishPro
-4
$('#root').append(child).anotherMethod();
Vivek
la source
1
J'ai besoin de m'appeler fonction javascript, pas fonction jQuery
David Horák