背景
公司有一个页面,分为三个部分,如下图:
分为三个部分:
第一部分:最左边,分为多个步骤,每一个步骤对应中间部分,和右边内容的滚动高度
第二部分:中间,会根据左边选中的步骤进行切换,同时也会跟进右侧滚动到顶部的模块来进行修改
第三部分:最右边,内容展示,可以进行滚动,与中间和左侧进行联动
整体来说就是,做不切换步骤,中间会变成对应步骤可编辑的内容,右侧会将对应步骤编辑好的内容滚动到页面顶部,反之,如果右侧滚动,也会引起中间内容变化和左侧选中的变化
问题
最近测试那边突然发现,点击左侧导航,右侧出现定位不准确的问题
问题分析
- 我的电脑、测试某些人电脑,无法复现问题,定位是兼容性问题
- 在复现电脑上观察,点击左侧按钮,右侧会正好定位到对应模块的上方一点点,也就差不多1px,只有右侧稍微往上面移动一点点,则左侧就变成正常选中,所以可以断定,根据左侧步骤索引找到的需要滚动的高度,和右侧计算出来滚动的高度不一致
- 左侧选中,是根据右侧当前变红的模块索引,单纯滚动右侧,左侧定位很准确
- 右侧变红,是根据左侧选中项的索引,从而计算右侧应该选中模块的滚动高度scrollTop和当前已经位移的距离offsetTop,从而计算出应该移动的距离,这一步出现问题
尝试
根据左侧索引来计算offsetTop和scrollTop,所以怀疑是否是右侧模块计算offsetTop有兼容性,或者是容器高度计算有兼容性,而且计算滚动距离实际上计算了了两遍,因为渲染页面的时候已经计算出右侧每一个容器都有滚动范围,如下:
_calculateHeight () {// 清空高度数组this.listHeight = []let height = 0this.listHeight.push(height)let divObjs = document.getElementsByClassName('content-box')for (let i = 0; i < divObjs.length; i++) {let item = divObjs[i]if (i == 0 || i == 1 || i == 2 || i == 3) {height += item.clientHeight} else {height += parseInt(item.clientHeight + 10)}this.listHeight.push(height)}}
所以我们不需要根据offsetTop来继续滚动距离,我们只需要取这个数组中对应索引的值即可,这个值就是模块距离父容器的高度
问题还是存在??
满怀信心部署完毕后,发现有问题的电脑,还是有问题
继续找问题
通过打印发现,原来有问题的电脑,打印出来的scrollTop值是浮点型数据类型,这就导致了滚动计算有差异,我们计算滚动距离代码如下:
let jump = document.getElementById(paraIndex)let scrollBox = document.getElementsByClassName('right-body')[0]let distance = scrollBox.scrollTop || document.body.scrollTop// let total = jump.offsetTop - swiperClass// 减去padding值let total = this.listHeight[paraIndex];let step = total / 50if (total > distance) {smoothDown()} else {// 这个地方如果是用浮点型计算,就是会有计算差异的let newTotal = distance - totalstep = newTotal / 50smoothUp()}function smoothDown () {if (distance < total) {// 这个地方如果是用浮点型计算,就是会有计算差异的distance += stepscrollBox.scrollTop = distancesetTimeout(smoothDown, 10)} else {scrollBox.scrollTop = total}}function smoothUp () {if (distance > total) {// 这个地方如果是用浮点型计算,就是会有计算差异的distance -= stepscrollBox.scrollTop = distancesetTimeout(smoothUp, 10)} else {scrollBox.scrollTop = total}}
由上代码可以看出,浮点型计算加减乘除会出现计算不准确的情况
解决
将滚动scrollToop值向上取整,去掉浮点型数据的情况
let distances = scrollBox.scrollTop || document.body.scrollToplet distance = !isNaN(distances) ? Math.ceil(distances) : distances;
注意
大部分浏览器的scrollTop值都是整数,现在已经慢慢出现小数了,所以以后要继续加减乘除计算的时候,可以考虑一下小数的情况
