背景:
客户喜欢把大段对话粘贴到系统, 表格里要 tooltip 显示这些对话全文, 而 vxe-table(项目里用的版本是 ^3.7.5) 仅设置了 tooltip 样式 max-width 600 , 导致 tooltip 内部没法滚动, 大段对话也显示不全, 影响客户使用
探索:
-
全局样式给 tooltip 加上 max-height 和 overflow-y: auto , 显示滚动条了, 但是…没法滚动, 滚动一下表格就直接滚动 + 关闭 tooltip 了
-
github 找 vxe-table 源码看, 发现它是监听 mousewheel , 尝试在页面上选中 tooltip , addEventListener mousewheel 然后 e.stopPropagation() , 成功只滚动 tooltip 内容 + 不滚动表格
-
根据上一步的发现, 想办法给 tooltip element 加上 onmousewheel 和 stopPropagation , 在 api 中找到 cell-mouseleave 事件, 测试发现鼠标离开单元格进入单元格触发的 tooltip 中也会触发此事件, 因此监听此事件, 在此事件回调中, 选中 tooltip , 加上
onmousewheel = e => e.stopPropagation();
, 成功实现"tooltip 内滚动不触发表格的滚动" -
发现新问题 — 切换不同单元格触发 tooltip , tooltip 内的滚动条位置不会复位/重置
-
为解决新问题, 尝试:
-
直接在 cell mouseleave 时, 重置 tooltip scrollTop ;
不可行, 这样会导致鼠标离开 tooltip 回到 cell 时 tooltip 滚动条也重置了, 理想情况应该是彻底离开 cell 关闭 tooltip 时重置 tooltip, 保证进入新 cell 打开 tooltip 时滚动条重置
-
如果 cell mouseleave 时, 能判断 tooltip 即将消失, 则可以在此时重置 tooltip 的 scrollTop;
不可行, 离开旧 cell 进入新 cell 时, 会触发 cell mouseleave , 但此时 vxe-table 实例数据的 $table.tooltipStore.visible 和 $refs.tooltip.visible 都为 true , 根本没办法判断旧 cell 的 tooltip 是否关闭
-
尝试 cell mouseenter
可行, 进入新 cell 时, $table.tooltipStore.visible 为 true , $refs.tooltip.visible 为 false , 说明此时 table 设置了要显示 tooltip 但尚未生效, 因此可以在此时重置 tooltip scrollTop
-
在 cell mouseenter 重置 tooltip scrollTop 未生效?
搜索发现, 这是因为 tooltip 显示出来之前, 是 display none 状态, 此时即便设置 scrollTop 也不生效; 因此, 改为先设置 tooltip visibility hidden , setTimeout 等 tooltip 显示之后再重置 scrollTop + 恢复 visibility visible
(setTimeout 的时间, 试了下, 500 不行, 600 可以, 看了下源码, 没主动设置 table 的 tooltip 的 enterDelay 的话, 就会使用 tooltip 的默认 enterDelay: 500)
-
最后, 为了避免快速在不同 cell 移动触发 tooltip 时 timeout 不合时宜地执行, 需要暂存 timeout id , 每次打开新 tooltip 时清除旧 timeout 重置 timeout
-
具体代码:
// template
<vxe-table
//...
@cell-mouseenter="handleCellMouseEnter"
@cell-mouseleave="handleCellMouseLeave"
>...</vxe-table>
// variables
// 延时显示 vxe-table tooltip 计时器 id
timeout_showTooltip: null
// methods
// vxe-table 的 tooltip 加上滚动条后, tooltip 关闭再打开, 滚动条仍然在之前 tooltip 滚动到的位置
// 因此给 tooltip 的滚动主体加上 scrollTop 置空
handleCellMouseEnter({ $table }) {
// 取 tooltip 实例
const $tooltip = $table && $table.$refs && $table.$refs.tooltip;
if ($tooltip && $tooltip.$el) {
// 如果此时设置了 tooltip 要显示但尚未生效, 则延时显示 tooltip , 避免 tooltip 内滚动条还未重置
// 注意, 此处是"鼠标移入单元格, 即将显示 tooltip"场景, 在此处处理 scrollTop 重置
// 不在 mouseleave 处理, 是因为 mouseleave 时这两个数据不准, 从一个有 tooltip 的 cell 移动到下一个, 这两个数据仍然是 true , 因此无法做到"鼠标移出单元格, tooltip 即将关闭, 此时重置 scrollTop"
if ($table.tooltipStore.visible && !$tooltip.visible) {
// 清除旧延时, 避免快速切换 tooltip 时, tooltip 的滚动条还未重置就显示出来了
if (this.timeout_showTooltip) {
clearTimeout(this.timeout_showTooltip);
}
// 延时显示 tooltip
this.delayShowTooltip($tooltip.$el);
}
}
},
// 延时显示 vxe-table tooltip
// 因为 tooltipEl 为 display none 状态时设置 scrollTop 无效, 所以先通过 visibility 隐藏 tooltip , 重置 scrollTop 完毕后再恢复 visible
delayShowTooltip(tooltipEl) {
// 取 tooltip 滚动主体元素
const tooltipContentEl = tooltipEl.querySelector('.vxe-table--tooltip-content');
if (tooltipContentEl) {
tooltipEl.style.visibility = 'hidden';
this.timeout_showTooltip = setTimeout(() => {
tooltipContentEl.scrollTop = 0;
tooltipEl.style.visibility = 'visible';
this.timeout_showTooltip = null;
}, 600); // 延时 600 是因为 tooltip 默认 enterDelay 为 500 , 低于 500 会因为 tooltip el 尚未 display , 导致 scrollTop 设置仍然无效
}
},
// vxe-table 的 tooltip , 存在"内部滚动条滚动时触发 table 的 mousewheel 事件, 导致 table 主动关闭 tooltip"问题
// 因此给 tooltip 滚动主体加上 mousewheel stopPropagation , 停止向上传递 mousewheel 事件给 table
handleCellMouseLeave({ $table }) {
// 取 tooltip 实例
const $tooltip = $table && $table.$refs && $table.$refs.tooltip;
if ($tooltip && $tooltip.$el) {
// 取 tooltip 滚动主体元素
const tooltipContentEl = $tooltip.$el.querySelector('.vxe-table--tooltip-content');
if (tooltipContentEl && !tooltipContentEl.onmousewheel) {
tooltipContentEl.onmousewheel = e => e.stopPropagation();
}
}
}
// css
.vxe-table--tooltip-wrapper .vxe-table--tooltip-content {
max-width: 600px;
max-height: 210px;
overflow-y: auto;
}