Funkcja jQuery po .append

92

Jak wywołać funkcję po .appendcałkowitym wykonaniu jQuery ?

Oto przykład:

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

Problem: Kiedy dołączana jest złożona struktura DOM, obliczenie nowego rozmiaru elementu głównego w wywołaniu zwrotnym funkcji append jest nieprawidłowe. Założenia są takie, że DOM nadal nie jest w pełni załadowany, tylko pierwsze dziecko dodanego złożonego DOM jest.

David Horák
źródło
Powinieneś dobrze łączyć wydarzenia; append()zajmuje bardzo mało czasu.
Bojangles
patrz stackoverflow.com/q/8840580/176140 (dom 'reflow' w przeglądarkach webkit)
schellmax
To może być pomocne: stackoverflow.com/a/1489987/1375015
Konstantin Schubert

Odpowiedzi:

71

Masz tutaj wiele ważnych odpowiedzi, ale żadna z nich tak naprawdę nie mówi ci, dlaczego to działa tak, jak działa.

W JavaScript polecenia są wykonywane pojedynczo, synchronicznie w kolejności, w jakiej przychodzą, chyba że wyraźnie powiesz im, aby były asynchroniczne, używając limitu czasu lub interwału.

Oznacza to, że Twoja .appendmetoda zostanie wykonana i nic więcej (pomijając potencjalne przekroczenia czasu lub interwały, które mogą istnieć) nie będzie wykonywane, dopóki ta metoda nie zakończy swojego zadania.

Podsumowując, nie ma potrzeby oddzwaniania, ponieważ .appendbędą uruchamiane synchronicznie.

mekwall
źródło
40
To wszystko prawda, ale tu jest mój problem: dołączam złożony DOM i zaraz po dodaniu obliczam nowy rozmiar elementu głównego, ale tutaj mam zły numer. I pomyśl, że problem jest następujący: DOM nadal nie jest w pełni załadowany, tylko pierwsze dziecko (.append ("<div id =" child "> <div id =" subChild_with_SIZE "> </div> </div>"))
David Horák
@JinDave, do jakiego rozmiaru masz na myśli? Czy jest to liczba dzieci, wzrost rodzica, czy też co musisz obliczyć?
mekwall
1
@ marcus-ekwall jsfiddle.net/brszE/3 to tylko metoda pisania, nie działa kod,
David Horák
22
@ DavidHorák, więc dlaczego jest to akceptowana odpowiedź? Prawdą jest, że nawet jeśli zmiana nastąpi, kod nie zablokuje się do pełnego ponownego przepływu, co jest dość zaskakujące, że ma 32 głosy w górę! (Lub nawet nie spowodowałem ponownego przepływu), coś takiego jak odpowiedzi [tutaj] ( stackoverflow.com/questions/ 8840580 /… ) może mieć większe znaczenie.
Andreas,
17
Zgadzam się z Andreasem, ta odpowiedź jest nieprawidłowa. Problem polega na tym, że mimo że funkcja append zwróciła, element nadal może nie być dostępny w DOM (zależny od przeglądarki, w niektórych działa awarie w innych). Użycie limitu czasu nadal nie gwarantuje, że element znajdzie się w DOM.
Severun,
21

Chociaż Marcus Ekwall ma całkowitą rację co do synchroniczności dołączania, odkryłem również, że w dziwnych sytuacjach czasami DOM nie jest całkowicie renderowany przez przeglądarkę po uruchomieniu następnej linii kodu.

W tym scenariuszu rozwiązania shadowdiver są zgodne z poprawnymi rozwiązaniami - z użyciem .ready - jednak znacznie łatwiej jest połączyć wywołanie z oryginalnym plikiem.

$('#root')
  .append(html)
  .ready(function () {
    // enter code here
  });
Daniel - Grupa SDS
źródło
1
Jest to absolutnie fałszywe i bezużyteczne w każdej sytuacji. api.jquery.com/ready
Kevin B
Cześć Kevin, w jaki sposób?
Daniel - SDS Group
nie ma absolutnie żadnej korzyści z używania .ready do czekania, aż dołączone elementy „wyrenderują” jakikolwiek inny efekt niż w przypadku zwykłego użycia settimeout, wtedy i tylko wtedy, gdy dokument nie jest jeszcze gotowy. jeśli dokument JEST gotowy, kod zostanie uruchomiony synchronicznie, a zatem nie będzie miał żadnego użytecznego wpływu.
Kevin B
Nie sądzę, by gotowy będzie czekał na załadowanie dołączonego dokumentu, ale zamiast tego będzie czekał na załadowanie całego DOM. Minęło około 3 lat, odkąd napisałem tę odpowiedź, ale myślę, że bardziej komentowałem powyżej nurka w cieniu, ale nie miałem wtedy możliwości komentowania.
Daniel - SDS Group
1
W rzeczywistości działa to lepiej niż oczekiwano. Lepsza niż jakakolwiek inna odpowiedź tutaj.
Niebieski
20

Cóż, mam dokładnie ten sam problem z przeliczeniem rozmiaru i po wielu godzinach bólu głowy muszę się nie zgodzić z .append()zachowaniem ściśle synchronicznym. Cóż, przynajmniej w Google Chrome. Zobacz poniższy kod.

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

Właściwość padding-left jest poprawnie pobierana w przeglądarce Firefox, ale w przeglądarce Chrome jest pusta. Przypuszczam, że podobnie jak wszystkie inne właściwości CSS. Po kilku eksperymentach musiałem zadowolić się opakowaniem „gettera” CSS setTimeout()z 10 ms opóźnieniem, które, jak wiem, jest brzydkie jak cholera, ale jedyne działające w Chrome. Byłbym bardzo wdzięczny, gdyby ktoś z Was wpadł na pomysł, jak lepiej rozwiązać ten problem.

Womi
źródło
15
to mi bardzo pomogło: stackoverflow.com/q/8840580/176140 - co oznacza, append () JEST synchroniczne, ale aktualizacja DOM nie jest
schellmax
W takim przypadku setTimeout nie jest brzydki (tylko 10 ms jest). Za każdym razem, gdy zażądasz manipulacji „dom”, chrome przed wykonaniem go zaczeka na zakończenie kodu. Więc jeśli wywołasz item.hide, a następnie item.show, po prostu nic nie zrobi. Po zakończeniu wykonywania funkcji, zmiany zostaną zastosowane w modelu DOM. Jeśli musisz poczekać na aktualizację "dom" przed kontynuowaniem, po prostu wykonaj akcję CSS / DOM, a następnie wywołaj settimeout (function () {co dalej}) ;. Settimeout działa jak stare dobre „doevents” w czasowniku 6! Nakazuje przeglądarce wykonanie zadań oczekujących (aktualizacja DOM), a następnie wraca do kodu.
foxontherock
Zobacz odpowiedź, która wykorzystuje .ready()znacznie lepsze i bardziej eleganckie rozwiązanie.
Mark Carpenter Jr
8

Jestem zaskoczony wszystkimi odpowiedziami tutaj ...

Spróbuj tego:

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

Zwróć uwagę na limit czasu 0. To nie jest dowolna liczba ... jak rozumiem (chociaż moje rozumienie może być nieco niepewne), istnieją dwie kolejki zdarzeń javascript - jedna dla zdarzeń makro i jedna dla zdarzeń mikro. „Większa” kolejka o zasięgu zawiera zadania, które aktualizują interfejs użytkownika (i DOM), podczas gdy mikro kolejka wykonuje operacje typu szybkiego zadania.

Pamiętaj również, że ustawienie limitu czasu nie gwarantuje, że kod będzie działał dokładnie przy tej określonej wartości. To zasadniczo powoduje umieszczenie funkcji w wyższej kolejce (tej, która obsługuje UI / DOM) i nie uruchamia jej przed upływem określonego czasu.

Oznacza to, że ustawienie limitu czasu na 0 powoduje umieszczenie go w części UI / DOM kolejki zdarzeń javascript, aby uruchomić ją przy najbliższej możliwej okazji.

Oznacza to, że DOM zostanie zaktualizowany o wszystkie poprzednie elementy kolejki (takie jak wstawione za pośrednictwem $.append(...);, a kiedy Twój kod zostanie uruchomiony, DOM jest w pełni dostępny.

(ps - nauczyłem się tego z Secrects of the JavaScript Ninja - świetnej książki: https://www.manning.com/books/secrets-of-the-javascript-ninja )

jleach
źródło
Dodawałem setTimeout()od lat, nie wiedząc dlaczego. Dziękuję za wyjaśnienie!
steampowered
5

Mam inny wariant, który może się komuś przydać:

$('<img src="http://example.com/someresource.jpg">').load(function() {
    $('#login').submit();
}).appendTo("body");
msangel
źródło
1
Dla tych, którzy ...on('load', function(){ ... uważają , że jest to przestarzałe i usunięte, to prawda, ale nadal możesz z niego korzystać, jak tutaj: stackoverflow.com/a/37751413/2008111
caramba
5

Natknąłem się na ten sam problem i znalazłem proste rozwiązanie. Dodaj po wywołaniu funkcji append dokument gotowy.

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

nurek cieni
źródło
Ładny. To zadziałało dla mnie (dodawałem HTML za pomocą Handlebars i JSON i oczywiście zwykłego dokumentu. Dzieje się to za wcześnie na to wszystko).
Katharine Osborne
1
@KatharineOsborne To nie różni się od „zwykłego document.ready”. document.ready jest wywoływana tylko w jednym konkretnym scenariuszu, używanie go w inny sposób nie powoduje, że działa inaczej.
Kevin B,
Cóż, minął już ponad rok, odkąd zostawiłem komentarz, ale IIRC, kontekst, w którym to nazywasz, jest ważny (tj. W funkcji). Od tego czasu kod został przekazany klientowi, więc nie mam już do niego dostępu, aby go ponownie zagłębić.
Katharine Osborne
5

Używanie MutationObservermoże działać jak callback dla appendmetody jQuery :

Wyjaśniłem to w innym pytaniu i tym razem podam tylko przykład dla nowoczesnych przeglądarek:

// 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
źródło
+1. Ze wszystkich odpowiedzi twoja najlepiej pasuje. Jednak w moim przypadku nie zadziałało dodawanie różnych wierszy do tabeli, ponieważ przechodzi w nieskończoną pętlę. Wtyczka, która sortuje tabelę, zmieni DOM, więc wywoła obserwatora nieskończenie wiele razy.
Azevedo,
@Azevedo - dlaczego miałoby być nieskończone? jestem pewien, że to skończone. w każdym razie możesz oddzielić funkcję sortowania od dodanego wiersza „zdarzenie”, będąc trochę kreatywnym. są sposoby.
vsync
Kiedy wtyczka sortowania sortuje tabelę, wyzwala obserwatora (zmieniono dom), który ponownie uruchomi sortowanie. Teraz myślę ... Mogę policzyć wiersze tabeli i zmusić obserwatora do wywołania sortownika tylko wtedy, gdy licznik wierszy został zmieniony.
Azevedo,
3

Myślę, że odpowiedź na to pytanie jest dobra, ale jest to trochę bardziej specyficzne dla obsługi „subChild_with_SIZE” (jeśli pochodzi od rodzica, ale możesz go dostosować z miejsca, w którym może być)

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

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

renatoluna
źródło
8
$.whendziała tylko dla tych obiektów, które zwracają obiekt obiecany
Rifat
działa, ale wyświetla błąd na konsoli .. "to nie jest funkcją"
Karan Datwani
1
Działa to tylko dlatego, że wywołujesz anotherMethod()od razu .. To nie jest przekazywane jako wywołanie zwrotne.
YTS
to nie ma sensu.
Kevin B
1

funkcja dołączania Jquery zwraca obiekt jQuery, więc możesz po prostu oznaczyć metodę na końcu

$("#root").append(child).anotherJqueryMethod();
Dve
źródło
Muszę zadzwonić do mnie niestandardowa funkcja javascript po .append, potrzebuję ponownie obliczyć rozmiar katalogu głównego
David Horák
możesz użyć jQuery dla każdej metody, ale append jest metodą synchroniczną, więc powinieneś być w porządku, aby zrobić wszystko, co musisz w następnej linii.
Dve
@JinDave, czego dokładnie potrzebujesz do przeliczenia? Liczba dzieci, wzrost rodzica, czyli co oznacza rozmiar ?
mekwall
0

W przypadku obrazów i innych źródeł możesz użyć tego:

$(el).one('load', function(){
    // completed
}).each(function() {
    if (this.complete)
        $(this).load();
});
Grzybnia
źródło
0

Najczystszym sposobem jest robienie tego krok po kroku. Użyj funkcji each, aby przejść przez każdy element. Jak tylko ten element zostanie dołączony, przekaż go do kolejnej funkcji, aby przetworzyć ten element.

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

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

Napotkałem ten problem podczas kodowania HTML5 na urządzenia mobilne. Niektóre kombinacje przeglądarki / urządzenia powodowały błędy, ponieważ metoda .append () nie odzwierciedlała natychmiastowo zmian w DOM (powodując awarię JS).

Szybkim i brudnym rozwiązaniem tej sytuacji było:

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
źródło
Interwały nigdy nie powinny być używane do czekania na zakończenie konstruktora kodu. Jest to całkowicie zawodne.
Gabe Hiemstra
-1

Tak, możesz dodać funkcję zwrotną do dowolnego wstawienia DOM:
$myDiv.append( function(index_myDiv, HTML_myDiv){ //.... return child })

Sprawdź dokumentację JQuery: http://api.jquery.com/append/
A oto praktyczny, podobny przykład: http://www.w3schools.com/jquery/ tryit.asp? filename = tryjquery_html_prepend_func

Pedro Ferreira
źródło
Bawiąc się przykładem w3schools, możesz sprawdzić drugi parametr, zastępując .prepend()metodę przez: $ ("p"). Prepend (function (n, pHTML) {return "<b> Ten element p </b> (\" <i > "+ pHTML +" </i> \ ") <b> ma indeks" + n + "</b>.";
Pedro Ferreira
1
Najwyraźniej nie rozumiesz celu tego przykładu ... nie jest to wywołanie zwrotne informujące, że treść została dołączona, ale raczej funkcja zwrotna, która zwraca to, co ma zostać dodane.
vsync,
@vsync: eactly. Najwyraźniej nie zrozumiałeś mojej odpowiedzi. „dziecko” to to, co zostało dodane, a nie kiedy zostało dodane.
Pedro Ferreira
-1

Niedawno napotkałem podobny problem. Rozwiązaniem dla mnie było odtworzenie kolekcji. Spróbuję pokazać:

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

Mam nadzieję że to pomoże!

kralyk
źródło
To mi nie pomogło. :(
Pal
-1

W jquery możesz użyć zaraz po dodaniu pliku

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

$ () wywołuje funkcję, która albo rejestruje wywołanie zwrotne gotowego do DOMU (jeśli przekazano do niego funkcję), albo zwraca elementy z DOM (jeśli przekazano do niego selektor lub element)

Możesz znaleźć więcej
różnic między $ a $ () w jQuery
http://api.jquery.com/ready/

AJchandu
źródło
-2

Wiem, że to nie jest najlepsze rozwiązanie, ale najlepsza praktyka:

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

setTimeout(function(){
  // Action after append
},100);
Manvel
źródło
8
Nie sądzę, żeby to była dobra praktyka. 100 to dowolna liczba, a zdarzenie może potrwać dłużej.
JasTonAChair
1
Zła droga. Nie zawsze może to zająć 100 ms
axelvnk
Zobacz moją odpowiedź tutaj, aby wyjaśnić, dlaczego to działa (liczba może / powinna wynosić 0, a nie 100): stackoverflow.com/questions/6068955/ ...
jleach
1
jest to co najmniej lepsza praktyka niż odpowiedzi tutaj przy użyciu .ready.
Kevin B
-4
$('#root').append(child);
// do your work here

Append nie ma wywołań zwrotnych, a jest to kod, który jest wykonywany synchronicznie - nie ma ryzyka, że ​​NIE zostanie wykonany

David Fells
źródło
3
Widzę zbyt wiele takich komentarzy i nie są one pomocne. @david upadł, tak, JS jest synchroniczny, ale DOM potrzebuje czasu na aktualizację. Jeśli musisz manipulować dodanymi elementami DOM, ale elementy te nie zostały jeszcze wyrenderowane w Twoim DOM, nawet jeśli dodawanie zostało zakończone, nowa manipulacja NIE zadziała. (Na przykład: $ ('# element').
On
-4
$('#root').append(child).anotherMethod();
Vivek
źródło
1
Muszę zadzwonić do mnie funkcja javascript, a nie jQuery
David Horák