如何在 Chrome 扩展内容脚本中正确使用 Floating UI

发布时间 - 2026-01-28 00:00:00    点击率:

本文详解如何绕过 esm 导入限制,在 chrome 扩展 content script 中成功集成 floating ui,实现动态 tooltip 定位,并提供可直接运行的模块化配置与调用方案。

在 Chrome 扩展中使用 Floating UI 构建响应式 tooltip 时,直接引入其 ESM 版本(如 @floating-ui/dom/+esm)会触发 Uncaught SyntaxError: Cannot use import statement outside a module 错误——这是因为 Chrome 内容脚本默认不支持顶层 import 语句,即使 manifest 中声明 "type": "module",若依赖的第三方库自身以裸 import 形式发布(如 Floating UI 的 ESM CDN 链接),仍无法被直接 import 加载。

✅ 正确解法是:改用 UMD(Universal Module Definition)构建版本,通过

✅ 推荐配置步骤

  1. 在 content_scripts 中按顺序加载 UMD 脚本(非 ESM)
    修改 manifest.json,移除 "type": "module"(该字段对 js 数组中的多个脚本不生效),改用显式
"content_scripts": [
  {
    "matches": ["*://*/*"],
    "js": ["content.js"],
    "css": ["tooltips.css"]
  }
]
  1. 在 content.js 开头动态加载 UMD 版本(CDN 推荐)
    使用 Floating UI 官方提供的 UMD 构建(v1.x 支持 UMD):
// content.js —— 首先确保 Floating UI 全局可用
if (!window.FloatingUICore || !window.FloatingUIDOM) {
  const scriptCore = document.createElement('script');
  scriptCore.src = 'https://unpkg.com/@floating-ui/core@1.6.5/dist/floating-ui.core.umd.js';
  scriptCore.async = false;

  const scriptDOM = document.createElement('script');
  scriptDOM.src = 'https://unpkg.com/@floating-ui/dom@1.6.11/dist/floating-ui.dom.umd.js';
  scriptDOM.async = false;

  document.head.append(scriptCore, scriptDOM);
}

// 等待脚本加载完成后再执行 tooltip 逻辑(推荐 Promise 化或使用 load 事件)
Promise.all([
  new Promise(r => scriptCore.onload = r),
  new Promise(r => scriptDOM.onload = r)
]).then(() => {
  // ✅ 此时 window.FloatingUICore / window.FloatingUIDOM 已就绪
  initTooltips();
});
  1. 创建 tooltip 并调用 computePosition
    示例:为页面中所有 .tooltip-trigger 元素添加顶部 tooltip:
function initTooltips() {
  const triggers = document.querySelectorAll('.tooltip-trigger');
  triggers.forEach(trigger => {
    const tooltip = document.createElement('div');
    tooltip.className = 'floating-tooltip';
    tooltip.textContent = 'This is a tooltip!';

    const arrow = document.createElement('div');
    arrow.className = 'tooltip-arrow';
    tooltip.appendChild(arrow);

    document.body.appendChild(tooltip);

    // 关键:使用挂载到 window 的 UMD 实例
    function updatePosition() {
      window.FloatingUIDOM.computePosition(trigger, tooltip, {
        placement: 'top',
        middleware: [
          window.FloatingUICore.offset(6),
          window.FloatingUIDOM.flip(),
          window.FloatingUIDOM.shift({ padding: 5 }),
          window.FloatingUIDOM.arrow({ element: arrow }),
        ],
      }).then(({ x, y, middlewareData, placement }) => {
        Object.assign(tooltip.style, {
          left: `${x}px`,
          top: `${y}px`,
        });

        // 处理箭头定位(需配合 CSS transform)
        const { x: arrowX, y: arrowY } = middlewareData.arrow || {};
        if (arrowX != null && arrowY != null) {
          Object.assign(arrow.style, {
            left: `${arrowX}px`,
            top: `${arrowY}px`,
          });
        }
      });
    }

    // 初始定位 + 监听滚动/resize
    updatePosition();
    window.addEventListener('scroll', updatePosition, { passive: true });
    window.addEventListener('resize', updatePosition);
  });
}

⚠️ 注意事项

  • ❌ 不要将 floating-esm.js 直接写入 manifest.json 的 js 数组并设 "type": "module" —— C

    hrome 不允许 content script 同时加载多个模块脚本且跨脚本共享 import 作用域。
  • ✅ UMD 版本(.umd.js)是专为浏览器全局环境设计的,无 import/export,天然兼容 content script。
  • ? 建议锁定 CDN 版本号(如 @1.6.11),避免因 Floating UI 主版本升级(如 v2+ 移除 UMD)导致扩展失效。
  • ? 若需更小体积或定制构建,可自行用 Vite/Webpack 打包 Floating UI 为 IIFE 格式并内联到扩展中。

通过以上方式,你即可在 Chrome 扩展中稳定、高效地利用 Floating UI 实现像素级精准的 tooltip 定位,兼顾性能、兼容性与可维护性。


# css  # js  # json  # vite  # 浏览器  # app  # win  # cdn  # 作用域  # red  # chrome  # webpack 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Laravel Facade的原理是什么_深入理解Laravel门面及其工作机制  Laravel用户密码怎么加密_Laravel Hash门面使用教程  如何用PHP工具快速搭建高效网站?  html5audio标签播放结束怎么触发事件_onended回调方法【教程】  Laravel辅助函数有哪些_Laravel Helpers常用助手函数大全  宙斯浏览器视频悬浮窗怎么开启 边看视频边操作其他应用教程  如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?  Laravel如何实现API速率限制?(Rate Limiting教程)  Laravel怎么发送邮件_Laravel Mail类SMTP配置教程  使用Dockerfile构建java web环境  高端网站建设与定制开发一站式解决方案 中企动力  Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】  jQuery 常见小例汇总  如何快速生成凡客建站的专业级图册?  网站制作免费,什么网站能看正片电影?  黑客如何通过漏洞一步步攻陷网站服务器?  BootStrap整体框架之基础布局组件  Linux系统命令中screen命令详解  网站建设整体流程解析,建站其实很容易!  Laravel用户认证怎么做_Laravel Breeze脚手架快速实现登录注册功能  装修招标网站设计制作流程,装修招标流程?  如何用免费手机建站系统零基础打造专业网站?  北京网站制作公司哪家好一点,北京租房网站有哪些?  Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程  Laravel任务队列怎么用_Laravel Queues异步处理任务提升应用性能  js代码实现下拉菜单【推荐】  JavaScript如何实现类型判断_typeof和instanceof有什么区别  Laravel如何理解并使用服务容器(Service Container)_Laravel依赖注入与容器绑定说明  详解Nginx + Tomcat 反向代理 负载均衡 集群 部署指南  java ZXing生成二维码及条码实例分享  Laravel如何发送邮件和通知_Laravel邮件与通知系统发送步骤  如何在不使用负向后查找的情况下匹配特定条件前的换行符  jQuery validate插件功能与用法详解  高防服务器如何保障网站安全无虞?  Laravel如何实现图片防盗链功能_Laravel中间件验证Referer来源请求【方案】  微信小程序 配置文件详细介绍  JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)  laravel怎么配置和使用PHP-FPM来优化性能_laravel PHP-FPM配置与性能优化方法  Laravel如何生成URL和重定向?(路由助手函数)  微信小程序 input输入框控件详解及实例(多种示例)  做企业网站制作流程,企业网站制作基本流程有哪些?  Laravel的辅助函数有哪些_Laravel常用Helpers函数提高开发效率  如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?  ,在苏州找工作,上哪个网站比较好?  猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?  使用spring连接及操作mongodb3.0实例  如何用AI帮你把自己的生活经历写成一个有趣的故事?  laravel怎么实现图片的压缩和裁剪_laravel图片压缩与裁剪方法  Linux安全能力提升路径_长期防护思维说明【指导】  Laravel怎么配置自定义表前缀_Laravel数据库迁移与Eloquent表名映射【步骤】