DOM 重叠的两种结构:
针对上面的两类DOM 重叠,在解决对某一块 DOM 进行点击事件的触发时,采取的策略是完全不同。以下简单展开来说一下:
这一类的重叠就是我们传统上的 DOM 树 Z 轴重叠,当点击 "子节点" 时,是可以通过事件捕获或者事件冒泡,来对每一层节点注册相对应的 “捕获” 或者 “冒泡”,从而实现相对应的事件的触发。
在点击事件上,当我们希望点击事件触发的是父节点,而不是子节点时,但点击的却是子节点,那么我们只需改变 DOM 树 Z轴的顺序就可以实现,通过 z-index: xxx;来实现,z-index 数值越大,那么对应的Z 轴层级就越靠前,从而实现点击的是最靠前的DOM 节点。
这类的节点的重叠是因为我们对这些节点的位置进行了移动,比如通过:float 浮动、position:absolute/relative;transform: translate()等方式进行了位置的移动,从而引起了重叠。这类的DOM节点重叠,是不能实现 事件的“捕获/冒泡”的关联起来的,也不能通过 z-index 的改变层级权值来改变 Z轴的顺序的。
这类DOM节点的重叠, 要实现 事件点击 能点击到下层DOM节点而不是点击移动过来的DOM节点,那么就需要使用到 css 的事件穿透属性 pointer-events: none这样就能使点击事件忽略上层设置了穿透的DOM节点,从而到达下层DOM 节点
1、dom过多,占用过多的内存。2、操纵dom时触发重排重绘,消耗浏览器性能。特别是每一次滚动事件将会让对应 DOM 中的所有元素重新渲染。
3、资源加载阻塞,比如js资源放在body之前、行内script阻塞、css加载会阻塞DOM树渲染(css并不会阻塞DOM树的解析)资源过大阻塞
至于第二个问题,详见 网红问题--前端性能优化(全流程) 或者 js回流和重绘
现在看来,这两点大条件是客观存在的,我们无法改变,所以这时候解决的方案就呼之欲出了,别让浏览器一次性渲染这么多元素,这里通常会对应三种做法来减少元素渲染。
这个方案是大家浏览到页面所常用的,通常在需要展示非常多行的数据时页面会采用分页的做法来分割数据,但在SQL结果集的场景下并不是通用方案,原因是虽然该方法减少了一次性所渲染的行数,但是如果查询的表列数非常多的话,还是有很大概率需要渲染非常多的元素,所以不是一个稳妥的选型。
该方案的解决方法是第一次只渲染所能承受范围内的数据量,当滚动条拖动接近底部(或右部)时,再去追加下一批所需要渲染的元素,该方案也是有一个明显的缺陷在于,无限地滚动下去必然会触及浏览器的性能瓶颈,而且所需要渲染的元素会越来越多,性能迟早会被拖垮。
其实答案已经隐藏在上面两种解决方案里面了,数据分页的方案是一次性渲染固定行数和列数的数据量,缺点是怕一次性的量就逼近上限。无限滚动的方案是想看更多数据的时候再继续渲染,不看就不渲染避免性能浪费,但缺点就在于只要一直触发“继续看”的操作,那么之前遗留的数据将会越来越多导致性能雪崩。
这时候可以把两个方案中和一下,既然在有限的视窗中我们只能看到一部分的数据,那么我们就通过计算可视范围内的单元格,这样就保证了每一次拖动,我们渲染的 DOM 元素始终是可控的,不会像数据分页方案怕一次性渲染过多,也不会发生无限滚动方案中的老数据堆积现象。接下来我们用一张图来表示虚拟滚动的表现形式。
根据图中我们可以看到,无论我们如何滚动,我们可视区域的大小其实是不变的,那么要做到性能最大化就需要尽量少地渲染 DOM 元素,而这个最小值也就是可视范围内需要展示的内容,也就是图中的绿色区块,在可视区域之外的元素均可以不做渲染。
可以通过如下几步来实现虚拟滚动: