如何判断动态创建的元素何时呈现

时间:2013-04-08 09:00:24

标签: javascript dom

我需要准确测量我的网络应用中的文字尺寸,我通过创建一个元素(带有相关的CSS类)来实现,设置其innerHTML然后使用{{1}将其添加到容器中}。

执行此操作后,在呈现元素之前需要等待,并且可以读取其appendChild以找出文本的宽度。

目前,我正在使用offsetWidth等待渲染完成。

我可以收听任何回调,还是以更可靠的方式告诉我创建的元素何时被渲染?

8 个答案:

答案 0 :(得分:35)

目前没有DOM事件表明元素已完全呈现(例如附加的CSS应用和绘制)。这可能会使一些DOM操作代码返回错误或随机结果(比如获取元素的高度)。

使用setTimeout为浏览器提供渲染的一些开销是最简单的方法。使用

setTimeout(function(){}, 0)

可能是最实际的准确,因为它将您的代码放在活动浏览器事件队列的末尾而没有任何延迟 - 换句话说,您的代码在渲染操作之后立即排队(以及当时发生的所有其他操作) )。

答案 1 :(得分:20)

接受的答案是从2014年开始,现在已经过时了。 setTimeout可能有用,但它不是最干净的,也不一定能保证元素已添加到DOM中。

今天,您应该使用MutationObserver来检测元素何时添加到DOM。 MutationObservers现在在所有现代浏览器中得到广泛支持(Chrome 26+,Firefox 14+,IE11,Edge,Opera 15+等。

将元素添加到DOM后,您将能够检索其实际尺寸。

这是一个简单的示例,说明如何在将元素添加到DOM时使用MutationObserver进行侦听。

为简洁起见,我使用jQuery语法构建节点并将其插入DOM中。

var myElement = $("<div>hello world</div>")[0];

var observer = new MutationObserver(function(mutations) {
   if (document.contains(myElement)) {
        console.log("It's in the DOM!");
        observer.disconnect();
    }
});

observer.observe(document, {attributes: false, childList: true, characterData: false, subtree:true});

$("body").append(myElement); // console.log: It's in the DOM!

observer事件处理程序将在document添加或删除任何节点时触发。在处理程序中,我们会执行contains检查以确定myElement中是否document

您不需要遍历mutations中存储的每个MutationRecord,因为您可以直接在document.contains执行myElement检查。

要提高效果,请将document替换为DOM中包含myElement的特定元素。

答案 2 :(得分:9)

This blog post Swizec Teller建议使用requestAnimationFrame,并检查元素的size

function try() {
    if (!$("#element").size()) {
        window.requestAnimationFrame(try);
    } else {
        $("#element").do_some_stuff();
    }
};
  

在实践中它只会重试一次。因为无论如何,在下一个渲染帧中,无论是60秒还是分钟,元素都将被渲染。

答案 3 :(得分:5)

MutationObserver可能是最好的方法,但这是一个可行的简单替代方案

我有一些javascript为大表构建HTML,并将div的innerHTML设置为生成的HTML。如果我在设置innerHTML后立即获取Date(),我发现时间戳是表完全呈现之前的一段时间。我想知道渲染需要多长时间(这意味着我需要在渲染完成后检查Date())。我发现我可以通过设置div的innerHTML然后(在同一个脚本中)调用页面上某个按钮的click方法来完成此操作。只有在完全呈现HTML之后才会执行click处理程序,而不仅仅是在div的innerHTML属性设置之后。我通过将Click处理程序生成的Date()值与设置div的innerHTML属性的脚本检索的Date()值进行比较来验证这一点。

希望有人发现这个有用的

答案 4 :(得分:3)

就我而言,setTimeoutMutationObserver 之类的解决方案并不完全可行。 相反,我使用了 ResizeObserver。根据{{​​3}}:

<块引用>

如果遵循规范,实现应该调用 在绘制之前和布局之后调整事件大小。

所以基本上观察者总是在布局后触发,因此我们应该能够获得被观察元素的正确尺寸。 作为奖励,观察者已经返回元素的尺寸。因此我们甚至不需要调用像 offsetWidth 这样的东西(即使它也应该工作)

const myElement = document.createElement("div");
myElement.textContent = "test string";

const resizeObserver = new ResizeObserver(entries => {
  const lastEntry = entries.pop();

  // alternatively use contentBoxSize here
  const width = lastEntry.borderBoxSize.inlineSize;
  const height = lastEntry.borderBoxSize.blockSize;

  resizeObserver.disconnect();

  console.log("width:", width, "height:", height);
});

resizeObserver.observe(myElement);

document.body.append(myElement);

我们也可以用这样一个方便的异步函数包装:

function appendAwaitLayout(parent, element) {
  return new Promise((resolve, reject) => {
    const resizeObserver = new ResizeObserver((entries) => {
      resizeObserver.disconnect();
      resolve(entries);
    });

    resizeObserver.observe(element);

    parent.append(element);
  });
}

// call it like this
appendAwaitLayout(document.body, document.createElement("div")).then((entries) => {
  console.log(entries)
  // do stuff here ...
});

答案 5 :(得分:0)

当您举例时

var clonedForm = $('#empty_form_to_clone').clone(true)[0];

var newForm = $(clonedForm).html().replace(/__prefix__/g, next_index_id_form);

// next_index_id_form is just a integer 

我在这做什么?
我克隆已经渲染的元素并更改要渲染的html。

接下来,我将该文本附加到容器中。

$('#container_id').append(newForm);

当我想在newForm中的按钮添加事件处理程序, WELL 时,问题就出现了,只需使用准备事件。

$(clonedForm).ready(function(event){
   addEventHandlerToFormButton();
})

我希望这对你有所帮助。

PS:对不起我的英文。

答案 6 :(得分:0)

假设您的元素具有类名class =“ test” 以下功能继续测试是否已发生更改 如果有,请运行功能

number

答案 7 :(得分:0)

根据@Elliot B.的回答,我制定了适合自己的计划。

const callback = () => {
  const el = document.querySelector('#a');
  if (el) {
    observer.disconnect();
    el.addEventListener('click', () => {});
  }
};

const observer = new MutationObserver(callback);
observer.observe(document.body, { subtree: true, childList: true });