# zoom引发的tooltip错位

* 抖音：[https://v.douyin.com/kPqcyfW/](https://v.douyin.com/kPqcyfW/)
* B站：[https://www.bilibili.com/video/BV1UM411F79q/](https://www.bilibili.com/video/BV1UM411F79q/)

> **Demo预览：**
>
> [https://sunzsh.github.io/vue-demos/#/tooltipzoom](https://sunzsh.github.io/vue-demos/#/tooltipzoom)
>
> **代码：**
>
> [https://github.com/sunzsh/vue-el-demo/blob/master/src/views/tooltipzoom.vue](https://github.com/sunzsh/vue-el-demo/blob/master/src/views/tooltipzoom.vue)

这个bug的两个解决思路挺好玩哈

我们先来看问题

这个小伙伴说啊

他只要给body设置这个zoom属性（样式）

然后再拉动滚动条啊

这个tooltip就会错位

大家看滚动的越远

错位的也就越远

大家看下边这个就错到这来了

为了方便调试啊

我把这个代码放到了我本地的demo里了

就是这样看看他也是错位的

我们看下边错位的更多一样的

然后啊我们先去快速的定位一下问题

我们先点到tooltip里

然后点到这个main里

然后这里边我之前再给大家介绍过

这里边有个updatePopper方法

这updatePopper在哪呢

其实在

mixins就是这个proper里边

我们再点到这个proper

然后这里边呢

我们再搜一下updatePopper

在这里

然后这里边它又调了popperJS的update方法

popperJS在哪呢

我们再来搜一下

它在这new了一个popperJS

这个popperJS呢就在上面声明的在这里

require popper然后我们再去看这个popper文件

popper文件里边我们再来搜

update方法

然后这个update的方法里边

关于位置的就是这个offset

我们再看这个offset方法

大家看这里边

他最终会把这个变量的left呀

top呀都赋值给这个结果然后返回

那我们来看这个变量哪来的

在这里他调了这个方法得到这个变量

我们再来点进去

好，这个方法就很重要了

因为这个bug的

根本原因出在了这个方法上

为什么呢

因为这个方法会拿到

我们这个button

元素在整个屏幕的一个位置

然后呢他再去定位这个popper

应该放在什么位置

但是呢如果我们设置了zoom

他这个方法拿到的数据就不准了

我们可以来验证一下，怎么验证呢？

首先啊我们先选中上面这个

拿上面这个button来举例子

选中他以后呢

document.documentElement.scrollTop

大家看 现在是

然后呢我把它设置成1，让它滚动一点

（1个像素）

之后呢我们再来看button的位置

点getBondingClientRect

然后呢.top（我们就看TOP值）

大看现在是这个数

然后我们再把滚动条滚动到头（0的位置）

回到0的位置我们再来看一下.top

我们来看一下相差了多少

用这个数减去这个数

那为什么不是1呢

其实这就跟我们设置的这个Zoom

等于1.2有关系

我们可以算一下

我们用1/1.2就是这个数0.83333几

我们可以再这样验证

我们把这个1.2改成1.

刷新一下

然后啊我们滚到顶先把这个button

打印出来

它的帮顶top值打印出来

这个大概是103点几

然后呢

我们再让滚动条往下滚一个像素

然后我们再打一下button的这个bounding值

大家看，是102点几

我们再来算一下他们差多少

减去

这个值

他看是0.

我们再用1除以1.

大家看看

就是0.

所以啊

出现这个错位的原因就是当我们

设置了zoom之后

再滚动的时候

再去获取这个绑定值

他就不准了

找到了原因

我们怎么去解决这个问题呢

我们再回头看一下这个方法

他刚才调的就是这个方法

然后他在这里调的我们刚才用的这个

HTMLElement的原生的这个方法

外边这个方法

我们肯定是没办法重写了

我们要重写的话呢

只能考虑重写里边的这个方法

这个方法就是HTML原生的了

我给大家分享两个方案

希望这两个方案能对大家以后解决

其他bug有所帮助哈

第一个方案呢

就是我们全局来改HTMLElement

里边的prototype的

这个BoundingClientRect方法

我们首先把HTML原声的这个boundingClientRect

的备份到一个bak方法备份起来

然后呢

我们来重新指定它的这个rect方法

在这里呢我们先去调用它原生的方法

（就是我们刚才备份那个去掉）

把这个this传进去拿到一个结果

这个时候注意我们要在这判断一下

因为

我们设置的zoom是针对body设置的

所以如果我们拿到是documentElement

其实这个时候

他拿到的这个位置是准确的

只有它里边的元素

在获取这个邦丁clientRect的时候

是不准的

所以我们

外边这个就不要再去用zoom去较准了

为什么呢

我们来看一下啊这个方法

他在调用的时候啊

他不止在调用当前这个元素

他还在调用这个parent

那这个parent是什么呢

我们再往上找

他在这调用呢

大家看这个就是那个button

这个popper是那个弹出的那个层（黑色的）

大家看这个方法里边

它是取这个offsetParent，同时取到的

offsetParent如果是body

它会返回（强制返回）一个documentElement

我们来试一下

我们先让它弹出

然后呢我们检查这个元素

为了不让它关闭

咱们先禁用一下Javascript

鼠标不要离开那里啊

直接输入disable Javascript

然后呢我们这时候移开就行了

这时候再选中它

注意选中这个tooltip了

然后我们 $0.offsetParent

大看是body

所以回到这

如果是body他强制返回了一个element

也就是说我们重写的这个方法呀

被用来计算这个button的位置

他可能也会用来计算document的位置

但document的位置呢

因为他没有外层没有缩放

所以这个位置是准的

我们就不用再做校准了啊

然后再往下我们拿到zoom

zoom就是我们设那个一点几：1.6、1.

然后呢

分别取到纵向和横向的滚动条的位置

然后根据这个zoom计算偏差

然后重新返回一个DOMRect

把这个偏差加上左右（都去解决一下）

我们再来试一下啊

刷新一下

好大家看！这个时候滚动就准确了

我们再看下边

大家看都没有问题了

那第二个方法是什么呢

我们先把它禁用掉

第二个方法我们就不改prototype的这个

rect方法了

我们呀写一个vue的指令

然后在指令里拿到这个element的

之后啊

我们单独针对这个对象去改他的

这个rect的方法

先去把原来的备份一下

然后思路跟刚才是一样的

然后最终用的时候呢我们在页面上

找到这个button

这个button现在位置是不准确的

我先保存试一下

刷新大家看是有偏差的

然后给这个button加上v

然后这个指令

刷新

大看这样就准了

下边的还不准

我们再来看一下

下边的就不知道偏哪去了

这个关掉

好，在下边…

然后呢我们把下边这个按钮也加一个

好刷新

大家看，下边的也准了
