当前位置: 首页 > news >正文

AGGrid自定义cellRenderer下tooltipShowMode不生效如何处理?

摘要

AG Grid 中设置tooltipShowMode: 'whenTruncated'后,普通单元格可以只在文本被截断时显示 tooltip;但如果列使用cellRendererSelector或自定义 cell renderer,tooltip 逻辑会换到 renderer 内部。本文基于 AG Grid 官方 issue 回复,整理setTooltip(value, shouldDisplayTooltip)的使用方式。

问题背景

一个常见需求是:表格里有一列描述文本,内容很长时用省略号显示;鼠标悬停时,只在文本被截断的情况下显示完整 tooltip。普通列上可以这样配置:

constgridOptions={tooltipShowMode:"whenTruncated",columnDefs:[{field:"description",tooltipField:"description"}]};

但当这一列改成自定义 renderer,尤其是通过cellRendererSelector动态选择 renderer 时,开发者可能会发现:即使内容没有截断,tooltip 仍然显示;或者tooltipShowMode像没有生效一样。

AG Grid issue #13779 讨论的就是这个现象。官方维护者给出的结论是:这不是单纯的回归 bug。列级别的tooltipShowMode: 'whenTruncated'是基于 Grid 自己渲染的单元格文本判断截断状态;当单元格内容由自定义 renderer 接管后,tooltip 应该由 renderer 通过setTooltip(value, shouldDisplayTooltip)自己注册。

官方回复要点

AG Grid 维护者说明:

  1. tooltipShowMode: 'whenTruncated'依赖 Grid 自身的 cell text。
  2. 自定义 cell renderer,包括由cellRendererSelector选出的 renderer,需要在 renderer 内部调用setTooltip
  3. setTooltip的第二个参数shouldDisplayTooltip是条件函数,适合放“当前内容是否被截断”的判断。
  4. 如果使用enableBrowserTooltips,该条件回调不适用,因为浏览器原生 tooltip 不由 Grid 控制。

所以,正确思路不是继续在 column definition 上堆 tooltip 配置,而是让自定义 renderer 对自己渲染的 DOM 负责。

实现步骤

第一步,给自定义 renderer 的文本元素保留可测量的 DOM 引用。下面示例以 React cell renderer 为例:

import { useLayoutEffect, useRef } from "react"; import type { CustomCellRendererProps } from "ag-grid-react"; type RowData = { id: number; description: string; kind: "short" | "long"; }; export function DescriptionRenderer( props: CustomCellRendererProps<RowData, string> ) { const textRef = useRef<HTMLSpanElement | null>(null); const tooltipValue = String(props.value ?? ""); const { setTooltip } = props; useLayoutEffect(() => { setTooltip(tooltipValue, () => { const el = textRef.current; return !!el && el.scrollWidth > el.clientWidth; }); }, [setTooltip, tooltipValue]); return ( <span ref={textRef} className="cell-ellipsis"> {tooltipValue} </span> ); }

对应样式要真正造成截断,否则scrollWidth > clientWidth永远不会成立:

.cell-ellipsis{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

第二步,在列定义中使用该 renderer。即便全局仍保留tooltipShowMode: 'whenTruncated',自定义 renderer 这列也要靠setTooltip自己判断。

constcolumnDefs=[{field:"id",width:100},{field:"description",cellRendererSelector:params=>{return{component:DescriptionRenderer,params:{emphasis:params.data?.kind==="long"}};}}];

第三步,确认没有启用浏览器原生 tooltip:

constgridOptions={enableBrowserTooltips:false,tooltipShowMode:"whenTruncated",columnDefs};

enableBrowserTooltips: true时,tooltip 行为交给浏览器,AG Grid 文档也说明setTooltip的条件回调不适用。想要“只在截断时显示”,应使用 Grid tooltip 机制。

排查清单

  1. 确认这一列是否使用了cellRenderercellRendererSelector。如果是,优先检查 renderer 内部是否调用了setTooltip
  2. 检查用于测量的元素是否就是实际被截断的元素,而不是外层容器。
  3. 检查 CSS 是否同时具备overflow: hiddentext-overflow: ellipsiswhite-space: nowrap
  4. 确认enableBrowserTooltips没有被设为true
  5. 如果单元格内容会异步变化,确保变化后重新调用setTooltip或触发 renderer 刷新。
  6. 如果 renderer 内有图标、按钮、标签等复杂结构,不要只根据字符串长度判断,要测量真实 DOM 宽度。

版本与注意事项

原 issue 的复现场景是 AG Grid35.3.0和 React。本文示例按当前 AG Grid React 文档中的CustomCellRendererPropssetTooltip机制整理。

示例未在当前工作区绑定对应版本的 AG Grid 依赖进行编译;不同主版本的类型导出位置、renderer props 命名可能略有差异,请以项目所用版本的 API 文档和 IntelliSense 为准。

此外,tooltipShowMode: 'whenTruncated'并不是“所有 tooltip 的全局截断判断器”。它适合 Grid 自己渲染文本的场景;当你接管单元格 DOM 后,就需要接管 tooltip 的显示条件。

总结

这个问题的关键不在于tooltipShowMode写错了,而在于单元格的渲染职责已经从 Grid 转移到了自定义 renderer。普通列可以让 Grid 判断文本是否截断;自定义 renderer 则应使用setTooltip注册 tooltip,并通过shouldDisplayTooltip测量自己的 DOM 是否溢出。这样才能让“只在截断时显示 tooltip”的规则和真实 UI 保持一致。

内容来源说明

  • 官方问答来源:AG Grid GitHub issue #13779,维护者于 2026-06-19 给出解释并关闭 issue。
  • API 核查来源:
    • https://github.com/ag-grid/ag-grid/issues/13779
    • https://www.ag-grid.com/react-data-grid/component-cell-renderer/#dynamic-tooltips
    • https://www.ag-grid.com/react-data-grid/tooltips/
http://www.jsqmd.com/news/1096939/

相关文章:

  • 3步搞定艾尔登法环存档管理:终极角色迁移方案
  • Multisim14丨界面布局异常恢复丨实战排查指南
  • 从零到一:基于STM8的125KHz RFID读卡器实现与曼彻斯特码解析实战
  • ORBSLAM3实战:手把手教你将KITTI数据集适配VIO/IMU模式,并完成精度评估
  • OpenAI API 0613更新深度解析:从GPT-3.5-turbo-16k到函数调用的实战指南
  • 红帽 Linux 零基础完整学习笔记 5
  • 从跑分到洞察:CPU性能评估工具全解析与实战指南
  • Yahoo Finance API:.NET开发者的金融数据革命性解决方案
  • 从编译产物到智能索引:详解gen_compile_commands.py生成compile_commands.json的实战路径
  • 从理论到实践:积极心理学与情绪智慧如何赋能研究生科研与生活
  • 深度解析Untrunc:开源视频修复工具的技术实现与实战应用
  • Python量化交易数据获取的终极解决方案:efinance免费金融数据库完全指南
  • AI智能审核技术架构解析:规则引擎与大模型协同的双重拦截
  • MCP 会取代 API 吗?普通开发者应该怎么理解它?
  • 20美元革命性突破:打造你的专属超声波定向音响系统
  • 深圳亚马逊卖家做GEO,哪家能提升站外AI流量?
  • STM32F407硬件SPI驱动GD25Q32闪存,从接线到读写数据的保姆级教程
  • 通用大模型 vs 行业垂类 vs 自建小模型:差 3 个点,和差23 个点
  • 深度学习图模型的优势、学习与深度学习方法(九十二)
  • 从Latte到StreamingT2V:一文看懂开源视频生成模型的“时空注意力”到底怎么玩的
  • 前端实现打包后自动上传代码到服务器
  • 开源AIOps革命:Keep平台如何重塑企业级智能运维架构
  • Typora 1.8.2 保姆级配置指南:从图片管理到自动保存,一次搞定所有隐藏设置
  • 专业网盘直链下载工具LinkSwift深度解析与实战配置指南
  • Zotero插件生态与高效文献管理实战:从基础配置到进阶工作流
  • 从MicroLogix升级到Micro800?手把手教你用CCW 22.0搞定PCCC通信迁移
  • 3步搞定!在Windows上轻松安装Android应用的终极方案
  • 从理论到实践:基于切比雪夫原型的宽带低通匹配网络设计全解析
  • 电价上涨、芯片交期30周:AI算力狂欢下,制造业的“成本焦虑”何解?
  • JDK系列01:Java环境搭建与JDK版本区别,JDK8/11/17安装、环境变量配置全教程