You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React Tailwind应用中点击Grammarly按钮触发模态框关闭的技术问询

Fix: Grammarly Extension Closes React + Tailwind Modal on Button Click

我之前在开发React+Tailwind的项目时也碰到过一模一样的问题,折腾了半天终于摸清楚了根源和可行的解决方案,分享给你:

问题根源

Grammarly网页扩展会在页面中动态注入它的工具栏元素,当你点击Grammarly的按钮时,这个点击事件会冒泡到document/body层面。而大多数React模态框的关闭逻辑(比如点击外部关闭)是监听document的点击事件,然后判断点击目标是否在模态框容器之外——Grammarly的元素显然属于模态框外部,所以就误触发了关闭逻辑。

解决方案

1. 优化模态框的外部点击判断逻辑(最推荐)

修改你的点击关闭监听,专门排除Grammarly的元素。具体来说,就是在判断点击是否在模态框外的同时,检查点击目标是否属于Grammarly扩展的DOM节点:

import { useRef, useEffect, useState } from 'react';

function YourModal() {
  const [isModalOpen, setIsModalOpen] = useState(true);
  const modalRef = useRef(null);

  useEffect(() => {
    const handleClickOutside = (event) => {
      // 检查点击目标是否是Grammarly的元素
      const isGrammarlyClick = event.target.closest('.grammarly-extension');
      // 只有当点击不在模态框内,且不是Grammarly的元素时才关闭
      if (modalRef.current && !modalRef.current.contains(event.target) && !isGrammarlyClick) {
        setIsModalOpen(false);
      }
    };

    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, []);

  return (
    <div className="fixed inset-0 flex items-center justify-center bg-black/50">
      <div ref={modalRef} className="bg-white p-6 rounded-lg">
        {/* 模态框内容,比如带Grammarly的输入框 */}
        <input type="text" className="border p-2 w-full" placeholder="Type something..." />
        <button onClick={() => setIsModalOpen(false)} className="mt-4 px-4 py-2 bg-red-500 text-white">
          Close
        </button>
      </div>
    </div>
  );
}

2. 阻止Grammarly元素的事件冒泡

另一种思路是找到页面上的Grammarly元素,阻止它的点击事件向上冒泡,这样就不会触发document层面的关闭监听:

useEffect(() => {
  // 当模态框打开时,给Grammarly元素添加阻止冒泡的监听
  if (!isModalOpen) return;

  const grammarlyElements = document.querySelectorAll('.grammarly-extension');
  const stopPropagation = (e) => e.stopPropagation();

  grammarlyElements.forEach(el => el.addEventListener('mousedown', stopPropagation));

  // 组件卸载或模态框关闭时移除监听
  return () => {
    grammarlyElements.forEach(el => el.removeEventListener('mousedown', stopPropagation));
  };
}, [isModalOpen]);

3. 针对第三方Tailwind组件库的适配

如果你用的是Headless UI、DaisyUI这类Tailwind组件库的模态框,可以直接在组件的关闭回调里过滤Grammarly的点击:

以Headless UI的Dialog为例:

import { Dialog } from '@headlessui/react';

function YourModal() {
  const [isModalOpen, setIsModalOpen] = useState(true);

  return (
    <Dialog 
      open={isModalOpen} 
      onClose={(event) => {
        // 过滤Grammarly的点击
        const isGrammarlyClick = event.target.closest('.grammarly-extension');
        if (!isGrammarlyClick) setIsModalOpen(false);
      }}
      className="fixed inset-0 flex items-center justify-center"
    >
      <Dialog.Panel className="bg-white p-6 rounded-lg">
        {/* 模态框内容 */}
        <input type="text" className="border p-2 w-full" />
      </Dialog.Panel>
    </Dialog>
  );
}

注意事项

  • Grammarly的DOM类名可能会有变化,如果你发现.grammarly-extension不生效,可以打开浏览器控制台,找到Grammarly的元素,查看它的类名或属性来调整判断逻辑。
  • 如果是动态加载的Grammarly元素(比如模态框打开后才注入),可以用MutationObserver来监听DOM变化,实时给新添加的Grammarly元素绑定阻止冒泡的事件。

内容的提问来源于stack exchange,提问作者Ahsaan shuja

火山引擎 最新活动