背景
el-table中的table-body文件中,当我们单元格设置tooltip属性时,我们会发现这么一段十分不常见的代码
const range = document.createRange();
range.setStart(cellChild, 0);
range.setEnd(cellChild, cellChild.childNodes.length);
为什么我们要使用range
有这样的场景:
需要判断一段文案,如果出现了省略号,就增加一个tooltip悬浮框来展示所有的文案
解法:
先判断文案正常渲染的宽度,然后跟容器进行判断,如果超过容器宽度,则表示需要渲染tooltip框
那么问题来了:
如果这个文案的框设置了自己的宽度或者是设置了overflow为 hidden,那我们就无法正常计算文案宽度了
解法:
先渲染文案计算宽度后,再加上外围的样式(这种方案相对更麻烦些,但是可以达到效果)
另外一种解法:
使用range,文案如果用range包围,则可以找到连续的文案,并且通过getBoundingClientRect 来进行宽度的计算,不论容器设置了width还是overflow,都可以准确计算出来!!!!
举例
// style
.app {
width: 600px;
height: 600px;
margin: 30px auto;
padding: 50px;
box-sizing: border-box;
border: 1px solid #aaa;
}
.wrapper {
width: 220px;
height: 24px;
overflow: hidden;
}
.cell {
// 这个很重要哦!!!
white-space: nowrap;
width: 50px;
overflow: hidden;
margin: 0;
}
// html
<div class="app">
<div class="wrapper">
<p class="cell">这是一段测试悬浮窗的文案,这是一段测试悬浮窗的文案,这是一段测试悬浮窗的文案,这是一段测试悬浮窗的文案,这是一段测试悬浮窗的文案,这是一段测试悬浮窗的文案,这是一段测试悬浮窗的文案,这是一段测试悬浮窗的文案,这是一段测试悬浮窗的文案</p>
</div>
</div>
// js
const cell = document.getElementsByClassName('cell')[0];
// 计算是否需要出现悬浮框
function expli(dom) {
const range = document.createRange();
range.setStart(dom, 0);
range.setEnd(dom, dom.childNodes.length);
// const seles = window.getSelection();
// seles.removeAllRanges();
// seles.addRange(range);
console.log(range.getBoundingClientRect().width, dom.offsetWidth);
}
expli(cell);
上面的的代码中,不管cell设置width还是overflow,宽度都是整个文案的宽度,如果想要修改宽度,我们只能通过增减文案的方式了
完整代码
handleCellMouseEnter(event, row) {
const table = this.table;
const cell = getCell(event);
if (cell) {
const column = getColumnByCell(table, cell);
const hoverState = table.hoverState = {cell, column, row};
table.$emit('cell-mouse-enter', hoverState.row, hoverState.column, hoverState.cell, event);
}
// 判断是否text-overflow, 如果是就显示tooltip
const cellChild = event.target.querySelector('.cell');
// *如果有el-tooltip类名,并且这个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
// TODO这部分代码干啥的?
const range = document.createRange();
// !需要注意的是,设置range的时候第一个参数(dom节点)使用的是cellChild,也就是cell这个单元格,所以以下做法是会选中当前tooltip文本那部分,如果使用cellChild.firstChild的话就不一样了,这个表示dom是text节点,则选中是文本的索引值进行选中的
range.setStart(cellChild, 0);
range.setEnd(cellChild, cellChild.childNodes.length);
console.warn(cellChild.childNodes.length);
// !运行以下代码可以看到上面代码的效果:会有选中效果,但是源码中没有以下代码,这是我自己添加的
// const seles = window.getSelection();
// seles.removeAllRanges();
// seles.addRange(range);
// console.log(range);
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.tooltip;
// TODO 会引起整个 Table 的重新渲染,需要优化
this.tooltipContent = cell.innerText || cell.textContent;
tooltip.referenceElm = cell;
tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none');
tooltip.doDestroy();
tooltip.setExpectedState(true);
this.activateTooltip(tooltip);
}
},