JavaScript 如何检测元素何时添加到 DOM?

JavaScript 如何检测元素何时添加到 Dom?这在 Web 网页中,尤其是针对不能操控源码的网站,是极为常见的问题。那么如何有效检测元素已成功渲染呢,本文旨在对此问题进行探讨。

需求背景

清风明月阁 ,是基于 Wiki.js 搭建的 Web 应用;其内容默认的排版(UI )并不是很喜欢;而 赫蹏 (专为中文网页内容设计的排版样式增强。它基于通行的中文排版规范,可为网站的读者带来更好的内容阅读体验)这种风格,令我更觉得舒服。于是就想为 清风明月阁 接入下 赫hè蹏tí

如何使用赫蹏?

项目地址: https://github.com/sivan/heti ,其使用方法如下:

  1. 在页面的 </head> 标签前中引入 heti.css 样式文件:
        <link rel="stylesheet" href="//unpkg.com/heti/umd/heti.min.css">
    
  2. 在要作用的容器元素上增加 class="heti" 的类名即可:
        <article class="entry heti">
          <h1>我的世界观</h1>
          <p>有钱人的生活就是这么朴实无华,且枯燥。</p>
          ……
        </article>
    

备注:赫蹏是正文区域的样式增强,不是normalize.cssCSS Reset的替代。因此不建议将它作用在根标签(如 <body> 或 <div class="container">)上。

而 Wiki.js 跟 Ghost 一样,有强大的后台,支持插入 Css / JS 代码于 head 或 body;那问题就来了,何时为文章内容加 heti 这个类呢?

JavaScript 如何检测元素何时添加到 Dom?

当然,可以很容易想到以下方案:

window.onload = () => {}

setTimeout(function(){}, 100)

但是,这两种方案,可以有效,但不够干净,不能得知元素是否已被渲染的准确时机。截至 2018 年,您应该使用 MutationObserver 来检测元素何时添加到 DOM。现在,所有现代浏览器(Chrome 26+、Firefox 14+、IE11、Edge、Opera 15+ 等)都广泛支持 MutationObservers。此外,将元素添加到 DOM 后,您将能够检索其实际尺寸。于是乎,为 清风明月阁 添加「赫蹏」,其体验就能变得非常自然,没有中间不符合预期的变化。

<link rel="stylesheet" href="//unpkg.com/heti/umd/heti.min.css">
<script type="text/javascript">
  window.addEventListener('DOMContentLoaded', (event) => {
    var observer = new MutationObserver(function(mutations) {
      var contents = document.querySelector('.contents')
      if (document.contains(contents)) {
        contents.classList.add('heti', 'heti--classic');
        observer.disconnect();
      }
    });
    observer.observe(document, {attributes: false, childList: true, characterData: false, subtree:true});
  }); 
</script>

另外,值得一提的是,在此处使用了 DOMContentLoaded 事件(当初始 HTML 文档完全加载和解析时触发,无需等待样式表、图像和子框架完成加载),在其成功回调中,再调用 MutationObservers,算是细微处优化处理。