跳转到主内容
Avatar
GrapeWell

transform引发的菜单bug

2025-05-06

在开发项目的时候,有一个需求是,给表格的每一行添加一个右键菜单,在实现的过程中发现点击右键之后,菜单出现的位置并不在鼠标点击的位置,而是发生了偏移

Transform Child Position Issue
Right-click inside this box to open the menu.

组件排查

dom结构大概是这样的,外层父组件包裹了内层的子组件,子组件里面使用了menu

<div class="container">
  <div class="transformed-parent">
    Right-click inside this box to open the menu.
    <div class="menu" id="context-menu">
      <div class="menu-item">Option 1</div>
      <div class="menu-item">Option 2</div>
      <div class="menu-item">Option 3</div>
    </div>
  </div>
</div>

看上去没有什么问题,在接着排查样式的时候,发现了父组件的样式里面增加了transform,而这个菜单组件的定位看了一眼源码之后,恰好使用的fixed,这下就找到问题的原因了。

理论知识

  1. transform会影响坐标系,会构建一个新的坐标系
  2. clientX和clientY返回的是视口的坐标,也就是说相对于浏览器左上角的坐标
  3. 在使用fixed定位后,由于父组件使用了transform,导致定位的参考会改为父组件而不是视口

真相只有一个

右键点击捕获的坐标是相对于视口的,但是呈现的位置是相对于父组件的,也就是说原点位置变了

解决办法

  1. 最简单的方式是在不影响原有功能的基础上去掉transform
  2. 将菜单的定位从fixed改为absolute,但是我这个需求用的第三方组件,所以pass
  3. 移动菜单的位置,不要放在父组件里面,这种方式需要看具体情况,如果是父组件是首页那种,包裹了很多子组件组成了首页,就不太好操作。
<div class="container">
  <div class="transformed-parent">
    Right-click inside this box to open the menu.
  </div>
</div>
<!-- 将菜单放在容器外部 -->
<div class="menu" id="context-menu">
  <div class="menu-item">Option 1</div>
  <div class="menu-item">Option 2</div>
  <div class="menu-item">Option 3</div>
</div>