Функція jQuery після .append


92

Як викликати функцію після .appendзавершення jQuery ?

Ось приклад:

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

Проблема: Коли додається складна структура DOM, обчислення нового розміру кореневого елемента в зворотному виклику функції додавання є неправильним. Припущення полягають у тому, що DOM все ще не повністю завантажений, є лише перша дочірня частина доданого складного DOM.


Ви повинні чудово поєднувати події; append()займає дуже мало часу.
Bojangles

див. stackoverflow.com/q/8840580/176140 (dom 'reflow' у браузерах webkit)
schellmax

Це може бути корисним: stackoverflow.com/a/1489987/1375015
Костянтин Шуберт

Відповіді:


71

Тут ви маєте багато дійсних відповідей, але жодна з них насправді не говорить вам, чому це працює так, як працює.

У JavaScript команди виконуються по черзі синхронно в тому порядку, в якому вони надходять, якщо ви явно не наказали їм бути асинхронними, використовуючи час очікування або інтервал.

Це означає, що ваш .appendметод буде виконаний, і ніщо інше (не враховуючи будь-які потенційні тайм-аути чи інтервали, які можуть існувати) не буде виконуватися, поки цей метод не закінчить свою роботу.

Підводячи підсумок, немає необхідності у зворотному дзвінку, оскільки .appendвін буде виконуватися синхронно.


40
Це все правда, але ось моя проблема: я додаю складний DOM, і відразу після додавання обчислюю новий розмір кореневого елемента, але тут я отримав неправильний номер. І подумайте, проблема полягає в наступному: DOM все ще не повністю завантажений, лише перший дочірній (.append ("<div id =" child "> <div id =" subChild_with_SIZE "> </div> </div>"))
Девід Хорак,

@JinDave, до якого розміру ви маєте на увазі? Це кількість дітей, зріст батьків чи те, що вам потрібно розрахувати?
mekwall

1
@ marcus-ekwall jsfiddle.net/brszE/3 це лише метод написання, а не робочий код,
David Horák

22
@ DavidHorák, чому чому це прийнята відповідь? Це правда, що навіть незважаючи на те, що зміна відбудеться, код не буде блокуватися, поки повне перероблення не буде здивовано, що у нього буде 32 голоси "за" (або це навіть не могло спричинити переформатування) щось на зразок відповідей [тут] ( stackoverflow.com/questions/ 8840580 /… ) може бути актуальною для руди.
Андреас

17
Я згоден з Андреасом, ця відповідь не є правильною. Проблема полягає в тому, що навіть незважаючи на те, що додаток повернувся, елемент все одно може бути недоступний у DOM (в залежності від браузера, в деяких браузерах працює перерва в інших). Використання тайм-ауту все ще не гарантує, що елемент буде в DOM.
Северун

21

Хоча Маркус Екволл абсолютно правий щодо синхронності додавання, я також виявив, що в дивних ситуаціях іноді DOM не повністю відображається браузером при запуску наступного рядка коду.

У цьому сценарії рішення для тіньових дайверів відповідають правильним правилам - із використанням .ready - однак набагато акуратніше прив'язувати виклик до вашого початкового додатка.

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

1
Це абсолютно помилково і марно в будь-якій ситуації. api.jquery.com/ready
Кевін В

Привіт Кевіне, яким чином?
Даніель - Група SDS

немає абсолютно ніякої вигоди від використання .готово чекати на додані елементи, щоб "відтворити" будь-яке море, ніж ви отримали б від простого використання settimeout, лише тоді, коли документ ще не готовий. якщо документ готовий, код буде виконуватися синхронно, що матиме нульовий корисний вплив.
Kevin B

Я не думаю, що готові будуть чекати завантаження доданого документа, а натомість чекають, поки завантажиться весь DOM. Минуло близько 3 років з моменту написання цієї відповіді, але, думаю, я більше коментував тіньового дайвера вище, але тоді не мав можливості коментувати.
Даніель - Група SDS

1
Насправді це працює краще, ніж очікувалося. Краще, ніж будь-яка інша відповідь тут.
Синій

20

Ну, у мене точно така ж проблема з перерахунком розміру, і через кілька годин головного болю я повинен не погодитися з .append()поведінкою суворо синхронно. Ну принаймні в Google Chrome. Див. Наступний код.

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

Властивість padding-left правильно отримана у Firefox, але в Chrome вона порожня. Як і всі інші властивості CSS, я припускаю. Після деяких експериментів мені довелося погодитися на обгортання CSS `` геттера '' setTimeout()із затримкою 10 мс, що, на мою думку, ПОРТНЕ, але єдине, що працює в Chrome. Якби хтось із вас мав ідею, як краще вирішити цю проблему, я був би дуже вдячний.


15
це мені
МНОГО

У цьому випадку setTimeout не є потворним (це лише 10 мс). Кожного разу, коли ви запитуєте маніпуляцію "dom", chrome буде чекати, поки ваш код закінчиться, перш ніж його виконати. Отже, якщо ви викликаєте item.hide, а потім item.show, він просто нічого не зробить. Коли ваша виконуюча функція закінчена, вона застосовує ваші зміни до DOM. Якщо вам потрібно зачекати на оновлення "dom", перш ніж продовжувати, просто виконайте дії CSS / DOM, а потім викличте settimeout (function () {що робити далі}) ;. Час виходу діє як старі добрі "події" у vb6! Він повідомляє браузеру виконувати завдання очікування (оновити DOM) і повертається до вашого коду після.
foxontherock

Дивіться відповідь, яка використовує .ready()набагато кращі та елегантніші рішення.
Марк Карпентер-молодший

8

Я дивуюсь на всі відповіді тут ...

Спробуйте це:

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

Зверніть увагу на тайм-аут 0. Це не довільне число ... наскільки я розумію (хоча моє розуміння може бути дещо хитким), є дві черги подій javascript - одна для макро подій та одна для мікро подій. У "більшій" області дії входить область завдань, які оновлюють інтерфейс користувача (і DOM), тоді як мікрочерга виконує операції типу швидкого завдання.

Також розумійте, що встановлення тайм-ауту не гарантує, що код працює точно за вказаним значенням. Це робить, по суті, поміщає функцію у вищу чергу (ту, яка обробляє UI / DOM), і не запускає її до зазначеного часу.

Це означає, що встановлення тайм-ауту 0 поміщає його в UI / DOM-частину черги подій javascript, яка запускатиметься з наступним можливим шансом.

Це означає, що DOM оновлюється з усіма попередніми елементами черги (наприклад, вставленими через $.append(...);, і коли ваш код працює, DOM є повністю доступним.

(ps - я дізнався про це з Secrets of JavaScript Ninja - відмінна книга: https://www.manning.com/books/secrets-of-the-javascript-ninja )


Я додав setTimeout()роками, не знаючи чому. Дякую за пояснення!
парогенератор

5

У мене є ще один варіант, який може бути корисним для когось:

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

1
Для тих, хто вважає, що це застаріло та видалено, це правильно, але ви все одно можете використовувати його, як ...on('load', function(){ ... див. Тут: stackoverflow.com/a/37751413/2008111
caramba

5

Я зіткнувся з тією ж проблемою і знайшов просте рішення. Додайте після виклику функції додавання документ, готовий.

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


Приємно. Це працювало для мене (я додавав HTML за допомогою Handlebars та JSON і, звичайно, звичайний документ. Це вже занадто рано для всього цього).
Кетрін Осборн,

1
@KatharineOsborne Це нічим не відрізняється від "звичайного документа. Вже". document.ready викликається лише в одному конкретному сценарії, якщо використовувати його по-різному, це не змушує працювати по-іншому.
Kevin B

Ну, минув більше року, як я залишив коментар, але IIRC, контекст, де ви називаєте це, є важливим (тобто у функції). З тих пір код переданий клієнту, тому я більше не маю до нього доступу, щоб копатись назад.
Кетрін Осборн,

5

Використання MutationObserverможе діяти як зворотний виклик для jQueryappend методу :

Я пояснив це в іншому питанні , і цього разу я наведу лише приклад для сучасних браузерів:

// 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. З усіх відповідей ваша відповідь підходить найкраще. Однак у моєму випадку це не спрацювало, додаючи різні таблиці до таблиці, оскільки це переходить у нескінченний цикл. Плагін, який сортує таблицю, змінить DOM, тому він буде викликати спостерігача нескінченно разів.
Азеведо

@Azevedo - чому це було б нескінченно? я впевнений, що це скінчено. у будь-якому випадку, ви можете відокремити функцію сортування від доданого рядка "подія", будучи трохи творчим. є способи.
vsync

Коли плагін сортування сортує таблицю, він запускає спостерігача (dom змінено), який знову запускає сортування. Зараз я думаю ... Я міг би підрахувати рядки таблиці і змусити спостерігача викликати сортувальник лише за умови зміни лічильника рядків.
Азеведо

3

Я думаю, що на це добре відповіли, але це трохи більш специфічно для обробки "subChild_with_SIZE" (якщо це надходить від батьків, але ви можете адаптувати його там, де це може бути)

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

2

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


8
$.whenпрацює лише для тих об'єктів, повернення яких є обіцяним об'єктом
Rifat

він працює, але видає помилку на консолі .. "тоді це не функція"
Karan Datwani

1
Це працює лише тому, що ви anotherMethod()одразу викликаєте .. Це не передається як зворотний дзвінок.
yts

це не має сенсу.
Kevin B

1

функція додавання Jquery повертає об'єкт jQuery, щоб ви могли просто позначити метод на кінці

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

Мені потрібно зателефонувати мені власну функцію javascript після .append, мені потрібно перерахувати розмір кореня
David Horák

Ви можете використовувати jQuery кожен метод, але додаток - це синхронний метод, тому ви повинні бути в порядку, щоб зробити все, що вам потрібно, у наступному рядку.
Dve

@JinDave, що саме потрібно для перерахунку? Кількість дітей, зріст батьків або що означає розмір ?
mekwall

0

Для зображень та інших джерел ви можете використовувати:

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

0

Найчистіший спосіб - це робити поетапно. Використовуйте кожен функціонал для перебору кожного елемента. Як тільки цей елемент додається, передайте його наступній функції для обробки цього елемента.

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

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

-1

Я зіткнувся з цією проблемою під час кодування HTML5 для мобільних пристроїв. Деякі комбінації браузера та пристрою спричинили помилки, оскільки метод .append () не відображав зміни в DOM негайно (спричиняючи збій JS).

Швидким і брудним рішенням цієї ситуації було:

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

Ніколи не слід використовувати інтервали для очікування закінчення конструктора коду. Це абсолютно ненадійно.
Gabe Hiemstra,

-1

Так, ви можете додати функцію зворотного виклику до будь-якої вставки DOM:
$myDiv.append( function(index_myDiv, HTML_myDiv){ //.... return child })

Перевірте документацію JQuery: http://api.jquery.com/append/
І ось практичний, подібний приклад: http://www.w3schools.com/jquery/ tryit.asp? filename = tryjquery_html_prepend_func


Спілкуючись із прикладом w3schools, ви можете перевірити 2-й параметр, замінивши .prepend()метод на: $ ("p"). Prepend (function (n, pHTML) {return "<b> Цей p елемент </b> (\" <i > "+ pHTML +" </i> \ ") <b> має індекс" + n + "</b>.";
Педро Феррейра,

1
Ви однозначно не розумієте мети цього прикладу .. це не зворотний виклик, який каже, що вміст доданий, а швидше функція зворотного виклику, яка повертає те, що має бути додано.
vsync

@vsync: точно. Ви явно не зрозуміли моєї відповіді. "дитина" - це те, що було додано, а не коли було додано.
Педро Феррейра

-1

Нещодавно я зіткнувся з подібною проблемою. Рішенням для мене було відтворити колекцію. Дозвольте спробувати продемонструвати:

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

Сподіваюся, це допомагає!


Мені це не допомогло. :(
Пал

-1

У jquery ви можете використовувати одразу після додавання

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

$ () викликає функцію, яка або реєструє готовий до DOM зворотний виклик (якщо до нього передається функція), або повертає елементи з DOM (якщо до нього передається рядок або елемент селектора)

Більше тут ви знайдете
різницю між $ та $ () у jQuery
http://api.jquery.com/ready/


-2

Я знаю, що це не найкраще рішення, але найкраща практика:

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

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

8
Я не думаю, що це хороша практика. 100 - це довільне число, і подія може зайняти більше часу.
JasTonAChair

1
Неправильний шлях. Це не завжди може зайняти 100 мс
axelvnk

Дивіться моя відповідь тут для пояснення того , чому це працює (число може / має бути 0, а не 100): stackoverflow.com/questions/6068955 / ...
jleach

1
це принаймні краща практика, ніж відповіді тут із використанням .ready.
Kevin B

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

У Append немає зворотних викликів, і це код, який виконується синхронно - немає ризику, що це НЕ буде зроблено


3
Я бачу забагато таких коментарів, і вони не корисні. @david падає, так, JS Є синхронним, але DOM потребує часу на оновлення. Якщо вам потрібно маніпулювати доданими елементами DOM, але елементи ще не були відображені у вашому DOM, навіть якщо додавання завершено, ваша нова маніпуляція НЕ працюватиме. (Наприклад: $ ('# element').
On

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.