How To Check If Text Is Truncated By Css Using Javascript
Solution 1:
The problem here is that both HTMLElement.offsetWidth
and Element.scrollWidth
are rounded values.
Your element's true inner-width is actually 300.40625px
on my computer, and this gets floored to 300px
in my Chrome.
The solution here is to use APIs that return float values, and there aren't much...
One could be tempted to check the inner <a>
's getBoundingClientRect().width
, and that would actually work in all OP's cases, but that would only work in these case: Add a padding to the div, a margin to these <a>
, or an other element and it's broken.
document.querySelectorAll( ".test" ).forEach( el => {
el.classList.toggle( "truncated", isEllipsisActive( el ) );
} );
functionisEllipsisActive( el ) {
return el.firstElementChild.getBoundingClientRect().width > el.getBoundingClientRect().width;
}
div.test {
margin-bottom: 1em;
background: red;
color: #fff;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 300px;
}
div.truncated {
background: green;
}
.margin-left {
margin-left: 225px;
}
<!-- should be green --><divclass="test"><a>Analytics reports comes through garbled. Plsss</a></div><!-- should be green --><divclass="test"><a>Analytics reports comes through garbled. Plsssssss</a></div><!-- should be green --><divclass="test"><a>Analytics</a><a> reports comes through garbled. Plsssssss</a></div><!-- should be green --><divclass="test"><aclass="margin-left">Shorter text</a></div><!-- should be red --><divclass="test"><a>Normal text</a></div>
So one might think a Range and its getBoundingClientRect()
method would do, however, while this is able to tell the real size of the text content in your element, this only checks for the text content. If the scrolling is caused by a margin, it won't work.
document.querySelectorAll(".test").forEach( el => {
el.classList.toggle( "truncated", isEllipsisActive( el ) );
} );
functionisEllipsisActive( el ) {
return el.scrollWidth !== el.offsetWidth ?
el.scrollWidth > el.offsetWidth :
checkRanges( el ); // Blink and Webkit browsers do floor scrollWidth
}
functioncheckRanges( el ) {
const range = newRange();
range.selectNodeContents( el );
const range_rect = range.getBoundingClientRect();
const el_rect = el.getBoundingClientRect();
// assumes ltr directionreturn range_rect.right > el_rect.right;
}
div.test {
margin-bottom: 1em;
background: red;
color: #fff;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 300px;
}
div.truncated {
background: green;
}
.margin-left {
margin-left: 225px;
}
.margin-right {
margin-right: 225px;
}
<!-- should be green --><divclass="test"><a>Analytics reports comes through garbled. Plsss</a></div><!-- should be green --><divclass="test"><a>Analytics reports comes through garbled. Plsssssss</a></div><!-- should be green --><divclass="test"><a>Analytics</a><a> reports comes through garbled. Plsssssss</a></div><!-- should be green --><divclass="test"><aclass="margin-left">Shorter text</a></div><!-- should be green --><divclass="test"><aclass="margin-right">Shorter text</a></div><!-- should be red --><divclass="test"><a>Normal text</a></div>
So the only solution I could think of relies on a Chrome specific behavior: They do expose the Client Rect of the rendered ellipsis in the result of Range.getClientRects()
.
So a way to know for sure, in Chrome, if the ellipsis is rendered, is to toggle the text-overflow
property and check if this DOMRect appeared.
However, since this is a Chrome only behavior, we still need to check for the Range's bounding-box position for Safari.
document.querySelectorAll(".test").forEach( el => {
el.classList.toggle( "truncated", isEllipsisActive( el ) );
} );
functionisEllipsisActive( el ) {
return el.scrollWidth !== el.offsetWidth ?
el.scrollWidth > el.offsetWidth :
checkRanges( el ); // Blink and Webkit browsers do floor scrollWidth
}
functioncheckRanges( el ) {
const range = newRange();
range.selectNodeContents( el );
const range_rect = range.getBoundingClientRect();
const el_rect = el.getBoundingClientRect();
// assumes ltr directionif( range_rect.right > el_rect.right ) {
returntrue;
}
// Following check would be enough for Blink browsers// but they are the only ones exposing this behavior.// first force ellipsis
el.classList.add( "text-overflow-ellipsis" );
// get all the client rects (there should be one for the ellipsis)const rects_ellipsis = range.getClientRects();
// force no ellipsis
el.classList.add( "text-overflow-clip" );
const rects_clipped = range.getClientRects();
// clean
el.classList.remove( "text-overflow-ellipsis" );
el.classList.remove( "text-overflow-clip" );
// if the counts changed, the text is truncatedreturn rects_clipped.length !== rects_ellipsis.length;
}
/* 2 new clasess to force the rendering of ellipsis */.text-overflow-ellipsis {
text-overflow: ellipsis !important;
}
.text-overflow-clip {
text-overflow: clip !important;
}
div.test {
margin-bottom: 1em;
background: red;
color: #fff;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 300px;
}
div.truncated {
background: green;
}
.margin-left {
margin-left: 225px;
}
.margin-right {
margin-right: 225px;
}
<!-- should be green --><divclass="test"><a>Analytics reports comes through garbled. Plsss</a></div><!-- should be green --><divclass="test"><a>Analytics reports comes through garbled. Plsssssss</a></div><!-- should be green --><divclass="test"><a>Analytics</a><a> reports comes through garbled. Plsssssss</a></div><!-- should be green --><divclass="test"><aclass="margin-left">Shorter text</a></div><!-- should be green --><divclass="test"><aclass="margin-right">Shorter text</a></div><!-- should be red --><divclass="test"><a>Normal text</a></div>
Solution 2:
Try using
functionisEllipsisActive(e) {
var c = e.cloneNode(true);
c.style.display = 'inline';
c.style.width = 'auto';
c.style.visibility = 'hidden';
document.body.appendChild(c);
const truncated = c.offsetWidth >= e.clientWidth;
c.remove();
return truncated;
}
It's hacky, but it works.
Post a Comment for "How To Check If Text Is Truncated By Css Using Javascript"