Big factory technology advanced front-end Node advanced Click above for Programmer Growth Guide and follow the official account Reply 1, join the advanced Node communication group
Foreword
If you want the text to be omitted with an ellipsis after it exceeds the width, just add the following css.
.ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
3 lines of css are done, but a problem arises:
If we want to display the popper when the mouse is hovering over the text when the text is omitted, that is, when the text exceeds the specified width, how should we implement it?
CSS handles the omission for us, but JS doesn’t know when text is omitted, so we have to calculate it through JS.
Next, I’ll introduce several ways to implement JS calculation omission.
createRange
I found that the Element-plus
form component has already implemented this function, so let’s learn its source code first.
Source code address:
https://github.com/element-plus/element-plus/blob/dev/packages/components/table/src/table-body/events-helper.ts
// Paste only the relevant ones const cellChild = (event.target as HTMLElement).querySelector('.cell') const range = document.createRange() range.setStart(cellChild, 0) range.setEnd(cellChild, cellChild.childNodes.length) let rangeWidth = range.getBoundingClientRect().width let rangeHeight = range.getBoundingClientRect().height /** detail: https://github.com/element-plus/element-plus/issues/10790 * What went wrong? * UI > Browser > Zoom, In Blink/WebKit, getBoundingClientRect() sometimes returns inexact values, probably due to lost precision during internal calculations. In the example above: * - Expected: 188 * - Actual: 188.00000762939453 */ const offsetWidth = rangeWidth - Math.floor(rangeWidth) if (offsetWidth < 0.001) { rangeWidth = Math.floor(rangeWidth) } const offsetHeight = rangeHeight - Math.floor(rangeHeight) if (offsetHeight < 0.001) { rangeHeight = Math.floor(rangeHeight) } const { top, left, right, bottom } = getPadding(cellChild) // see below const horizontalPadding = left + right const verticalPadding = top + bottom if ( rangeWidth + horizontalPadding > cellChild.offsetWidth || rangeHeight + verticalPadding > cellChild.offsetHeight || cellChild.scrollWidth > cellChild.offsetWidth ) { createTablePopper( parent?.refs.tableWrapper, cell, cell.innerText || cell.textContent, nextZIndex, tooltipOptions ) }
// getPadding function in line 17 of the above code const getPadding = (el: HTMLElement) => { const style = window.getComputedStyle(el, null) const paddingLeft = Number.parseInt(style.paddingLeft, 10) || 0 const paddingRight = Number.parseInt(style.paddingRight, 10) || 0 const paddingTop = Number.parseInt(style.paddingTop, 10) || 0 const paddingBottom = Number.parseInt(style.paddingBottom, 10) || 0 return { left: paddingLeft, right: paddingRight, top: paddingTop, bottom: paddingBottom, } }
document.createRange()
is a method in JavaScript that creates a Range object that represents a range in the document. Range objects are typically used to select a portion of a document and then operate on it.
it can:
-
Set the selected text range: You can use the
document.createRange()
method to create a Range object, and use thesetStart()
andsetEnd()
methods to set it Select the starting and ending position of the text. -
Inserting new elements: You can use the
document.createRange()
method to create a Range object and theinsertNode()
method to insert new elements into the document at a specified position. -
Get the position of a specific element: You can use the
document.createRange()
method to create a Range object, and use thegetBoundingClientRect()
method to get the position and size information of the element in the document.
Here element uses getBoundingClientRect of the range object to obtain the width and height of the element. At the same time, because the obtained width and height value has many decimal places, element-plus makes a judgment and discards the decimal part if the decimal value is less than 0.001.
Next, let’s make a copy. You can adjust the width of the box to see whether there are ellipses on the page.
<div class="ellipsis box"> Lorem ipsum dolor sit amet consectetur adipisicing elit. </div> <style> .ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .box { border: 1px solid gray; padding: 10px; } </style>
Note here that we need to distinguish between clientWidth
and offsetWidth
, because we have now added a 1px border to the box, so offsetWidth = 1 * 2 (border width on the left and right sides) + clientWidth, so we use clientWidth here to represent the actual width of the box.
const checkEllipsis = () => { const range = document.createRange(); range.setStart(box, 0) range.setEnd(box, box.childNodes.length) let rangeWidth = range.getBoundingClientRect().width let rangeHeight = range.getBoundingClientRect().height const contentWidth = rangeWidth - Math.floor(rangeWidth) const { pLeft, pRight } = getPadding(box) const horizontalPadding = pLeft + pRight if (rangeWidth + horizontalPadding > box.clientWidth) { result.textContent = 'Ellipses exist' } else { result.textContent = 'The container is wide enough and there are no ellipsis' } }
In this method, the elements and styles placed in the div are not restricted. For example, html written this way can still be calculated correctly.
<div class="ellipsis box"> Lorem ipsum dolor sit amet consectetur adipisicing elit. <span style="font-size: large;">hello world</span> <span style="letter-spacing: 20px;">hello world</span> </div>
Create a div to get simulated width
We can also get the actual width of the element without overflow:hidden
by creating an almost identical div.
<div class="ellipsis box"> Lorem ipsum dolor sit amet consectetur adipisicing elit. </div> <style> .ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .box { border: 1px solid gray; padding: 10px; } </style>
const checkEllipsis = () => { const elementWidth = box.clientWidth; const tempElement = document.createElement('div'); const style = window.getComputedStyle(box, null) tempElement.style.cssText = ` position: absolute; top: -9999px; left: -9999px; white-space: nowrap; padding-left:${style.paddingLeft}; padding-right:${style.paddingRight}; font-size: ${style.fontSize}; font-family: ${style.fontFamily}; font-weight: ${style.fontWeight}; letter-spacing: ${style.letterSpacing}; `; tempElement.textContent = box.textContent; document.body.appendChild(tempElement); if (tempElement.clientWidth >= elementWidth) { result.textContent = 'Ellipses exist' } else { result.textContent = 'The container is wide enough and there are no ellipsis' } document.body.removeChild(tempElement); }
When there are multiple dom elements in the box element, you have to create the dom recursively, or you can try cloneNode(true)
to try cloning.
Create a block element to wrap the inline element
This method was learned from acro design vue
and should be the simplest method. The key point is that the outer layer must be a block element, and the inner layer must be an inline element.
<div class="ellipsis box"> <span class="content"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Elit. </span> </div> <style> .ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .box { border: 1px solid gray; padding: 10px; } </style>
Through the above processing of css and html, we can realize the ellipisis of the text in the box element. At the same time, since there is no overflow processing on span.content
, the offsetWidth of the span is still constant.
const checkEllipsis = () => { const { pLeft, pRight } = getPadding(box) const horizontalPadding = pLeft + pRight if (box.clientWidth <= content.offsetWidth + horizontalPadding ) { result.textContent = 'Ellipses exist' } else { result.textContent = 'The container is wide enough and there are no ellipsis' } }
Similarly, as long as the outer element is block and the inner element is inline, the DOM elements inside can be placed casually.
<div class="ellipsis box"> <span class="content"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Elit. <span style="font-size: large;"> hello world </span> <span style="letter-spacing: 20px;"> hello world </span> </span> </div>
Comparison of methods
-
Performance (personal subjective judgment) 3>1>2
-
Level of worry (personal subjective judgment): 1>3>2
-
Accuracy (personal subjective judgment): The accuracy of the three methods is almost the same. If I insist on comparing, I think it is 3>1>2
After that, I will look at other component libraries to see what good methods they have, and then add more. The front end is always doing these very small things, haha.
Author: Jiaqi coder
Link: https://juejin.cn/post/7262280335978741797
Node community
I have formed a Node.js community with a very good atmosphere. There are many Node.js friends in it. If you are interested in learning Node.js (you can also have plans in the future), we can do Node.js-related work together. Exchange, learn and build together. Add Koala friends below and reply “Node”.
"Share, Like, Watch" to support