背景
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>// jsconst 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, 如果是就显示tooltipconst 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);}},
