前言
vue2
+element-ui
的项目,el-table
本身支持超出宽度显示省略号,悬浮展示tooltip
。最近产品非常严肃的提了要求,“你得让客户能复制啊,不然这隐藏还不如不隐藏”。
网上翻了翻,看见官网里有个issue [Bug Report] el-table component adds show-overflow-tooltip, and text in tooltip cannot be copied by mouse selection
唉,技术更新快,但是旧项目跟不上啊,搞吧!
实现
这个issue下面有个大佬的评论,提供了临时解决办法,尝试之后还有一点小问题,计时器的时差会导致在该出现的时候关闭了,体验不是很好。于是看源码,并且根据大佬利用计时器的思路重新写了一下。
el-tooltip
的enterable
默认就是true
,读el-table
源码发现这里在使用的时候并没有传这个属性,之所以鼠标不能进入,是因为一个table
整体使用一个el-tooltip
,并且在table-body
里去直接控制了tooltip
的操作。
重写覆盖原有的handleCellMouseLeave
方法,在离开时延迟100毫秒关闭,让鼠标可以移入,核心改动如下。
handleCellMouseLeave(event) {
...
tooltip.setExpectedState(false);
clearTimeout(tooltip.timeoutLeave);
tooltip.timeoutLeave = setTimeout(() => {
if (!tooltip.expectedState) {
tooltip.handleClosePopper();
}
tooltip.timeoutLeave = null;
}, 100);
...
}
此时移入是ok了,但是又有个小问题,如果鼠标快速进入另一个会展示tooltip
的单元格,tooltip
就会直接消失。是由于这行代码导致的。
tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none');
于是改动了一下handleCellMouseEnter
方法,如果tooltip
存在那就需要重新打开一下。而且考虑到鼠标移入tooltip
的时候,存在路过另一个要展示tooltip
单元格的情况,还需要给个路过的计时。
handleCellMouseEnter(event, row) {
...
if (tooltip.showPopper) {
clearTimeout(tooltip.timeoutEnter);
tooltip.timeoutEnter = setTimeout(() => {
if (!tooltip.expectedState) {
tooltip.handleClosePopper();
showTooltip();
}
tooltip.timeoutEnter = null;
}, 100);
return;
}
...
}
因为想要全局引入,那加了个功能,还得加个配置(万一产品说有的地方不能让他复制呢)。这里我把配置加在el-table
上了,如果想要加在el-table-column
上,renderCell
方法需要引入支持jsx
,我的项目里并没有,如果某位大佬有兴趣可以改下子。(2023年了,感觉没必要。。)
const ElTable = {
extends: Table,
props: {
tooltipEnterable: {
type: Boolean,
default: true,
},
},
};
最终完整代码如下
// main.js
import ElTable from '@/components/elTable';
...
Vue.use(ElTable);
// elTable.js
import { Table } from 'element-ui';
import { getCell, getColumnByCell } from 'element-ui/packages/table/src/util';
import { getStyle, hasClass } from 'element-ui/src/utils/dom';
Object.assign(Table.components.TableBody.methods, {
handleCellMouseEnter(event, row) {
const { table } = this;
const cell = getCell(event);
if (cell) {
const column = getColumnByCell(table, cell);
table.hoverState = { cell, column, row };
const { hoverState } = table;
table.$emit('cell-mouse-enter', hoverState.row, hoverState.column, hoverState.cell, event);
}
// 判断是否text-overflow, 如果是就显示tooltip
const cellChild = event.target.querySelector('.cell');
if (!(hasClass(cellChild, 'el-tooltip') && cellChild.childNodes.length)) {
return;
}
// use range width instead of scrollWidth to determine whether the text is overflowing
// to address a potential FireFox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1074543#c3
const range = document.createRange();
range.setStart(cellChild, 0);
range.setEnd(cellChild, cellChild.childNodes.length);
const rangeWidth = range.getBoundingClientRect().width;
const padding = (parseInt(getStyle(cellChild, 'paddingLeft'), 10) || 0)
+ (parseInt(getStyle(cellChild, 'paddingRight'), 10) || 0);
if (
(rangeWidth + padding > cellChild.offsetWidth
|| cellChild.scrollWidth > cellChild.offsetWidth)
&& this.$refs.tooltip
) {
const { tooltip } = this.$refs;
const { tooltipEnterable } = this.table;
const showTooltip = () => {
this.tooltipContent = cell.innerText || cell.textContent;
tooltip.referenceElm = cell;
tooltip.doDestroy();
tooltip.setExpectedState(true);
this.activateTooltip(tooltip);
};
if (tooltipEnterable && tooltip.showPopper) {
clearTimeout(tooltip.timeoutEnter);
tooltip.timeoutEnter = setTimeout(() => {
if (!tooltip.expectedState) {
tooltip.handleClosePopper();
showTooltip();
}
tooltip.timeoutEnter = null;
}, 100);
return;
}
if (!tooltipEnterable && tooltip.$refs.popper) {
tooltip.$refs.popper.style.display = 'none';
}
showTooltip();
}
},
handleCellMouseLeave(event) {
const { tooltip } = this.$refs;
if (tooltip) {
tooltip.setExpectedState(false);
const { tooltipEnterable } = this.table;
if (tooltipEnterable) {
clearTimeout(tooltip.timeoutLeave);
tooltip.timeoutLeave = setTimeout(() => {
if (!tooltip.expectedState) {
tooltip.handleClosePopper();
}
tooltip.timeoutLeave = null;
}, 100);
} else {
tooltip.handleClosePopper();
}
}
const cell = getCell(event);
if (!cell) return;
const oldHoverState = this.table.hoverState || {};
this.table.$emit(
'cell-mouse-leave',
oldHoverState.row,
oldHoverState.column,
oldHoverState.cell,
event,
);
},
});
/**
* @description 扩展el-table,实现当showOverflowTooltip时,鼠标可移入tooltip功能
* @prop {Boolean} tooltipEnterable 仅在列属性showOverflowTooltip为true时生效,鼠标是否可进入到 tooltip 中,默认为true
*/
const ElTable = {
extends: Table,
props: {
tooltipEnterable: {
type: Boolean,
default: true,
},
},
};
export default (Vue) => {
Vue.component('ElTable', ElTable);
};
新人写文章,希望老师多多指点,多多交流。