DEV Community

Calculate HTML Element width before render

Steven Straatemans on February 19, 2021

I want to show you a small trick to know the size of an element, before rendering that element on the screen. This trick can be useful for various ...
Collapse
 
ismailisimba profile image
Ismaili Simba

Ok, so hear me out, I swear I'm not crazy!

If you know the font size of the text in those two lines, in pixels, this might work...

I assume you get a string like a short description to display there, but at the end of two lines you have to truncate it and display the read more button.

You can calculate the width of the containing element using getComputedStyles then read it as pixels.

Divide that by your font size and you would know the maximum amount of characters you can fit in one line in that container. Multiply it by two and you have your two lines length.

Calculate the width of read more and remove it from the two lines length.

Now all you have to do is truncate the string you are receiving for those two lines at that length.

If you position:relative the container of the two lines. You can position the read more wherever you want using absolute values.

I saw where you said what the easiest way to do this was and this popped up in my mind.

Just wanted to share before I forgot, still reading...

Collapse
 
zachhaber profile image
ZachHaber

I've tried that sort of process before in an application. The one with trying to use line height and the length of the line.
The problem is that it only works properly with monospace fonts. And as I've learned, the users rather dislike reading monospace fonts.

Collapse
 
ismailisimba profile image
Ismaili Simba

Not line height. Just length. You never check line height at all.

Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇

Ok hear me out both of you. JavaScript will be executed async after the painting so unless you are using react, svelte, angular or similar it may cause flickering. That being said I used similar trick on the past but with other purposes rather than adding a read more button in which case you need to add visibility: hidden, then to avoid flickering just add a min-height to the parent container and then give priority to this script execution

Thread Thread
 
grahamthedev profile image
GrahamTheDev • Edited

Ok so hear me out all of you 😋.

May I humbly present a solution that is CSS based and only uses JS for the expand, button name change and adding the expanded class.

At less than 50 lines of CSS (and it could be much less) and 20 lines of JavaScript (including the JS to handle the input change updating the CSS variable!) I think it does the job quite nicely.

One downside is it doesn't know if all the lines are shown on screen by default and so can only be used where you know that there will be lines hidden by your height limitation.

Thread Thread
 
sstraatemans profile image
Steven Straatemans

Oeh I like this one, thanks. But yeah, not knowing if all is shown, is too bad.

Thread Thread
 
grahamthedev profile image
GrahamTheDev • Edited

Ok if you insist....😋

Now responds to screen resize and automatically initiates and destroys itself depending on whether it is needed or not.

Resize the below example to mobile size and you should see it all fire into action, resize again and it will automatically remove itself. Also fixed a bug where if you set the clamp to 200 lines it would make the box 200 lines tall! Obviously you can also just edit the number of lines using the input to see it fire into action.

Oh and you could use this with a very large block of text so that is an added bonus (as there is no counting involved).

Perhaps you could take the principle and turn it into a fully fledged component that accounts for incorrect inputs, auto inserts the HTML etc etc.

By the way your example one seems to have an issue if you resize the screen and then change the line values, as you can see in the following example I managed to get it in an open state (showing "read less") while only showing 9 lines when I set it to 10. I am sure mine has issues too but yours is in production so thought I better give you a heads up!

strange issue when resizing the screen

Thread Thread
 
sstraatemans profile image
Steven Straatemans • Edited

@joelbonetr : Thanks for the reply, but about the flickering:
It will not cause flickering, because the actual repaint is done at the end of the function. The reflow is done everytime, but that will not show up on your screen.
I made a small Vanilla JS example:
codesandbox.io/s/calculate-width-j...

Thread Thread
 
sstraatemans profile image
Steven Straatemans

@inhuofficial : niiiice! and thanks for the heads up. I will look into it ASAP

Collapse
 
sstraatemans profile image
Steven Straatemans

the font styles are inherited. so that should work fine with normal fonts as well

Collapse
 
szalonna profile image
Joe

The font size is not the same as font width. If you use non-mono font, you cannot just assume, that the count of letters is in 1-to-1 ratio with the width of the container. Imagine the folloeing two word with the same char coun: wwwwww and llllll.

Collapse
 
sstraatemans profile image
Steven Straatemans

it inherits the style, so also the fonts. So it should be fine

Collapse
 
ismailisimba profile image
Ismaili Simba

Ooh, so that's why it won't work unless you're using monospaced fonts

Collapse
 
sstraatemans profile image
Steven Straatemans

Looks like there is a CSS solution after all.
It uses shape-outside and it has pretty good browser support
The only caveat is that you need to know the height of the element you want in the lower-right corner.
There is a great article about it on CSS-tricks