实现的功能
- 单例模式,有多个单元格同时存在 tooltip,来回移动仅显示一个
- 非纯文本,提取内容显示
具体实现逻辑:
- 鼠标移入移出控制 tooltip 的显示
文件:packages\components\table\src\table-body\render-helper.ts
return h(
TdWrapper,
{
...
onMouseenter: ($event: MouseEvent) =>
handleCellMouseEnter(
$event,
row,
mergedTooltipOptions as TableOverflowTooltipOptions
),
onMouseleave: handleCellMouseLeave,
},
{
default: () => cellChildren(cellIndex, column, data),
}
)
- handleCellMouseEnter 实现
const handleCellMouseEnter = (
event: MouseEvent,
row: T,
tooltipOptions: TableOverflowTooltipOptions
) => {
...
const cellChild = event.target.querySelector('.cell')
// 通过 createRange 的方式来计算内容宽度是否超出单元格宽度
const range = document.createRange()
range.setStart(cellChild, 0)
range.setEnd(cellChild, cellChild.childNodes.length)
// 获取 range 的宽度和高度
const { width: rangeWidth, height: rangeHeight } =
range.getBoundingClientRect()
// 获取 cellChild 的宽度和高度
const { width: cellChildWidth, height: cellChildHeight } =
cellChild.getBoundingClientRect()
// 获取元素的 padding 值,用于精确计算文本占用空间
const { top, left, right, bottom } = getPadding(cellChild)
const horizontalPadding = left + right
const verticalPadding = top + bottom
// 此处加上padding值,是由于 range 中包含的是所有的子元素,其宽高不包含 cellChild 的 padding 值
if (
isGreaterThan(rangeWidth + horizontalPadding, cellChildWidth) || // 水平方向溢出检测
isGreaterThan(rangeHeight + verticalPadding, cellChildHeight) || // 垂直方向溢出检测
isGreaterThan(cellChild.scrollWidth, cellChildWidth) // 备用检测方法
) {
// 超出的处理逻辑
createTablePopper(
tooltipOptions,
(cell?.innerText || cell?.textContent) ?? '',
row,
column,
cell,
table
)
} else if (removePopper?.trigger === cell) {
...
}
}
此处的水平检测是为了检测单行文本超出的情况,垂直检测是检测多行文本超出的情况
- 超出显示 tooltip
interface prop = {
props: TableOverflowTooltipOptions
popperContent: string
row: T
column: TableColumnCtx<T> | null
trigger: HTMLElement | null
table: Table<DefaultRow>
}
createTablePopper(
tooltipOptions,
(cell?.innerText || cell?.textContent) ?? '',
row,
column,
cell,
table
)
- popperContent
优先获取 innerText,是因为当子元素带有 display: none 时,innerText 会获取不到,当都获取不到时,获取 textContent,textContent 不受 display: none 影响
- Element Plus Tooltip 虚拟触发器模式
{
virtualTriggering: true, // 开启虚拟触发
virtualRef: trigger, // 标识虚拟触发时的触发元素
}
开启虚拟触发,将触发元素绑定成当前 mouseenter 的 cell 元素,内容采用前面传入的 popperContent
- 单实例模式
每次 mouseenter 时,如果存在已经启用的 tooltip,并且 tooltip 的 目标元素是当前元素,则更新内容,否则删除 tooltip,并创建新的 tooltip
// 记录当前的 popper
removePopper = () => {
if (vm.component?.exposed?.onClose) {
vm.component.exposed.onClose()
}
render(null, container)
const currentRemovePopper = removePopper as RemovePopperFn
scrollContainer?.removeEventListener('scroll', currentRemovePopper)
currentRemovePopper.trigger = undefined
currentRemovePopper.vm = undefined
removePopper = null
}
removePopper.trigger = trigger ?? undefined
removePopper.vm = vm
// 判断是否是当前触发元素
if (removePopper?.trigger === trigger) {
const comp = removePopper.vm?.component
merge(comp?.props, mergedProps)
if (comp && tableOverflowTooltipProps.slotContent) {
comp.slots.content = () => [tableOverflowTooltipProps.slotContent]
}
return
}
removePopper?.()