如何等待元素完全加载到DOM中?

问题描述 投票:0回答:3

下面是一个例子,我使用了以下方法 element.appendChild() 然后继续,好像该元素已被同步添加。

但正如日志所显示的那样,它没有。至少所有与其渲染相关的浏览器计算都没有完成。

如何等待元素完全加载完毕后再继续执行?当然在这种情况下,睡眠1000毫秒是可行的,但在一般情况下,这是不可靠的。

const serializedSvg = `<svg style="width:100%;height:100%;">
  <line x1="25%" y1="25%" x2="75%" y2="75%" style="stroke: rgb(234, 243, 234);stroke-width: 5;"></line>
</svg>`

const svgDom = new DOMParser().parseFromString(serializedSvg, 'text/html').querySelector('svg')
const container = document.querySelector("#container")
container.appendChild(svgDom)

const lineEl = document.querySelector('line')
console.log("fail:" + lineEl.x1.baseVal.value)
setTimeout(() => {
	console.log("success:" + lineEl.x1.baseVal.value)
}, 1000)
<div id="container">

</div>
javascript svg dom
3个回答
0
投票

澄清一下,不是像回流和SVG属性的两个链接所解释的那样没有加载SVG。 这些值是按照规范中所说的那样进行的。

一种可能性是使用MutationObserver或ResizeObserver,这样你就可以复制setTimeout(1000)的精确性,并考虑到由于操作顺序引起的布局变化,并且完全 "精确",而不是依赖于非标准行为。https:/developer.mozilla.orgen-USdocsWebAPIResizeObserver。

在实际操作中,我建议只用setTimeout(0)就可以了,因为基本上所有的浏览器都会表现得很正确(有点像对象属性可以按顺序遍历)。 你可以在每次更新布局的时候触发一个回流,以确保这些值在运行时是正确的,并确保SVG的东西只在所有东西都被渲染并且你已经触发了一个布局之后才会运行。我猜想setTimeout(1000)对你来说是有效的,因为之后还有一些影响布局的代码在运行,就像当consolepreview被hiddenshown时,由于consolepreview div被隐藏或显示时的布局变化,这些片段上的值在这里会偏离几个像素。

是什么迫使布局reflow。 https:/gist.github.compaulirish5d52fb081b3570c81e3a SVG属性。https:/trac.webkit.orgwikiSVG%20properties。

根据SVG属性的文档,这些属性在需要之前是被懒惰计算的,并且是基于 "撕掉 "快照(x1是SVGAnimatedLength对象)。

下面强制回流,将 "直到需要为止 "坐实,这样在访问百分比之前就会计算并更新它们。

const serializedSvg = `<svg style="width:100%;height:100%;"">
  <line x1="25%" y1="25%" x2="75%" y2="75%" style="stroke: rgb(234, 243, 234);stroke-width: 5;"></line>
</svg>`

const svgDom = new DOMParser().parseFromString(serializedSvg, 'text/html').querySelector('svg')
const container = document.querySelector("#container")
container.appendChild(svgDom)

const lineEl = svgDom.querySelector('line')

// force layout/reflow
lineEl.getBBox()

console.log("fail:" + lineEl.x1.baseVal.value)

setTimeout(() => {
	console.log("success:" + lineEl.x1.baseVal.value)
}, 1000)
<div id="container">

</div>

-1
投票

你可以使用 Promise.resolve().then() 为获得 async 价值。该 .resolve() 将数据传入 .then() 当它完成的时候。你可以做这样的事情。

const lineEl = document.querySelector('line');

Promise.resolve(lineEl).then(res => {
    console.log(res.x1.baseVal.value);
});

-1
投票

你需要等待你要插入的文档的加载事件。

const serializedSvg = `<svg style="width:100%;height:100%;">
  <line x1="25%" y1="25%" x2="75%" y2="75%" style="stroke: rgb(234, 243, 234);stroke-width: 5;"></line>
</svg>`

const svgDom = new DOMParser().parseFromString(serializedSvg, 'text/html').querySelector('svg')
const container = document.querySelector("#container")
container.appendChild(svgDom)

window.addEventListener("load", function(){
const lineEl = document.querySelector('line')
console.log("success:" + lineEl.x1.baseVal.value)
}); 
<div id="container">

</div>

-1
投票

在一个评论中已经提到了。事件循环

阅读。https:/dev.tolydiahalliejavascript-visualized-event-loop-3dif。

基本上,它归结为浏览器引擎优化写到DOM的内容。

这意味着有些语句是捆绑的,而你不能 阅读 的东西写到DOM上后,马上就有了

所以,你的代码是一个有效的方法来等待事件循环的完成。

setTimeout(() => {
    console.log("success:" + lineEl.x1.baseVal.value)
}, 1000)

AND 你可以改变它 10000 !!!

你也可以用 requestAnimationFrame它也是这样做的:等待事件循环。

© www.soinside.com 2019 - 2024. All rights reserved.