背景

公司有一个页面,分为三个部分,如下图:
滚动值出现小数导致滚动定位失败 - 图1
分为三个部分:
第一部分:最左边,分为多个步骤,每一个步骤对应中间部分,和右边内容的滚动高度
第二部分:中间,会根据左边选中的步骤进行切换,同时也会跟进右侧滚动到顶部的模块来进行修改
第三部分:最右边,内容展示,可以进行滚动,与中间和左侧进行联动
整体来说就是,做不切换步骤,中间会变成对应步骤可编辑的内容,右侧会将对应步骤编辑好的内容滚动到页面顶部,反之,如果右侧滚动,也会引起中间内容变化和左侧选中的变化

问题

最近测试那边突然发现,点击左侧导航,右侧出现定位不准确的问题

问题分析

  1. 我的电脑、测试某些人电脑,无法复现问题,定位是兼容性问题
  2. 在复现电脑上观察,点击左侧按钮,右侧会正好定位到对应模块的上方一点点,也就差不多1px,只有右侧稍微往上面移动一点点,则左侧就变成正常选中,所以可以断定,根据左侧步骤索引找到的需要滚动的高度,和右侧计算出来滚动的高度不一致
  3. 左侧选中,是根据右侧当前变红的模块索引,单纯滚动右侧,左侧定位很准确
  4. 右侧变红,是根据左侧选中项的索引,从而计算右侧应该选中模块的滚动高度scrollTop和当前已经位移的距离offsetTop,从而计算出应该移动的距离,这一步出现问题

尝试

根据左侧索引来计算offsetTop和scrollTop,所以怀疑是否是右侧模块计算offsetTop有兼容性,或者是容器高度计算有兼容性,而且计算滚动距离实际上计算了了两遍,因为渲染页面的时候已经计算出右侧每一个容器都有滚动范围,如下:

  1. _calculateHeight () {
  2. // 清空高度数组
  3. this.listHeight = []
  4. let height = 0
  5. this.listHeight.push(height)
  6. let divObjs = document.getElementsByClassName('content-box')
  7. for (let i = 0; i < divObjs.length; i++) {
  8. let item = divObjs[i]
  9. if (i == 0 || i == 1 || i == 2 || i == 3) {
  10. height += item.clientHeight
  11. } else {
  12. height += parseInt(item.clientHeight + 10)
  13. }
  14. this.listHeight.push(height)
  15. }
  16. }

所以我们不需要根据offsetTop来继续滚动距离,我们只需要取这个数组中对应索引的值即可,这个值就是模块距离父容器的高度

问题还是存在??

满怀信心部署完毕后,发现有问题的电脑,还是有问题

继续找问题

通过打印发现,原来有问题的电脑,打印出来的scrollTop值是浮点型数据类型,这就导致了滚动计算有差异,我们计算滚动距离代码如下:

  1. let jump = document.getElementById(paraIndex)
  2. let scrollBox = document.getElementsByClassName('right-body')[0]
  3. let distance = scrollBox.scrollTop || document.body.scrollTop
  4. // let total = jump.offsetTop - swiperClass
  5. // 减去padding值
  6. let total = this.listHeight[paraIndex];
  7. let step = total / 50
  8. if (total > distance) {
  9. smoothDown()
  10. } else {
  11. // 这个地方如果是用浮点型计算,就是会有计算差异的
  12. let newTotal = distance - total
  13. step = newTotal / 50
  14. smoothUp()
  15. }
  16. function smoothDown () {
  17. if (distance < total) {
  18. // 这个地方如果是用浮点型计算,就是会有计算差异的
  19. distance += step
  20. scrollBox.scrollTop = distance
  21. setTimeout(smoothDown, 10)
  22. } else {
  23. scrollBox.scrollTop = total
  24. }
  25. }
  26. function smoothUp () {
  27. if (distance > total) {
  28. // 这个地方如果是用浮点型计算,就是会有计算差异的
  29. distance -= step
  30. scrollBox.scrollTop = distance
  31. setTimeout(smoothUp, 10)
  32. } else {
  33. scrollBox.scrollTop = total
  34. }
  35. }

由上代码可以看出,浮点型计算加减乘除会出现计算不准确的情况

解决

将滚动scrollToop值向上取整,去掉浮点型数据的情况

  1. let distances = scrollBox.scrollTop || document.body.scrollTop
  2. let distance = !isNaN(distances) ? Math.ceil(distances) : distances;

注意

大部分浏览器的scrollTop值都是整数,现在已经慢慢出现小数了,所以以后要继续加减乘除计算的时候,可以考虑一下小数的情况