How to display only the first few lines of a div (clamping)? - javascript

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.
Show first 3 lines in html paragraph
Show first line of a paragraph

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/

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.

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});

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.

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.

Related

jQuery Add & Remove Class Div Width

How can i add/remove class according as div width ? I tried some codes but I have no idea about jquery codes. I'd like add div class according as antoher div width. Just i need add class like that. If container is smaller than 600px "add class" to content div else "remove class" from content div. These are my codes;
<div class="container">
<div class="content"></div>
</div>
$(window).resizeboxes(function() {
if ($(".container").width < 600){
$( ".content" ).addClass( ".content_600" );
}
});
else{
removeClass('.content_600')
}
$(window).trigger('resizeboxes');
This works, though the code is changed slightly. There were some problems with the syntax also, so I've corrected those (for instance the else statement was slightly misplaced). Here is a working example:
https://jsfiddle.net/vt0nbx36/3/
Here is the code:
var resizeboxes = function() {
if ($(".container").width() < 600)
{
$(".content").addClass("content_600");
}
else
{
$(".content").removeClass("content_600")
}
};
resizeboxes();
$(window).resize(function(){
resizeboxes();
});
For this need exactly, you have jQuery's .toggleClass() function. It takes the class name as a first parameter, and optional second boolean parameter that states wether the class name should be added or removed. You can find the documentation here
$(".content").toggleClass("content_600", ($(".container").width() < 600));
Even tho your question is a JS related question, CSS as a matter of fact can handle this like no other beast can (mostly)!
CSS allows you to use media-queries to resize your content based on the width of the viewport.
The upside of this is that the browser will handle this for you within the rendering engine rather than having JS between your change and the rendering engine.
The major downside is that you can't define the width of element A based on element B but are unfortunately locked to using the viewport as an indicator.
Before I explain why you'd want to use CSS I'd like to point out why you don't want to use JS for this if possible.
The jQuery.resize eventhandler fires inconsistently across browsers and it fires alot of times usually.
This causes your scrolling to clog up and make your webpage feel laggy.
If there's anything your users will dislike it's the fact that scrolling is controlled by something they don't even know of which is slowing you down.
As for a CSS solution, a media query looks like this:
.my-selector {
width: 900px;
}
#media all and (max-width: 600px) {
.my-selector {
width: 600px;
...
}
}
You wrap your code in a sort-of conditional that allows you to be very flexible with manipulating elements on the page.
What happens in the above piece of code is that when the parser reads the CSS it sees the first selector not in a media query so it applies width: 900px; then it sees a media query and sees the other rule for my-selector however it will only apply that rule when the screen is at that width we defined in the #media ... rule. When you resize CSS handles things differently behind the scenes so it's much faster than JS in that case.
I'm not sure if it actually applies to your situation but if your container is sized by the viewport rather than parent elements this should be possible and I thought it'd be nice atleast to show you a good way of playing with element dimensions.
Also, you can use #media to for instance make a webpage print friendly by changing the all to print for example and setting the background-color: transparent for an element - saves ink ^.^ which is an additional extra on top of the general awesomeness of media queries.
Hope it helps, good luck if you wish to make your webpage 5 times faster ;)

Using floats within a grid at variable heights

I have the following grid and each red block is a div. As you can see, the longest div is pushing the bottom two divslower, creating unwanted space.
Here is how I would like to have it, so the space is tighter and not broken by the longest div:
Is there a CSS solution to this, or an alternative to Masonry / Isotope?
There is no way to accomplish what you want using floats. You can however use:
div{
position:absolute;
}
This will achieve what you want because it will allow you to position each box exactly where you want, down to the pixel. Be aware that this will remove each div from the document flow. For that reason and a few others, I recommend constraining the divs within some sort of container that is set to:
position: relative;
This will limit the scope of the absolute positioning.

HTML div with absolute position tries to wrap the text. Can I avoid it, without using white space

I have a set of divs with position = absolute, and they can be positioned across the screen.
If the content of any div doesn't fit on the screen, the browser wraps the text into multiple lines and attempt to fit inside the window.
However, I dont want the browser to do that, It should instead hide the content.
http://jsbin.com/welcome/35835/edit/
Edit:
you may think of it as a div on a page with absolute positioning. and
1) the user can drag the div around
2) user can manually change the width of the div( there is a stretch box widget, which the user can use)..
So the problem is when the user is dragging the div around near the edges of the screen, the text should hide and not wrap if it goes out of the window. Hope this explains better
As shown in the example, block 2 shown is what I want.
So, lets say the width of the div is 100px, and the left position of the CSS style is (screen width - 50), then the rest of the text should hide.
Solution 1: white-space:nowrap. Cant use this, since this is a flexible width UI where user can change the width of the div if they want.
Solution 2: If I set the width of the div, explicitly to a number, it works fine.
But not a optimal solution, as then here I will always have to calculate the width for all divs at the time of rendering.
Is there a more optimal solution, which can make the browser not try to fit the text into the screen.
Hard to tell what you're asking. But I think you can use
{
height: 1.2em;
overflow: hidden;
}
To hide the content that is longer than the one line you support
http://jsfiddle.net/MXXDC/2/
If you put them all inside a huge (e.g. 5k px * 5k px absolute positioned div you should see the expected effect: http://jsbin.com/welcome/35862/edit
Is this what you want? (second item)
I wrapped the inner text in a very long div and applied overflow:hidden to it's parent.
I am not sure the exact use case of the widget so I am not 100% sure on what it can have and not have. I have an idea, maybe it will be useful - setting width to a % might help, something like this
.block2{
left: 50%;
top: 100px;
width: 100%;
}
you can set this in the css to avoid calculation with js, but like I said I am not sure of how this is used so this might not work but it might give you some ideas

Set initial height of div based on how much space is left inside of parent div

I have 3 divs:
.main_content, .content_top, .content_bottom
.main_content is set to 100% but 100% is not the size of the browser window, it's inside the middle of my page.
.content_top is set at 60% height.
I want to set the height of .content_bottom to the rest of the space available inside .main_content via javascript.
For example, if .main_content was 800px high, and .content_top was 600px high, I want to set .content_bottom to 200px.
This is a simplified example, my situation is not as easy as specifying 40% or leaving the browser to decide. For one, there's currently 46px of padding on .content_top. I'm doing a split screen like interface between .content_top and .content_bottom dragging a bar to resize both. This is mostly working, just having trouble with the bottom portion. Being able to set .content_bottom to a specific height(i.e. 198px) would solve all of my current problems. Happy to elaborate on this example, as well as dig into some actual code, but was hoping there was an easy method for calculating this and was having trouble finding a good example that worked cross-browser, thanks!
Assuming you're using vanilla JavaScript (and not a library), I'd suggest:
​var cBs = ​document.getElementsByClassName('content_bottom');
for (var i=0,len=cBs.length; i<len; i++){
var p = cBs[i].parentNode;
cBs[i].style.height = (parseInt(p.offsetHeight,10) - parseInt(p.getElementsByClassName('content_top')[0].offsetHeight,10)) + 'px';
}​​​​​​​​​​
JS Fiddle demo.
Simple jQuery solution:
$("content_bottom").css({"height": $("main_content").height() + $("content_top").height());

jQuery slide is jumpy

I tried to slide in and out a DIV with the toggle function of jQuery but the result is always jumpy at the start and/or end of the animation. Here's the js code that I use:
$(document).ready(function(){
$('#link1').click(
function() {
$('#note_1').parent().slideToggle(5000);
}
);
And the HTML:
<div class="notice">
<p>Here's some text. And more text. <span id="link1">Test1</span></p>
<div class="wrapper">
<div id="note_1">
<p>Some content</p>
<p>More blalba</p>
</div>
</div>
</div>
You can also see the complete example here: jQuery Slide test
I usually use Mootools and I can do this slide without any problems with it. But I'm starting a new project in Django and most app in Django use jQuery. So for that and after reading this jQuery vs Mootools I decided that will be a good occasion to start using jQuery. So my first need was to slide this DIV. And it didn't work properly.
I did more search and I found that's an old bug in jQuery with margin and padding applied to the DIV. The solution is to wrap the DIV in another DIV. It didn't fix the thing in my case.
Searching further I found this post Slidedown animation jumprevisited. It fix a jump at one end but not at the other (Test2 in jQuery Slide test).
On Stack Overflow I found this jQuery IE jerky slide animation. In the comments I saw that the problem is with the P tag inside the DIV. If I replace the P tags with DIV tags that fix the problem but that's not a proper solution.
Lastly I found this Weird jQuery behavior slide. Reading it I understood that the problem resolved by switching from P tag to DIV was with the margins of the P (not present in the DIV) and the collapsing of margins between elements. So if I switch the margins to paddings it fix the problem. But I loose the collapsing behavior of margins, collapsing that I want.
Honestly I can say that my first experience with jQuery is not really good. If I want to use one of the simplest effect in jQuery I have to not use the proper function (slideToggle) but instead use some hand made code AND wrap the DIV in another DIV AND switch margins to paddings, messing my layout.
Did I miss a simpler solution ?
As krdluzni suggest, I tried to write as custom script with the animate method. Here's my code:
var $div = $('#note_2').parent();
var height = $div.height();
$('#link2').click(
function () {
if ( $div.height() > 0 ) {
$div.animate({ height: 0 }, { duration: 5000 }).css('overflow', 'hidden');
} else {
$div.animate({ height : height }, { duration: 5000 });
}
return false;
});
But that doesn't work either because jQuery always set the overflow to visible at the end of the animation. So the DIV is reapearing at the end of the animation but overlaid on the rest of the content.
I tried also with UI.Slide (and Scale and Size). It works but the content below the DIV doesn't move with the animation. It only jump at the end/start of the animation to fill the gap. I don't want that.
UPDATE:
One part of the solution is to set the height of the container DIV dynamically before anything. This solve one jumping. But not the one cause by collapsing margin. Here's the code:
var parent_div = $("#note_1").parent();
parent_div.css("height", parent_div.height()+"px");
parent_div.hide();
SECOND UPDATE:
You can see the bug on the jQuery own site at this page (Example B):
Tutorials:Live Examples of jQuery
THIRD UPDATE:
Tried with jQuery 1.4, no more chance :-(
I found what works consistently is setting an invisible 1px border:
border: 1px solid transparent;
No need to fix the width or height or anything else and the animation doesn't jump. Hooray!
The solution is that sliding div must have the width set in pixels. Do not use 'auto' nor '%'. And you will have great result! The problem is in inline elements thats are in a sliding div.
but if they have width in px the height will be identical. Try it.
I've ran into this problem today. I did notice however that disabling all CSS fixed the problem. Also I knew it worked fine before so it must have been recent changes that caused the issue.
It turned out I used transitions in CSS to ease in and out of hovers.
Once these transitions were removed from the elements I was adding everything was fine.
So if you have the same issue, just add these lines to the elements you're adding:
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
-ms-transition: none;
transition: none;
(I might have abused transitions a bit by not just adding them to the elements I want to have transitions for, but using them for the entire website.)
Try removing all CSS margins from all the elements. Usually jerky animation comes from margins not being taken into account by the animation framework.
Jerking happens when the parent div ".wrapper" in your case has padding.
Padding goes on the child div, not the parent. jQuery is animating height not padding.
Example:
<div class="notice">
<p>Here's some text. And more text. <span id="link1">Test1</span></p>
<div class="wrapper" style="padding: 0">
<div id="note_1" style="padding: 20px">
<p>Some content</p>
<p>More blalba</p>
</div>
</div>
</div>
Hope this helps.
I find animate() is the most reliable way to animate anything in jQuery (cross browser at least).
This dynamically wraps the content in a div, then animates the height of that div wrapper by using the height of its inner content.
http://jsfiddle.net/BmWjy/13/
$('a').click(function(event) {
event.preventDefault();
xToggleHeight($(this).next());
});
//For each collapsible element.
$('.collapsible').each(function() {
//Wrap a div around and set to hidden.
$(this).wrap('<div style="height:0;overflow:hidden;visibility:hidden;"/>');
});
function xToggleHeight(el){
//Get the height of the content including any margins.
var contentHeight = el.children('.collapsible').outerHeight(true);
//If the element is currently expanded.
if(el.hasClass("expanded")){
//Collapse
el.removeClass("expanded")
.stop().animate({height:0},5000,
function(){
//on collapse complete
//Set to hidden so content is invisible.
$(this).css({'overflow':'hidden', 'visibility':'hidden'});
}
);
}else{
//Expand
el.addClass("expanded").css({'overflow':'', 'visibility':'visible'})
.stop().animate({height: contentHeight},5000,
function(){
//on expanded complete
//Set height to auto incase browser/content is resized afterwards.
$(this).css('height','');
}
);
}
}
You could write a custom animation using the animate method. This will give you absolute control over all details.
I noticed if you have a <br /> after your container <div> the animation will also be jumpy. Removing this resolved my problem.
css padding and jquery slideToggle doesn't work well together. Try to box out padding.
There are obviously a lot of different solutions to this issue - and depending on your layout, different solutions have different results.
Here was what I had (stripped down)
<div>
<p>Text</p>
</div>
<div class="hidden">
<p></p>
</div>
When I would use jQuery to show <div class="hidden">, the margin on the <p> element would collapse with the margin of the <p> element above it.
I thought it was strange since they were in different <divs>.
My solution was to eliminate the margin on the bottom of the <p>. Having a margin on one side prevents the margin from the bottom of the first <p> from collapsing with the top of the second <p>.
This workaround solved my problem, can probably be applied to others, but may not work for all.
You just have to modify the up, down effects in effects.js to have them take into account margins or paddings that may exist and then adjust what they perceive to be the total size of the element to accommodate those values...something along these lines....
Effect.BlindDown = function(element) {
element = $(element);
var elementDimensions = element.getDimensions();
//below*
var paddingtop = parseInt(element.getStyle('padding-top'));
var paddingbottom = parseInt(element.getStyle('padding-bottom'));
var totalPadding = paddingtop + paddingbottom;
if(totalPadding > 0)
{
elementDimensions.height = (elementDimensions.height - totalPadding);
}
//above*
return new Effect.Scale(element, 100, Object.extend({
scaleContent: false,
scaleX: false,
scaleFrom: 0,
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
afterSetup: function(effect) {
effect.element.makeClipping().setStyle({height: '0px'}).show();
},
afterFinishInternal: function(effect) {
effect.element.undoClipping();
}
}, arguments[1] || { }));
};
Try setting the 'position' property of the the container (in this case the .notice div) to 'relative'.
Worked for me.
Source: slideToggle height is "jumping"
There are a lot of suggestions here and a lot of back and forth as to what works. For me, the behavior problem was when the animation of expanding the container would over expand and then bounce back to the correct expansion height (all done as part of the one animation). In way of example, the animation would expand to a height of 500px initially and then retract to 450px. There was no problem with collapse.
The solution that worked was to add to the expanding/collapsing div, a CSS of:
white-space: nowrap;
That worked perfectly - smooth expansion to the correct height.
I had the same issue, but not a single one of the proposed solutions worked for me, so I propose a solution that eliminates relying on slideToggle() altogether.
Spark Notes: Load the page as normal, collect the height of each element you want to toggle, store that height in a special data attribute, and then collapse each element. Then it's as easy as changing the max-height between the value in the element's data-height attribute(expanded) and zero(collapsed). If you want to add extra padding and margins, etc to the elements, I recommend storing those in a separate CSS class to add and remove with the max-height property.
Place the jQuery right after the elements you want to toggle and allow them to execute during page load (so you don't have to watch them all load and then collapse).
HTML
<ul id="foo">
<li>
<h2>Click Me 1</h2>
<div class="bar">Content To Toggle Here 1</div>
</li>
<li>
<h2>Click Me 2</h2>
<div class="bar">Content To Toggle Here 2</div>
</li>
</ul>
CSS
#foo>li>div.bar {transition: all 0.5s;
overflow: hidden;}
jQuery
$('#foo h2').each(function(){
var bar = $(this).siblings('.bar');
bar.attr('data-height', bar.height()); //figure out the height first
bar.css('max-height', '0px'); //then close vertically
});
$('#foo h2').click(function(){
var bar = $(this).siblings('.bar');
if ( bar .css('max-height') == '0px' ){ //if closed (then open)
bar.css('max-height', bar.data('height') + 'px');
} else { //must be open (so close)
bar.css('max-height', '0px');
}
});
Here is a working JSFiddle: http://jsfiddle.net/baacke/9WtvU/
The problem is that you are performing the action on the parent, doing this removes the CSS related to that element.
You need to run the slide on your note1, not the parent of note 1.
I had the same issue and fixed it by moving down a level.
For me removing the min-height from my container solved the problem.
You might try adding a doctype if you don't have one, it worked for me on IE8 after I found the suggestion here on SO: jQuery slideToggle jumps around. He suggests a strict DTD but I just used the doctype that google.com uses: <!doctype html> and it fixed my problem.
i came across the same bug took days to find a solution. the problem is when the element is hidden jquery is getting the wrong height. top fix it you must get the hight before hiding and use a custom animation to that height. its tricky go here for a better explanation
I had the same problem with 'jerkyness' with divs inside my nav tag - my aim is to show an unordered list on hover of the div (if one exists). My lists are dynamically created so they do not have a fixed value.
Heres the fix:
$("nav div").hover(
function() { // i.e. onmouseover function
/****simple lines to fix a % based height to a px based height****/
var h = jQuery(this).find("ul").height(); //find the height
jQuery(this).find("ul").css("height", h);
//set the css height value to this fixed value
/*****************************************************************/
jQuery(this).find("ul").slideDown("500");
},
function(){ // i.e. onmouseout function
jQuery(this).find("ul").slideUp("500");
});
});
Ran into this issue today, saw this question, and started tinkering based on what I saw here. I solved our jumpy issue by removing the position:relative from the CSS of the containing div. No more weirdness after that. My 2 cents.
Make sure you don't have CSS transition rules set globally or on your container or any included elements. It will also cause jerkiness.
In my case I solved it adding style="white-space:nowrap;" to the element to prevent miscalculations on the jQuery function; no need to set a fixed width unless you need to wrap.
I was using slideDown() like this
$('#content').hide().delay(500).slideDown(500);
For me, it was the main container #content element. I was having it hidden and then calling slideDown(). I removed the padding property in the CSS and everything worked fine after that. It's usually a margin, padding, or % width, so the easiest method is commenting out each property and testing them 1 by 1 to get your results.
I just learned that this problem can also occur if there are floated elements in the expanding/collapsing element. In that case, a clearfix (clear: both;) at the end (still within) the animated element can get rid of it.
I had the same issue. I fixed it by adding this:
.delay(100)
Guess giving it more time to think helps it along?
Adding my solution: turned out my issue was flexbox (only in chrome). I had align-items: baseline; set on the parent element. Set align-self: center; to my slideToggling full-width child element and it cleared it right up. Great use of two hours.
For me the solution was, that i had a CSS style definition like following:
* {
transition: all .3s;
}
Removing this was the solution!

Categories