@radix-ui/react-select 打开下拉框时,页面其他元素的鼠标事件禁用问题

485 阅读3分钟

前言

事实证明radix-ui的生态还有待提升啊……我以为这是个比较常见的问题。
在github的discussions里有看到类似的问题(相关的github disscussions),只是开发者承诺的解决到现在还没有消息。 只能自己改了,希望后面的版本有组件上的支持吧,现在只能自己改了。希望给同样遇到这个问题的人一点帮助。

现象

事情是这样的,我在自己制作一个编辑器时,有这样一个需求:
正常情况下,代码块上方的切换语言等工具栏应该是隐藏的;在鼠标悬浮到代码块上时才会显示。
那当然这种情况下不只是我鼠标放在代码块上需要它显示,我放到下拉框上也得让它保持不消失吧? 这很简单,我给整个组件加一个mouseenter事件等等进行管理就是了……
但是!在我用hover进行管理的时候,发现在下拉框打开的时候,页面上的其他鼠标事件都被屏蔽掉了,不只是这里的hover,你还会发现你自己设置的各种鼠标样式都不生效了,除了这个下拉框之外的所有区域都是这样。
image.png 这还玩个der?!

问题分析

当然,很容易就能发现原因,是因为radix内部有套逻辑,在打开portal的时候,它会默认给你的body上加一个pointer-events: none,这个属性的效果不清楚的直接上mdn看吧不解释了。
其实radix是有想到这一点的,前面也说了他们本来计划的是要改只是没改而已,我前面还用过他们的DropdownMenu组件,它提供了modal属性可以解决这个问题。但是不知道为啥搁置了……

解决方案

说了一大堆,解决方案其实很简单,给它body上的这个属性去掉就是了,这里提供一个hook吧:

const useRadixFix = () => {
  useEffect(() => {
    const observer = new MutationObserver(() => {
      if (getComputedStyle(document.body).pointerEvents === 'none') {
        document.body.style.removeProperty('pointer-events')
      }
    })
    observer.observe(document.body, { attributes: true })
    return () => observer.disconnect()
  }, [])
}

这里配合@radix-ui/react-select的使用可以看下我的实现(组件的其他细节略):

// 略
const [open, setOpen] = useState(false);
useEffect(() => {
  const observer = new MutationObserver(() => {
    if (getComputedStyle(document.body).pointerEvents === 'none') {
      document.body.style.removeProperty('pointer-events');
    }
  });
  if (open) {
    observer.observe(document.body, { attributes: true });
  } else {
    console.log('disconnecting');
    observer.disconnect();
  }
  return () => observer.disconnect();
}, [open]);
return <Select.Root open={open} onOpenChange={setOpen} onValueChange={onChange}>...内部实现略</Select.Root>

最后必须说一下,这种解决方案是很拙劣的,属于是没办法的方案,写了之后可能所有组件都会受到影响(或许有方法束缚,但是也并不优雅),总之还是希望官方早点出组件上的解决吧,遇到问题的也可以去催一催。
后面我可能还会继续使用radix-ui,遇到一些问题也会陆续提出来,有些可能有自己的解决方法有些则依赖大家一起想方法了,有相关需求的请关注我,对radix、tiptap这块有兴趣的也可以多多交流。 That's end