【问题标题】:Injecting html changes the properties of an element's saved instance注入 html 会更改元素已保存实例的属性
【发布时间】:2017-03-27 21:07:17
【问题描述】:

当我尝试在文档正文中注入原始 html 时,使用 .querySelector(); 检索到的元素的保存实例会突然重置它的 .clientHeight 和 .clientWidth 属性。

以下页面显示问题

<head>

<script>
    window.addEventListener('load', pageInit);

    function pageInit() {
        // saving instance for later use
        var element = document.querySelector('#element')

        alert(element.clientHeight);   // returns 40

        document.querySelector('body').innerHTML += '<p>anything</p>';

        alert(document.querySelector('#element').clientHeight);  // returns 40
        alert(element.clientHeight);  // returns 0
    }
</script>

<style>
    #element {
        height: 40px;
    }
</style>
</head>

<body>

    <div id="element"></div>

</body>

为什么元素变量的实例属性会被重置? 正如这个问题中所指出的,addEventListener gone after appending innerHTML eventListeners 被分离,但这仍然不能解释为什么该元素节点仍然存在以及为什么它的属性被清零。

【问题讨论】:

标签: javascript


【解决方案1】:

似乎当您重置body 元素的innerHTML 时,您会导致整个页面重新呈现。因此,页面上显示的div#element 与JavaScript 变量element 指向的元素不同;相反,您看到的是重新呈现页面时创建的新元素。但是,element指向的div#element仍然存在;它还没有被浏览器的垃圾收集器销毁。

为了证明这一点,尝试用console.log(element) 替换最后一个警报(警报为0 的那个),然后尝试单击控制台窗口中的元素。你会看到&lt;div id="element"&gt;&lt;/div&gt;,但是当你点击它时,什么也没有发生,因为 div 不在页面上。

相反,您想要做的是:

const newEl = document.createElement('p');
newEl.innerHTML = 'anything';
document.querySelector('body').appendChild(newEl);

而不是设置body.innerHTML 属性。

仅供参考,您可能应该将alerts 切换为console.logs,因为警报会惹恼人们,而控制台是在开发中测试事物的方式。

【讨论】:

  • 发布的 sn-p 只是一个更大的网站的调试重构,我更喜欢在正文末尾直接注入原始 svg 符号定义
  • 或者:document.querySelector('body').insertAdjacentHTML("beforeend", '&lt;p&gt;anything&lt;/p&gt;'); 或任何其他非破坏性插入方法。 - 无论如何,是的,换句话说,最重要的部分是了解.innerHTML += 将 1:获取元素的 innerHTML 的字符串表示,2. 附加另一个字符串,3. 返回复合 string 进入元素的 inner 'body' 因此(正如正确指出的那样)将使任何以前的引用都无用。
  • 明白了。我只是假设你想向身体添加元素,我的错
  • @RokoC.Buljan 这里有一些有趣的东西 - 当我测试将新元素附加到正文时,对 div#element 的旧引用现在自动指向新元素。那么,我会假设 chrome 足够聪明,可以意识到旧的 div 已经存在,并且不需要重新创建它。只是觉得这很酷,可以分享:)
  • chrome 版本 56.0.2924.87 仍然复制相同的行为,我想从现在开始我将坚持非破坏性插入
【解决方案2】:

您遇到了浏览器重排文档并转储其保存的属性的问题。也称为Layout Thrashing。访问某些 DOM 属性(和/或调用某些 DOM 方法)“将触发浏览器同步计算样式和布局”。这句话来自 Paul Irish 的 Comprehensive list of what forces layout/reflow。虽然innerHTML 不包括在内,但很确定这是罪魁祸首。

在您的示例中,第一个警报的工作原因很明显。第二个有效,因为您要求浏览器去查找元素并再次获取属性。第三个失败是因为您依赖于存储的值(不再存储)。

最简单的解决方法是在使用会强制回流的方法/属性时使用requestAnimationFrame

  window.addEventListener('load', pageInit); 
  function pageInit() {

      // saving instance for later use
      var element = document.querySelector('#element');

      console.log("before:",  element.clientHeight);   // returns 40

      requestAnimationFrame(function() {
        document.querySelector('body').innerHTML += '<p>anything</p>';
      });

      console.log("WITH rAF after:", element.clientHeight);  // returns ~0~ 40!
      
      // out in the wild again
      document.querySelector('body').innerHTML += '<p>anything</p>';
      
      // oh no!
      console.warn("W/O rAF after:", element.clientHeight);  // returns 0 

  }
#element {
   height: 40px;
}
&lt;div id="element"&gt;&lt;/div&gt;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-13
    • 1970-01-01
    • 2010-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    相关资源
    最近更新 更多