问题表现
就在我正准备录制下个系列视频的时候,发现何方的个人小站-Markdown 工具页面下面出现了大面积的空白(如下图 1)。
这是什么情况?我第一反应可能是我忘了给代码或预览的窗口添加 overflow: auto
导致元素的整个长度直接推在了元素流里面。
原因分析
但当我看了代码和界面以后发现是对的(如下图 2),并没有忘记添加 overflow: auto
。而且我查看 body
元素的时候发现高度是正常的,并没有包含下面空白区域的高度。这时候我感觉可能是 Edge 浏览器的 Bug。于是我试了 Chrome、Firefox、Safari 以及 Linux 和 Windows 中的各种浏览器,都能复现这个问题。确定了不是浏览器的 Bug。
于是我又查看了 html
元素的高度,发现在 html
高度竟然是包含了空白区域高度的(如下图 3)。到这个时候我就确定一定是有什么元素浮动到了元素流里面,而且这个元素一定是不可见或非常小的,这就只能逐个排查可能的元素。
在我排查过程中发现如果把预览关掉或不预览 HTML 渲染结果(见下图 4),只预览 HTML 代码的情况下是没有空白问题的。这就可以确定是 Markdown 的渲染结果出现了问题。但渲染结果是 Markdown 解析器生成的,这又增大了排查的难度。
这个时候我突然想起来,浏览器的开发都工具是可以查看页面 3D 结构的。在复合图层里面并没有看出什么问题(见下图 5)。但当我查看 3D DOM 结构(见下图 6)并把视频拉到最下面是发现了问题——在最下面出现了几条东西。点击 3D 视图可以跳转到对应的 dom 结果里面,原来导致页面空白的罪魁祸首是公式(见下图 7)。
问题原因
我在 Markdown 生成 HTML 的时候使用的是 katex 来渲染公式,默认情况下它会生成两套 DOM 结构,一套 mathml
用于读屏软件或搜索引擎等识别这是公式、另一套是 html
它用于渲染显示我们看到的公式。
为了不让 mathml
和 html
同时显示出现,katex
把 mathml
设置成了 position: absolute
且只有一个像素大小。
.katex-mathml {
clip: rect(1px, 1px, 1px, 1px);
border: 0;
height: 1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
问题就出在了 position: absolute
上面,因为我们的预览容器是 overflow: auto
的。在可滚动的容器里面如果存在绝对定位的元素,这个元素就会脱离容器出现在主元素流里面。也就是说如果容器没有溢出滚动的时候这个元素该出现在哪儿,在有溢出滚动的时候它还在那儿。这就造成了它前面出现的空白。
解决方案
知道了问题原因也就好解决了,三种改动较小的方案1:
- 把 katex 生成
htmlAndMathml
改为html
。 - 把类
.katex-mathml
的position: absolute
改成position: fixed
。 - 把
.katex-mathml
的父类的position
改成非static
。