11

I have a list of divs in which I display the preview of longer documents. The documents use varying font styles. So I don't have a constant line height. Here is an example: http://jsfiddle.net/z56vn/

I need to only show the first few lines of each document. We've determined that 300px is about right. If I simply set a max-height of 300px to the divs, then depending on text properties (size, padding, margin) the bottom of last line gets clipped.

How can I set a max-height for each block that will be close to 300px but that will not cause clipping?

The solution can use CSS, Javascript and jQuery.


Those two questions are similar but their solutions assume a constant line height.

Community
  • 1
  • 1
Sylvain
  • 19,099
  • 23
  • 96
  • 145
  • 1
    You’ll need to include all factors: font-size, line-height, character width, container width/height etc, then calculate a max number of characters that are guaranteed to fit. – David Hellsing Dec 26 '13 at 23:33
  • possible duplicate of [Cross browsers mult-lines text overflow with ellipsis appended within a width&height fixed div?](http://stackoverflow.com/questions/3404508/cross-browsers-mult-lines-text-overflow-with-ellipsis-appended-within-a-widthhe) – Adriano Mar 17 '15 at 13:42
  • related http://stackoverflow.com/questions/6222616/with-css-use-for-overflowed-block-of-multi-lines – Adriano Mar 03 '16 at 13:36

5 Answers5

19

The algorithm to calculate all the factors perfectly using only javascript would be too complex.

With css3 there is line-clamp

But this works only on modern browsers.

p{
 margin:20px;
 overflow: hidden;
 text-overflow: ellipsis;
 display: -webkit-box;
 -webkit-line-clamp: 3;
 -webkit-box-orient: vertical;
}

DEMO

http://jsfiddle.net/MM29r/

this allows you to set the number of lines you want to display before adding the 3 dots.

now you want 300px... so:

var p=document.getElementsByTagName('p')[0],
lineheight=parseInt(window.getComputedStyle(p).getPropertyValue("line-height"));
var lines=Math.floor(300/lineheight);
p.style['-webkit-line-clamp']=lines;

so this gives you an element that is 300px or less

DEMOS

http://jsfiddle.net/MM29r/1/

http://jsfiddle.net/MM29r/2/

NEEDED VALUES: line-height

Now if you want to make the box exactly 300px height just add margins or paddings to the paragraphs.But that depends on your preferences.

if you have some questions just ask.

Note

every js function that adds 3 dots at the end by calculating the words would be to resources intensive to apply in a real world website.

a better approach would be to calculate every paragraph one time and add the clamped result to a db or store it in the static website.

but then again every browser displays fonts in a different way.

EDIT

Here is a different way to display partial content. Using max-height & -webkit-column-count

https://stackoverflow.com/a/20691677/2450730

DEMO

http://jsfiddle.net/HNF3d/10/

the support is slightly higher than line-clamp and you are abe to display the whole content.

EDIT2

Fading image at the bottom.

p{
 width:300px;
 max-height:300px;
 overflow:hidden;
}
p:before{
 content:"";
 display:block;
 position:absolute;
 margin-top:240px;
 background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0%,rgba(255,255,255,1) 80%);
 height:60px;
 width:300px;
}

http://jsfiddle.net/MM29r/9/

EDIT3

fading image old browsers (use real images links, not base64)

http://jsfiddle.net/MM29r/13/

Community
  • 1
  • 1
cocco
  • 16,442
  • 7
  • 62
  • 77
  • You wrote: "using only javascript would be too complex". What do you mean by "too complex" here? – Sylvain Dec 27 '13 at 15:42
  • it would take 1 or more seconds to calculate every div on low cpu devices.And the function is basically impossible as every browser display's font in a different way. – cocco Dec 27 '13 at 15:46
  • Your demos for `-webkit-line-clamp` do not represent my use case because in your demo, each document (that you've represented as a `

    `) has a constant `line-height`. Here is an example that is closer to my use case: http://jsfiddle.net/MM29r/4/

    – Sylvain Dec 27 '13 at 15:47
  • oh so you have different fontsizes in each p (or div).. yeah that makes everything a little harder. You need to apply that calculation to each element inside each p (or div) sum that values and add the clamp only to the last element based on the space remaining. – cocco Dec 27 '13 at 15:50
  • inside a loop: if((p.el[i].offsetHeight+currentheight)>300){applyclamp(p.el[i])}; – cocco Dec 27 '13 at 15:54
  • But imagine there is a `p` in the document that has its `display` set to `inline`. I can't simply go over each known block element and calculate their line-height. For each element I'd have to check whether or not it's hidden, collapsed, inline, floated or uses a positioning different then the default. – Sylvain Dec 27 '13 at 16:00
  • yes it's a mess. your trying to do somethimg almost impossible. Use max-height and add a fading image over the bottom.and forget the 3 dots.i write you an example. – cocco Dec 27 '13 at 16:02
  • 3 dots in a new line ... bah.. sizes differ they are not exactly 300px or less. – cocco Dec 27 '13 at 16:15
  • Nice! I really your fading image over the bottom technique. I don't want the ellipsis anyways. – Sylvain Dec 27 '13 at 16:15
  • and again.. as you can see in the :before image, there is no javascript needed. javascript needs to use alot cpu to calculate.and it's not nice like real line clamp. – cocco Dec 27 '13 at 16:18
  • About Clamp.js, I don't really mind that the div don't end up exactly the same height. But your solution is so much simpler and the risks of bugs are almost non-existent. I have to make it IE8+ friendly but that, hopefully, will not be a problem. – Sylvain Dec 27 '13 at 16:21
  • Please add your "fading image over the bottom" technique as an other option to your answer and I'll accept your answer. Thanks. – Sylvain Dec 27 '13 at 16:23
  • ie 8 does not support before, so you need to add an image gradient png at the bottom of each p. – cocco Dec 27 '13 at 16:23
  • 1
    added fading image for older browsers. – cocco Dec 27 '13 at 16:38
  • `line-clamp` doesn't work on "modern browsers". It is an experimental implementation in Webkit, there is no specification, and the property is dependent on an outdated implementation of flexbox. It is less likely that other browsers will support the property than it is that it gets removed from Webkit altogether. – djk Feb 15 '18 at 19:40
3

One alternative is to use the dotdotdot jQuery plugin.

Its used like

$("div.text_element").dotdotdot({
  ellipsis : "...",
  wrap : "word"
});

This way, you can just concern yourself with the div dimensions rather than line height or other CSS attributes. Also, it allows you to trigger events to show and hide the hidden text.

Jason
  • 11,263
  • 21
  • 87
  • 181
  • dotdotdot was the only one that worked for me (fixed-height container with a long link inside) in all browsers. – parrker9 Jun 18 '14 at 10:54
1

You should look for line clamping techniques

A list of them can be found here http://css-tricks.com/line-clampin/

As you can see the above link explains various methods to achieve line clamping, but only one of them is truly a cross browser solution. There seems to be a javascript library that solves this problem exactly, and it works even if you use various font sizes or styles

Clamp.js [ https://github.com/josephschmitt/Clamp.js ]

Here is an example

var paragraph = document.getElementById("myParagraphId");

$clamp(paragraph, {clamp: 3});
Igor Šarčević
  • 3,481
  • 1
  • 19
  • 21
  • 1
    +1... but you really ought to include a summary of the techniques in your link. It will protect against your link ever going down, as well as allowing S.O. users to find the answer without going off-site ... which is perhaps why someone else just down-voted you while I was writing this comment. – Zach Lysobey Dec 26 '13 at 23:55
  • If the link is dead, your post is useless. – Felix Kling Dec 27 '13 at 00:05
  • The simple fact that he put a name on the problem is helpful (clamping). – Sylvain Dec 27 '13 at 15:28
1

You could definitely use Clamp.js, which is a JavaScript plugin created by Joseph Schmitt. The minified version of the code can be found here.

You could then use it like this:

var elem = document.getElementsByTagName("div");

for(var z=0;z < elem.length; z++){
  $clams(elem[z], {clamp: '300px'});
}

Alternatively, you could use getElementsByClassName if not all your <div>s needed clamping.

JCOC611
  • 19,111
  • 14
  • 69
  • 90
0

Here what I would do in this case;

First we have to get the div and find out the line-height so I am assuming you got your div as jQuery object.

var $divToClamp = $("#");
var $cloneDiv = $divToClamp.clone();
$divToClamp.insertAfter($cloneDiv.html("A"));
// created a new div as same place with the div to get same css, from parents, class etc.
// i don t know how jQuery handles the ids you must check that
var lineHeightToClamp = $cloneDiv.height() * 3;
$cloneDiv.remove();
// remove the clone we are done with it this does not work create clone div as fixed position back of the actual div and visibility hidden (not display:none)
//now we now the line-height for 3 lines set the css
$divToClamp.css({
      overflow : "hidden",
      lineHeight: lineHeightToClamp
    });

some thing similar to this should fix you case but there might be some exceptions like margin of the div i am not sure $cloneDiv.height() includes them or not.

also if there is another element (like span) in your div with different css that will also change the situation.

Hope this helps.

Sylvain
  • 19,099
  • 23
  • 96
  • 145
Onur Topal
  • 3,042
  • 1
  • 24
  • 41