如何判断jquery html()的更改何时完成?

12

我正在使用jQuery来更改标签的HTML内容,新的HTML可能是一个非常长的字符串。

$("#divToChange").html(newHTML);

我希望能够选择新HTML中创建的元素,但如果我紧接着上面这行代码放置选择元素的代码,就会出现一种竞争条件,因为html()方法所做的更改可能尚未完成渲染。在这种情况下,尝试选择新元素并不总是起作用。

我想知道的是,有没有触发事件或其他方式通知html()方法已经完成渲染的方法?我遇到了jQuery watch插件,它可以作为解决方法,但并不理想。是否有更好的方法?


3
JS是单线程的,所以这种情况不应该发生...你能在jsbin.com上发布一个可重现的例子吗? - Michael Haren
1
@Michael - 你可以试试jsFiddle,我觉得它更加实用:http://jsfiddle.net/ - Nick Craver
3个回答

6

正如评论者已经提到的那样,JavaScript是单线程的,因此您无法出现竞争条件。

然而,可能会让您困扰的是,UI不会基于JavaScript自行更新,直到一个线程完成。这意味着整个方法必须完成,包括在调用html(...)之后的所有代码,然后浏览器才会呈现内容。

如果在调用html(...)之后的代码依赖于页面布局在继续之前被重新计算,您可以像这样做:

$("#divToChange").html(newHTML);
setTimeout(function() {
    // Insert code to be executed AFTER
    // the page renders the markup
    // added using html(...) here
}, 1);

在JavaScript中,使用时间为1setTimeout(...)可以使代码在当前调用函数的JavaScript代码完成并且浏览器更新UI后再执行。这可能会解决您的问题,但除非您能提供一个可重现错误的示例,否则很难确定。


2
重新流动(reflow)不会等待所有正在运行的代码完成。按顺序运行多个操作,每个操作都可能引起重新流动,会比单个操作遭受更大的重新流动性能影响(即使重新流动的后果是相同的)。 - eyelidlessness
1
@eyelidlessness 除非我误解了你的意思,否则这简直是不正确的。你可以通过访问我创建的这个简单测试来证明:http://jsbin.com/avuli/6 点击文档一次,代码将执行一个5秒的while循环。请注意,在5秒结束之前,UI不会更新。第二次点击时,我使用计时器在循环中更新UI。这次,你会注意到UI在DOM更改时实时更新。 - Dan Herbert
2
澄清一下,“reflow”并不一定意味着文档会在视觉上进行更新。你的while循环是一个阻塞操作,因此在它完成之前,视觉上不会发生任何更新。但是每次操作都会发生回流,这样查询依赖于回流的数据的DOM就能够给出正确的结果,即使页面在视觉上停滞不前。请参见我的脚本编辑:http://jsbin.com/ayulu3(首次单击后查看控制台)。 - eyelidlessness
3
为了深入探讨这个话题,每次操作时都会发生回流这一事实,是批量 DOM 操作表现更好的原因之一,当它们能够强制只进行单个回流时(例如将单个 <div> 或包含要附加元素集合的 DocumentFragment 追加),它们的表现会更佳。 - eyelidlessness

2
使用 .ready jQuery 函数
$("#divToChange").html(newHTML).ready(function () {
                    // run when page is rendered
                    });

1
这是7年后的今天,我遇到了和@mikel描述的情况完全一样的场景,我无法避免使用“基于计时器的解决方案”。因此,我想分享一下我开发的解决方案,以防仍有人在处理此问题时遇到困难。
我不喜欢在代码中使用setTimeoutsetInterval。因此,我创建了一个小插件,您可以将其放置在您认为最好的位置。我使用了setInterval,但您可以更改为setTimeout或其他您心目中的解决方案。简单来说,我们的想法是创建一个Promise并持续检查元素。一旦准备就绪,我们就会解决该Promise。
// jquery.ensure.js

  $.ensure = function (selector) {
    var promise = $.Deferred();
    var interval = setInterval(function () {
      if ($(selector)[0]) {
        clearInterval(interval);
        promise.resolve();
      }
    }, 1);
    return promise;
  };

// my-app.js

  function runWhenMyElementExists () {
    // run the code that depends on #my-element
  }

  $.ensure('#my-element')
    .then(runWhenMyElementExists);

非常好,我正好在寻找这个非常简单和短小的代码。它对我来说绝对有效。 - PiMathCLanguage

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接