【问题标题】:How to wait for element to load completely into DOM?如何等待元素完全加载到 DOM 中?
【发布时间】:2020-08-29 15:13:41
【问题描述】:

这是一个示例,我使用方法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


    【解决方案1】:

    为了澄清,并不是没有加载 svg,正如关于回流和 SVG 属性的两个链接所解释的那样。这些值的行为符合规范中所述的预期。

    一种可能性,以便您可以复制 setTimeout(1000) 的准确性,同时考虑到由于操作顺序而导致的任何布局更改,并且完全“准确”而不是依赖于非标准行为,那就是使用 MutationObserver 或ResizeObserver:
    https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver

    在实践中,我建议只使用建议的 setTimeout(0) 并使用它,因为基本上所有浏览器都会正确运行(有点像对象属性可以按顺序遍历)。您可以在更新布局时触发重排,以确保这些值在运行时是正确的,并确保 SVG 内容仅在所有内容都已呈现并且您已触发布局后运行。
    我猜 setTimeout(1000) 对你有用,因为之后会运行一些其他影响代码的布局,就像控制台/预览被隐藏/显示时这些 sn-ps 上的值如何在此处关闭几个像素一样隐藏或显示控制台/预览 div 时布局会发生变化。

    什么迫使布局/回流:https://gist.github.com/paulirish/5d52fb081b3570c81e3a
    SVG 属性:https://trac.webkit.org/wiki/SVG%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>

    【讨论】:

    • 只是为了进一步解释正在发生的事情,% 值取决于布局。但是 x1 值基本上已经使用“tear-off”值进行了缓存,并且没有任何东西可以触发它更新计算的 x1 SVGAnimatedLength 属性,该属性是“在需要时从反射 SVG 属性更新的”。这就是为什么我建议触发布局/重排,它应该保证该值在检索时是最新的。
    • 这是迄今为止提出的唯一解决方案,似乎与我的实际应用程序中的各种示例一致。我想给你一个额外的感谢你为你的回答提供了这么多的背景。我插入了一个 svg.getBBox() 来强制回流。
    【解决方案2】:

    您可以使用Promise.resolve().then() 获取async 值。 .resolve() 将在完成后将数据传递到 .then()。您可以执行以下操作:

    const lineEl = document.querySelector('line');
    
    Promise.resolve(lineEl).then(res => {
        console.log(res.x1.baseVal.value);
    });
    

    【讨论】:

    • 为什么它会可靠地工作? querySelector 方法不返回承诺。我认为它可能在给定示例中起作用的原因是它会导致 console.log 语句在下一个事件循环周期中执行,顺便说一句,这有足够的时间来加载行。
    【解决方案3】:

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

    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>

    【讨论】:

      【解决方案4】:

      已经在其中一个 cmets 中提到过:事件循环

      阅读:https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif

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

      这意味着某些语句是捆绑在一起的,您无法在写入 DOM 后立即读取某些内容

      所以你的代码是等待事件循环完成的有效方式

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

      AND您可以将 1000 更改为 0 !!!

      您也可以使用requestAnimationFrame,它的作用相同:等待事件循环

      【讨论】:

      • 只想指出这个概念(经常被重复)可以被证明是错误的。尝试使用 rAF,您会发现它不一致,有时会返回 0。事件循环延迟仅适用于(基本上所有)浏览器优先考虑 UI 更新,但理论上并不准确。
      • 我猜你的意思只是requestAnimationFramesetTimeout 0 确实有效(在实践中)?
      • PS,我不知道谁-1所有其他答案,不是我
      • 也许是 OP。他可能对答案不满意。他可能“想要”的是一个布局挂钩,它延迟或更改提供给他的 SVG 操作代码的值,这将保证他的代码使用页面上呈现的最新值。不幸的是,这仍然需要一台时间机器来获取布局后检索到的值,并在他的代码执行之前放置它们。
      • 啊是的...一旦 JavaScript 0.1 + 0.2 输出正确答案就会发生...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-29
      • 2018-11-01
      • 2020-02-08
      • 2019-12-08
      • 2023-03-30
      • 2011-12-23
      • 2021-12-21
      相关资源
      最近更新 更多