Checking for CSS-truncated text with JavaScript

I am currently working on a JavaScript project to determine if text is truncated. While I found a useful solution mentioned here, there is one edge case that needs to be addressed. Despite the visual truncation of the text, the first block on mouse hover incorrectly returns false.

function checkTruncationStatus(element) {
  return (element.offsetWidth < element.scrollWidth);
}

function handleMouseHover(element) {
  console.log(`Is the text truncated: ${checkTruncationStatus(element)}`);
}
div.red {
  margin-bottom: 1em;
  background: red;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 300px;
  cursor: pointer;
}
<h6>Hover over the text and check the console for messages.</h6>

<!-- Expected to return true -->
<div class="red" onmouseover="handleMouseHover(this)">
  <a>Analytics reports come through garbled. Please check</a>
</div>

<!-- Expected to return true -->
<div class="red" onmouseover="handleMouseHover(this)">
  <a>Analytics reports come through garbled. Please check again</a>
</div>

<!-- Expected to return false -->
<div class="red" onmouseover="handleMouseHover(this)">
  <a>Normal text</a>
</div>

The solution I am seeking is for the function to accurately detect when text is truncated by CSS.

Answer №1

It appears that the issue lies in the fact that both HTMLElement.offsetWidth and Element.scrollWidth are rounded values.
On my computer, the true inner-width of your element is actually 300.40625px, but this gets rounded down to 300px in Chrome.

The solution is to utilize APIs that return float values, although there are limited options available...

One might be tempted to inspect the inner width of the <a> element using getBoundingClientRect().width, which would work in most cases. However, it may fail if there are additional styling elements like padding in the div or margins on the <a> element.

A possible solution involves checking whether the text content exceeds the element's visible width by adding the truncated class dynamically based on the result.

Another approach utilizes Range and its getBoundingClientRect() method to determine if the text is truncated due to the width exceeding the element's boundaries.

To address the specific behavior in Chrome, where the ellipsis rendering is exposed in the result of Range.getClientRects(), a method toggles the text-overflow property and observes the appearance of the DOMRect.

While this strategy satisfies the detection in Chrome, a separate check for Safari is required, considering the differences in how browsers handle the ellipsis rendering.


Update:

Chrome no longer exposes the bounding box of the ellipsis when the start range is 0, impacting the reliability of the workaround in certain scenarios. Therefore, additional measures may be needed to address this change in behavior.

Answer №2

Here is a simple solution to check if text is truncated:

const isTextTruncated = (element) => {
  const clone = element.cloneNode(true);
  clone.style.display = 'inline';
  clone.style.width = 'auto';
  clone.style.visibility = 'hidden';
  document.body.appendChild(clone);
  const isTruncated = clone.offsetWidth >= element.clientWidth;
  clone.remove();
  return isTruncated;
}

This code may not be the most elegant, but it gets the job done.

Answer №3

Kaiido correctly identified the issue when discussing the discrepancy between rounded values like offsetWidth and scrollWidth, and the floating-point values used to display ellipsis. While a suitable cross-browser solution eluded him, a combination of his insights with a tweaked version of see sharper's method proved effective and reliable across different browsers.

The enhanced approach involving a modified version of the code below:

function isEllipsisActive(e) {
    const temp = e.cloneNode(true);

    temp.style.position = "fixed";
    temp.style.overflow = "visible";
    temp.style.whiteSpace = "nowrap";
    temp.style.visibility = "hidden";

    e.parentElement.appendChild(temp);

    try {
        const fullWidth = temp.getBoundingClientRect().width;
        const displayWidth = e.getBoundingClientRect().width;

        return fullWidth > displayWidth;
    } finally {
        temp.remove();
    }
}

When combined with the following script:

function isEllipsisActive(e) {
    const temp = e.cloneNode(true);

    temp.style.position = "fixed";
    temp.style.overflow = "visible";
    temp.style.whiteSpace = "nowrap";
    temp.style.visibility = "hidden";

    e.parentElement.appendChild(temp);

    try {
        const fullWidth = temp.getBoundingClientRect().width;
        const displayWidth = e.getBoundingClientRect().width;

        return {
            offsetWidth: e.offsetWidth,
            scrollWidth: e.scrollWidth,
            fullWidth,
            displayWidth,
            truncated: fullWidth > displayWidth
        };
    } finally {
        temp.remove();
    }
}

// Additional functions and event listeners go here
...

The final solution delivers accurate results and is capable of handling ellipsis display inconsistencies in various web browsers seamlessly.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for Utilizing Border-Radius in Safari

What is the best way to hide a child element's corners within its parent (#div1) Ensuring that the child does not overflow its parent container? ...

Issue with jQuery fadeIn() and fadeOut() functions on IE versions 7 and 8

I have a project in Ruby on Rails that showcases illustrations. The top of the page features category links that fade out the current illustrations, replace them with new content, and then fade back in. Currently, I am utilizing jQuery version 1.6.2 for t ...

AngularJS Encounter: A Grand 404 Glitch

Hey, I'm just getting started with AngularJS and following this helpful tutorial. However, I've run into an issue where the $http.get method is consistently giving me a 404 Error when trying to access a JSON file. The JSON file is located in the ...

What is the best way to clear an input value in Vuejs after it has been submitted?

Could you help me with my to-do list in Vuejs? I'm having trouble resetting the input value when a new item is added. Any suggestions on how to achieve this? I've attempted to retrieve the input value and set it to an empty string, but unfortuna ...

I am interested in creating a ranking system in JavaScript using JSON data based on points

I have a desire to create the following: var users = {jhon: {name: 'jhon', points: 30}, markus:{name: 'Markus', points: 20}}; // I want it to return like this 1. Jhon with number of points: 30 // 2. Markus with number of points: 20 ...

Span wrapped in a hover effect

I am facing an issue with hover options... What I am looking for is a text in a circle Finally, I achieved the desired look using csswrap Next, I added some jQuery to insert links into all spans with the same class Everything is working fine, except fo ...

How to position ion-title at the center using position:absolute

I am a newcomer to the world of Ionic Framework. Recently, I created a page that features a swipe-back-button located on the left side of the navigation bar. This positioning causes the title to shift slightly to the right, as demonstrated by the addition ...

Can state values be utilized as content for Meta tags?

I am looking for a way to display image previews and titles when sharing a page link. In order to achieve this, I am using the Nextjs Head Component. The necessary details are fetched on page load and used as content for the meta attributes. let campaign = ...

Resize images in PHP and MySQL with precision without distorting the image

I'm looking for a solution to resize images on upload without deforming them. The current code uploads the image and stores it in the database, but I need to add resizing functionality. I'd prefer not to use JavaScript, but if necessary, please i ...

Exploring the Differences Between Arrays in JavaScript

I am currently tackling the task of comparing arrays in JavaScript, specifically within node.js. Here are the two arrays I am working with: Array 1: [16,31,34,22,64,57,24,74,7,39,72,6,42,41,40,30,10,55,23,32,11,37,4,3,2,52,1,17,50,56,60,65,48,43,58,28,3 ...

If I choose a different option from the dropdown list, I would like a textbox to appear

Hey there, I have a list of industries and one of the options is "Other". When a user clicks on "Other", a text box should appear for them to specify. I'm struggling to make this work using Django AJAX. Any help would be appreciated! <div class ...

What is the best way to synchronize MultiView and TreeView in ASP.NET web forms?

I'm looking to set up a tree view on the left side of my screen, along with a multiview showing content based on the selected tree item on the right. However, I'm having trouble aligning the multiview properly next to the tree view - it seems to ...

Page for users to login using React

Struggling to create a login page in React due to the asynchronous nature of setState. Upon submission, the state is not updating with form values, only displaying old initial values. How can I ensure that the submit function receives the new values? Is ...

When iterating through a JavaScript object, opt for utilizing references instead of repeatedly specifying the path

for (var element in array[index1][index2][index3].property) { alert(array[index1][index2][index3].property[element].data); } Is there a more succinct way to achieve the same result by referencing the object directly like this: for (var ...

The presence of Null is detected during the HTML parsing process

I'm in the process of developing dynamic HTML pages using JSON data. Each time the application encounters a special identifier like #@, it gets replaced by a specific value. However, upon loading my page, I noticed that a null is being printed at the ...

An asynchronous function does not provide a result

As a beginner in the realm of Javascript, I find myself challenged by a task involving reading multiple files and constructing a JSON object response. While I have managed to execute most of the code successfully, I am stuck at the final hurdle - receiving ...

What is the specified height for the AppBar and Toolbar components in Material-UI?

I'm trying to figure out the exact height being used in this scenario: <AppBar position="static"> <Toolbar> because I'll need that information for a calculation in another component. My current assumption is that it's 64px, b ...

Is there a way to merge attribute selectors for elements that satisfy one condition or the other?

Is there a way to combine selectors effectively? <input type=number> <input type=date> I was able to successfully hide the arrows in a number field with the following code: input[type=number]::-webkit-inner-spin-button, input[type=number]::- ...

Adding new rows between existing rows in an HTML table using Jquery syntax

How can jQuery be used to insert rows between existing rows in an HTML table? <Table id="TEST"> <tr>1</tr> <tr>2</tr> </table> I need to include a new row <tr>xx</tr> between the rows with values ...

Utilize the power of MYSQL and PHP in conjunction with an HTML form to

Having trouble with a basic PHP/SQL search bar for my database. The search results are not showing up even though the search bar appears on the page. When I type something, it doesn't reflect in the URL. Below is the code snippet. I am connecting to t ...