我需要在我的网络应用程序中准确测量文本的尺寸,我通过创建一个元素(使用相关的 CSS 类)、设置它innerHTML
然后使用appendChild
.
完成此操作后,在元素被渲染之前有一个等待,并且offsetWidth
可以读取它以了解文本的宽度。
目前,我一直setTimeout(processText, 100)
在等待渲染完成。
有没有我可以听的回调,或者更可靠的方式来告诉我创建的元素何时被渲染?
我需要在我的网络应用程序中准确测量文本的尺寸,我通过创建一个元素(使用相关的 CSS 类)、设置它innerHTML
然后使用appendChild
.
完成此操作后,在元素被渲染之前有一个等待,并且offsetWidth
可以读取它以了解文本的宽度。
目前,我一直setTimeout(processText, 100)
在等待渲染完成。
有没有我可以听的回调,或者更可靠的方式来告诉我创建的元素何时被渲染?
接受的答案来自 2014 年,现已过时。AsetTimeout
可能有效,但它不是最干净的,也不一定保证元素已添加到 DOM。
截至 2018 年,您应该使用MutationObserver来检测元素何时添加到 DOM。现在,所有现代浏览器(Chrome 26+、Firefox 14+、IE11、Edge、Opera 15+ 等)都广泛支持 MutationObservers。
将元素添加到 DOM 后,您将能够检索其实际尺寸。
这是一个简单的示例,说明如何使用 aMutationObserver
来侦听何时将元素添加到 DOM。
为简洁起见,我使用 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
.
您不需要遍历存储的每个 MutationRecord,mutations
因为您可以document.contains
直接在myElement
.
要提高性能,请替换document
为将包含myElement
在 DOM 中的特定元素。
当前没有 DOM 事件指示元素已完全呈现(例如,附加的 CSS 应用和绘制)。这会使一些 DOM 操作代码返回错误或随机的结果(例如获取元素的高度)。
使用 setTimeout 给浏览器一些渲染开销是最简单的方法。使用
setTimeout(function(){}, 0)
可能是最实际准确的,因为它将您的代码放在活动浏览器事件队列的末尾而没有任何延迟 - 换句话说,您的代码在渲染操作(以及当时发生的所有其他操作)之后立即排队。
Swizec Teller的这篇博requestAnimationFrame
文建议使用, 并检查size
元素的 。
function try_do_some_stuff() {
if (!$("#element").size()) {
window.requestAnimationFrame(try_do_some_stuff);
} else {
$("#element").do_some_stuff();
}
};
在实践中,它只重试一次。因为无论如何,到下一个渲染帧,无论是 60 秒还是一分钟,元素都将被渲染。
您实际上需要稍等片刻才能获得渲染后的时间。requestAnimationFrame在下一次绘制之前触发。requestAnimationFrame(()=>setTimeout(onrender, 0))
在元素被渲染之后也是如此。
MutationObserver 可能是最好的方法,但这里有一个可能有效的简单替代方法
我有一些 javascript 为大表构建 HTML,并将 div 的 innerHTML 设置为生成的 HTML。如果我Date()
在设置 innerHTML 后立即获取,我发现时间戳是在表格完全呈现之前的一段时间。我想知道渲染需要多长时间(这意味着我需要Date()
在渲染完成后检查)。我发现我可以通过设置 div 的 innerHTML 然后(在同一个脚本中)调用页面上某个按钮的 click 方法来做到这一点。只有在 HTML 完全呈现后才会执行单击处理程序,而不仅仅是在设置 div 的 innerHTML 属性之后。我通过将点击处理程序生成的 Date() 值与设置 div 的 innerHTML 属性的脚本检索到的 Date() 值进行比较来验证这一点。
希望有人觉得这很有用
在我的情况下,解决方案就像setTimeout
或MutationObserver
不完全可行。相反,我使用了ResizeObserver。根据MDN:
如果实现遵循规范,则应在绘制之前和布局之后调用调整大小事件。
所以基本上观察者总是在布局后触发,因此我们应该能够获得被观察元素的正确尺寸。作为奖励,观察者已经返回了元素的尺寸。因此我们甚至不需要调用类似的东西offsetWidth
(即使它也应该工作)
const myElement = document.createElement("div");
myElement.textContent = "test string";
const resizeObserver = new ResizeObserver(entries => {
const lastEntry = entries.pop();
// alternatively use contentBoxSize here
// Note: older versions of Firefox (<= 91) provided a single size object instead of an array of sizes
// https://bugzilla.mozilla.org/show_bug.cgi?id=1689645
const width = lastEntry.borderBoxSize?.inlineSize ?? lastEntry.borderBoxSize[0].inlineSize;
const height = lastEntry.borderBoxSize?.blockSize ?? lastEntry.borderBoxSize[0].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 ...
});
假设你的元素有 classname class="test" 下面的函数继续测试如果发生了变化,如果发生了变化,运行函数
function addResizeListener(elem, fun) {
let id;
let style = getComputedStyle(elem);
let wid = style.width;
let hei = style.height;
id = requestAnimationFrame(test)
function test() {
let newStyle = getComputedStyle(elem);
if (wid !== newStyle.width ||
hei !== newStyle.height) {
fun();
wid = newStyle.width;
hei = newStyle.height;
}
id = requestAnimationFrame(test);
}
}
let test = document.querySelector('.test');
addResizeListener(test,function () {
console.log("I changed!!")
});
例如,当您制作时
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:对不起我的英语。
根据@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 });