Функция jQuery после .append


92

Как вызвать функцию после того, как jQuery .appendполностью завершен?

Вот пример:

$("#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 это всего лишь метод написания, а не рабочий код,
— Давид Хорак

22
@ DavidHorák, почему это принятый ответ? Это правда, что даже несмотря на то, что изменение произойдет, код не будет блокироваться до тех пор, пока полная перекомпоновка не удивит, у него будет 32 голоса за! (Или это даже не вызывает перекомпоновку) что-то вроде ответов [здесь] ( stackoverflow.com/questions/ 8840580 /… ) может иметь отношение к руде.
— Андреас

17
Я согласен с Андреасом, это неправильный ответ. Проблема в том, что даже несмотря на то, что добавление было возвращено, элемент все еще может быть недоступен в DOM (зависит от браузера, работает в некоторых браузерах, в других не работает). Использование тайм-аута еще не гарантирует, что элемент будет в DOM.
— Severun

21

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

В этом сценарии решения shadowdiver находятся в правильном направлении - с использованием .ready - однако гораздо проще связать вызов с исходным дополнением.

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

1
Это абсолютно неверно и бесполезно в любой ситуации. api.jquery.com/ready
— Кевин Би

Привет, Кевин, каким образом?
— Дэниел - SDS Group

нет абсолютно никакой пользы от использования .ready для ожидания «рендеринга» добавленных элементов, как и от простого использования settimeout, если и только если документ еще не готов. если документ готов, код будет выполняться синхронно, что не окажет полезного воздействия.
— Kevin B

Я не думаю, что готовый будет ждать загрузки добавленного документа, но вместо этого будет ждать загрузки всей DOM. Прошло около 3 лет с тех пор, как я написал этот ответ, но я думаю, что больше комментировал Shadow Diver выше, но в то время у меня не было возможности комментировать.
— Дэниел - SDS Group

1
На самом деле это работает лучше, чем ожидалось. Лучше, чем любой другой ответ здесь.
— Blue

20

Что ж, у меня точно такая же проблема с пересчетом размера, и после нескольких часов головной боли я не согласен с тем, чтобы .append()вести себя строго синхронно. Хорошо хоть в Google Chrome. См. Следующий код.

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

Свойство padding-left правильно извлекается в Firefox, но в Chrome оно пусто. Полагаю, как и все другие свойства CSS. После некоторых экспериментов мне пришлось довольствоваться заключением «геттера» CSS setTimeout()с задержкой в 10 мс, что, как я знаю, УЖАСНО, но единственный, работающий в Chrome. Если бы у кого-то из вас была идея, как лучше решить эту проблему, я был бы очень благодарен.


15
это мне ОЧЕНЬ помогло: stackoverflow.com/q/8840580/176140 - это означает, что append () ЯВЛЯЕТСЯ синхронным, а обновление DOM - нет
— schellmax

В этом случае setTimeout не уродлив (только 10 мс). Каждый раз, когда вы запрашиваете манипуляцию с "dom", хром будет ждать завершения вашего кода перед его выполнением. Итак, если вы вызовете item.hide, а затем item.show, он ничего не сделает. Когда ваша выполняемая функция завершена, она применяет ваши изменения к DOM. Если вам нужно дождаться обновления «dom», прежде чем продолжить, просто выполните действие CSS / DOM, а затем вызовите settimeout (function () {что делать дальше}) ;. Settimeout действует как старые добрые «события» в vb6! Он сообщает браузеру выполнить свои ожидающие задачи (обновить DOM) и после этого вернуться к вашему коду.
— foxontherock

Посмотрите ответ, в котором используется .ready()гораздо лучшее и более элегантное решение.
— Марк Карпентер-младший,

8

Меня удивляют все ответы здесь ...

Попробуй это:

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

Обратите внимание на тайм-аут 0. Это не произвольное число ... как я понимаю (хотя мое понимание может быть немного шатким), есть две очереди событий javascript - одна для макро-событий и одна для микро-событий. Очередь с «большей» областью действия содержит задачи, которые обновляют пользовательский интерфейс (и DOM), а микро-очередь выполняет операции типа быстрой задачи.

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

Это означает, что установка тайм-аута, равного 0, помещает его в UI / DOM-часть очереди событий javascript для запуска при следующей возможности.

Это означает, что DOM обновляется со всеми предыдущими элементами очереди (например, вставленными через $.append(...);, и когда ваш код запускается, DOM полностью доступен.

(ps - я узнал об этом из Secrects of the 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». document.ready вызывается только в одном конкретном сценарии, использование его по-другому не заставляет его работать по-другому.
— Кевин Би

Ну, прошло больше года с тех пор, как я оставил комментарий, но IIRC, контекст, в котором вы это вызываете, важен (то есть в функции). С тех пор код был передан клиенту, поэтому у меня больше нет к нему доступа, чтобы
— Кэтрин Осборн,

5

Использование MutationObserverможет действовать как обратный вызов для appendметода jQuery :

Я объяснил это в другом вопросе , и на этот раз приведу пример только для современных браузеров:

// 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

Когда плагин сортировки сортирует таблицу, он запускает наблюдателя (доменное имя изменено), который снова запускает сортировку. Теперь я думаю ... Я мог бы подсчитать строки таблицы и заставить наблюдателя вызывать сортировщик, только если был изменен счетчик строк.
— Azevedo

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

он работает, но выдает ошибку на консоли .. «
— Значит,

1
Это работает только потому, что вы вызываете anotherMethod()сразу .. Это не передается как обратный вызов.
— yts

это не имеет никакого смысла.
— Кевин Би

1

функция добавления Jquery возвращает объект jQuery, поэтому вы можете просто пометить метод в конце

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

Мне нужно вызвать пользовательскую функцию javascript после .append, мне нужно пересчитать размер корневого каталога,
— Дэвид Хорак,

вы могли бы использовать метод jQuery each, но append - это синхронный метод, поэтому вы должны быть в порядке, просто сделайте все, что вам нужно, в следующей строке.
— Дв

@JinDave, что именно вам нужно пересчитать? Количество детей, рост родителей или что означает размер ?
— mekwall

0

Для изображений и других источников вы можете использовать это:

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

0

Самый чистый способ - делать это шаг за шагом. Для обхода каждого элемента используйте функцию each. Как только этот элемент добавлен, передайте его следующей функции для обработки этого элемента.

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

Никогда не следует использовать интервалы для ожидания завершения конструктора кода. Это совершенно ненадежно.
— Гейб Хиемстра 05

-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, вы можете проверить второй параметр, заменив .prepend()метод на: $ ("p"). Prepend (function (n, pHTML) {return "<b> This p element </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();

Надеюсь это поможет!


Мне это не помогло. :(
— Pal

-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 01

См. Мой ответ здесь, чтобы объяснить, почему это работает (число может / должно быть 0, а не 100): stackoverflow.com/questions/6068955/…
— jleach

1
это, по крайней мере, лучшая практика, чем ответы здесь с использованием .ready.
— Kevin B

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

В приложении нет обратных вызовов, и это код, который выполняется синхронно - нет риска, что это НЕ будет выполнено.


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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.