.append之后的jQuery函数


90

jQuery .append完成后如何调用函数?

这是一个例子:

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

问题:当添加复杂的DOM结构时,在append函数回调中计算根元素的新大小是错误的。假设DOM仍未完全加载,只有添加的复杂DOM的第一个子代才加载。


您应该进行精细的连锁活动;append()花费很少的时间。
Bojangles

请参阅stackoverflow.com/q/8840580/176140(Webkit浏览器中的dom“重排”)
schellmax 2012年

Answers:


70

您在这里有很多有效的答案,但是没有一个能真正告诉您它为什么如此有效。

在JavaScript中,除非您通过使用超时或间隔明确告诉它们是异步的,否则命令一次才能执行,并且顺序是同步的。

这意味着您 .append方法将被执行,并且在该方法完成其工作之前,将不执行任何其他操作(不考虑任何潜在的超时或间隔)。

总而言之,由于.append将同步运行,因此无需回调。


39
没错,但这是我的问题:我附加了复杂的DOM,并在追加之后立即计算了根元素的新大小,但是这里输入的数字错误。并认为,问题是这样的:DOM仍未完全加载,只有第一个子对象(.append(“ <div id =” child“> <div id =” subChild_with_SIZE“> </ div> </ div>”))
大卫·霍拉克(DavidHorák),

@JinDave,您指的是什么尺寸?是孩子的数量,父母的身高还是需要计算什么?
mekwall

1
@马库斯- ekwall jsfiddle.net/brszE/3这只是写入方法,不工作的代码,
大卫HORAK

22
@DavidHorák那么,为什么这是可接受的答案?这是真的,即使发生变化的代码将不会被阻塞,直到完全回流很惊讶这个已经32了,票!(或者是我不甚至造成回流)类似的答案[这里](stackoverflow.com/questions/ 8840580 /…)可能与矿石有关。
Andreas

17
我同意安德里亚斯的说法,这个答案是不正确的。问题是即使返回了append,该元素在DOM中仍可能不可用(取决于浏览器,在某些浏览器中有效,在其他浏览器中无效)。使用超时仍然不能保证该元素将在DOM中。
Severun '16

19

好吧,我在重新计算尺寸时遇到了完全相同的问题,经过数小时的头痛之后,我不同意.append()严格同步的行为。至少在Google Chrome中是如此。请参阅以下代码。

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

在Firefox中可以正确检索padding-left属性,但在Chrome中为空。像我想的所有其他CSS属性一样。经过一些实验后,我不得不适应将CSS'getter'封装为setTimeout()10毫秒的延迟,我知道这很丑陋,但唯一在Chrome中工作的。如果您有任何想法如何更好地解决此问题,我将不胜感激。


15
这帮助了我很多:stackoverflow.com/q/8840580/176140-意味着,append()是同步的,但是DOM更新不是
schellmax 2012年

在这种情况下,setTimeout并不难看(仅10ms)。每次您请求“ dom”操作时,chrome都会在执行代码之前等待代码结束。因此,如果您先调用item.hide,然后再调用item.show,则它将什么都不做。当您的执行函数结束时,它将您的更改应用于DOM。如果您需要等待“ dom”更新后再继续,只需执行CSS / DOM操作,然后调用settimeout(function(){下一步操作});。settimeout就像vb6中的旧“ doevents”一样!它告诉浏览器执行其等待的任务(更新DOM),然后返回到您的代码。
foxont​​herock

请参见使用.ready()更好,更优雅的解决方案的答案。
马克·卡彭特(Marc Carpenter Jr)

19

尽管Marcus Ekwall关于append的同步性是绝对正确的,但我还发现,在奇怪的情况下,有时当下一行代码运行时,浏览器无法完全呈现DOM。

在这种情况下,shadowdiver解决方案将遵循正确的思路-使用.ready-不过,将调用链接到您的原始追加文件要好得多。

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

在每种情况下,这都是绝对错误和无用的。api.jquery.com/ready
Kevin B

嗨,凯文,用什么方式?
Daniel-SDS Group

如果仅当文档尚未准备就绪时,使用.ready等待附加元素进行“渲染”绝对没有任何好处,这比仅使用settimeout所获得的效果还要好。如果文档已准备就绪,则代码将同步运行,从而使有用影响为零。
凯文B

我不认为就绪将等待附加文档的加载,而是等待整个DOM的加载。自从我写出这个答案大约已有3年了,但我想我在上面对影子潜水员的评论更多,但当时没有发表评论的便利。
丹尼尔-SDS集团

1
实际上,这比预期的要好。比这里的任何其他答案都要好。
蓝色

8

我对这里的所有答案感到惊讶...

试试这个:

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

注意0超时。这不是一个任意的数字...据我了解(尽管我的理解可能有些不稳定),有两个javascript事件队列-一个用于宏事件,一个用于微事件。“较大”范围的队列包含更新UI(和DOM)的任务,而微队列执行快速任务类型的操作。

还应意识到,设置超时并不能保证代码完全按照该指定值执行。这实际上是将函数放入较高的队列(处理UI / DOM的队列),并且不在指定时间之前运行它。

这意味着将超时设置为0会将其放入javascript事件队列的UI / DOM部分,以便在下一次可能的机会上运行。

这意味着DOM将使用所有先前的队列项目(例如通过插入)进行更新$.append(...);,并且在您的代码运行时DOM完全可用。

ps-我从JavaScript忍者的秘诀中学到了这本书-很棒的书: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

我遇到了同样的问题,并找到了一个简单的解决方案。调用append函数后添加一个文档准备就绪。

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


真好 这对我有用(我正在使用Handlebars和JSON附加HTML,当然还添加了常规文档。所有这些准备就绪都为时过早)。
凯瑟琳·奥斯本

1
@KatharineOsborne这与“常规文档。就绪”没有什么不同。document.ready仅在一种特定情况下被调用,以不同的方式使用它不会使它以不同的方式工作。
凯文B

自从我发表评论以来已经过去了一年多,但是IIRC,您所说的位置的上下文很重要(即在函数中)。该代码已经被传递到客户端,所以我不能够获得任何更多的挖回来。
凯瑟琳·奥斯本

5

使用MutationObserver可以像jQuery的回调一样append方法:

我已经在另一个问题中对此进行了解释,这一次,我仅给出现代浏览器的示例:

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

@Azevedo-为什么会是无限的?我确定这是有限的。无论如何,您可以通过有点创意将排序功能与添加的行“事件” 分开。有办法。
vsync

当sort插件对表进行排序时,它触发观察者(更改了dom),这将再次触发排序。现在我在想...我可以计数表的行数,并且只有在更改行计数器时才让观察者调用排序器。
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 2012年

它正在工作,但在控制台上给出了一个错误。“然后不是函数”
Karan Datwani 2015年

1
这仅适用于您立即调用anotherMethod()。.不会作为回调传递。
15:04

这没有任何意义。
凯文B

1

Jquery append函数返回一个jQuery对象,因此您只需在最后标记一个方法

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

我需要在.append之后给我打电话自定义javascript函数,我需要重新计算根的大小
DavidHorák,

您可以使用jQuery每个方法,但是append是一个同步方法,因此您应该可以在下一行中进行所需的操作。
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>');

间隔永远不应该用于等待代码构造函数完成。这是完全不可靠的。
加贝·希姆斯特拉

-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>此p元素</ b>(\” <i >“ + pHTML +” </ i> \“)<b>的索引为” + n +“ </ b>。”
Pedro Ferreira 2014年

1
你显然不明白,example..it它不是一个回调称内容已被追加为目的的,而是由一个回调函数返回什么要追加的。
vsync 2016年

@vsync:确实。您显然不明白我的回答。“孩子”是什么已被追加并没有当它被追加。
Pedro Ferreira

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

希望这可以帮助!


它没有帮助我。:(
2013年

-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的答案更好。
凯文B

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

Append没有回调,并且这是同步执行的代码-没有未完成的风险


3
我看到太多这样的评论,它们没有帮助。@david拒绝了,是的,JS是同步的,但是DOM需要时间来更新。如果您需要操作DOM附加元素,但尚未将元素呈现到DOM中,即使添加完成,您的新操作也不起作用。(例如:$( '#元件')上()。
NoobishPro

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.