Some background: I have a div in which elements of different height will be added to and I'm in need of achieving the following:
The div has a max-height property, when the different elements that are added to the Div overlap such height, I can't have the div "overflowing (putting a scrollbar on it)". Instead, I need to detect when this happens, so I can create ANOTHER div in which I could put the rest of the elements. Attached is an image that I hope illustrates what I'm trying to do.
Use jQuery:
var maxHeight = $(".someElement").css("max-height");
var height = 0;
$(".elements").each(function(){
height += $(this).height();
if(height >= maxHeight){
//create new div here and put the rest of the elements there
height = 0; //so you can continue with the loop and creating more divs.
}
});
I have a pseudo function below that I think could get you started on the right track. You will have to fill in the appropriate information for it.
$(elements).each(function() {
var currentDiv = $(currentDiv);
if($(currentDiv ).height() > MAX_HEIGHT)
{
$(currentDiv).insertAfter(newDiv);
currentDiv = $(newDiv);
}
$(currentDiv).append(element);
});
You'll have to keep track of the current div you are adding info to. Just add info like normal but when it overflows you should insertAfter it a new div and change the current div variable to be that one and then continue appending again.
To test if a div is currently overflowing, compare it's scrollHeight to its height.
With jQuery
if ($(obj)[0].scrollHeight > $(obj).height()) {
// do stuff
}
In this case though, you'll probably want to test against the css max-height before adding content. To do this (again in jQuery) load the content you plan to add into a variable so you can check its height before adding it to the document.
var content = // your content here
if ($(container).height() + content.height() > parseInt($(container).css("max-height"), 10)) {
// this means it would overflow, so do stuff
} else {
// no overflow here
$(container).append(content);
}
Here's a quick fiddle. http://jsfiddle.net/k0g47xdr/2/
edit:
the parseInt call around .css("max-height") is to convert from the text format it comes in to a regular number. As written it assumes the value is in px, not em or percent.
I'm trying to make a kind of "news bar" on a web page.
The bar use a CSS animation, and the text content is refreshed on each iteration using ajax to retrieve data from an RSS feed.
My problem is that the content length may vary, so I want to set the size of the font used to display the text small enough to display all the content.
I then use this function to resize the text content:
function resize(content /*content div*/, maxHeight/*bar height*/, maxWidth /*bar width*/) {
content.parentNode.style.fontSize = maxHeight + 'px';
var totalwidth = content.offsetWidth;
if (totalwidth > maxWidth) {
content.parentNode.style.fontSize = (maxHeight * maxWidth / totalwidth) + 'px';
}
}
It sometimes works on first iteration, but after it returns totally wrong values (like 36) for totalWidth.
If I put a breakpoint on the 'if' line the debugger display a coherent value (like 4320) for content.offsetWidth.
How can I fix this ?
I've found the solution.
I have to set the content as "inline-block".
If content display is set as "block" its offsetWidth vary with the css animation.
I have an Html/JavaScript application that contains N columns that need to be large enough contain all of the possible LI elements from all of the columns.
The simple solution seems to count the heights of all of the items in each column, compensate for padding, and then set the height to that total for each of the columns.
This works great when the LI elements contain plain text. Unfortunately, when the LI elements contain images instead, various browsers have problems. For example, when I first load the page in FireFox, it looks like the screenshot below, but upon another refresh, it works fine. It doesn't work as expected in Chrome either.
My application does not pre-populate the LI elements when the page loads - it uses JavaScript, as follows:
function populateUnsetAnswers(unsetCategoryAnswers) {
for (i in unsetCategoryAnswers) {
if (unsetCategoryAnswers.hasOwnProperty(i.toString())) {
$('#categoryQuestionArea #possibleAnswers').append(
categoryAnswerLiTag(unsetCategoryAnswers[i])
);
}
}
}
function categoryAnswerLiTag(unsetCategoryAnswer) {
var html = '<li id="' + unsetCategoryAnswer.id + '">';
if (unsetCategoryAnswer.image) {
html += '<img class="categoryAnswerImage" title="';
html += unsetCategoryAnswer.text;
html += '" src="/trainingdividend/rest/streaming/';
html += unsetCategoryAnswer.image.fileName;
html += '" style="height: ';
html += unsetCategoryAnswer.image.height;
html += ';';
html += '" />';
} else {
html += unsetCategoryAnswer.text
}
html += '</li>';
return html;
}
When the page is done loading, an ajax request fetches all of the objects to be put into LI elements, and then calls the first function above.
After all of the LI elements are created, I call this function right after it:
function resize() {
var currentHeight, totalHeight;
totalHeight = 0;
$("#categoryQuestionArea ul").children().each(function() {
currentHeight = $(this).height();
totalHeight += currentHeight + 13;
});
$("#categoryQuestionArea ul").height(totalHeight);
$("#categoryQuestionArea div#separator").css("padding-top", (totalHeight / 2) + "px");
}
Is there any way to tell jQuery, "Don't call resize() until all of the LI's are fully loaded and the images have rendered" ?
I think what's happening is that on the initial page load, the height of these LI elements is 0 or a small value because it doesn't contain the image, so my resize function is calculating the wrong result (I tested this with some alert statements). As long as the LIs are populated and the images have loaded, the total height is calculated just fine.
Any help? Thanks
To literally answer the question you asked, if you want to only call resize() when all images have finished loading, then you need to install onload handlers for those images and when you've recorded that the last one is now loaded, you can call the resize() function. You could do that like this (code explanation below):
var remainingAnswerImages = 0;
function categoryAnswerImageLoadHandler() {
--remainingAnswerImages;
if (remainingAnswerImages === 0) {
resize();
}
}
function populateUnsetAnswers(unsetCategoryAnswers) {
// add one extra to the image count so we won't have any chance
// at getting to zero before loading all the images
++remainingAnswerImages;
var possibleAnswers$ = $('#categoryQuestionArea #possibleAnswers');
for (i in unsetCategoryAnswers) {
if (unsetCategoryAnswers.hasOwnProperty(i.toString())) {
possibleAnswers$.append(categoryAnswerLiTag(unsetCategoryAnswers[i]));
}
}
// remove the one extra
--remainingAnswerImages;
// if we hit zero on the count, then there either were no images
// or all of them loaded immediately from the cache
// if the count isn't zero here, then the
// categoryAnswerImageLoadHandler() function will detect when it does hit zero
if (remainingAnswerImages === 0) {
resize();
}
}
function categoryAnswerLiTag(unsetCategoryAnswer) {
var obj = document.createElement("li");
obj.id = unsetCategoryAnswer.id;
if (unsetCategoryAnswer.image) {
// count this image
++remainingAnswerImages;
var img = new Image();
img.onload = img.onerror = img.onabort = categoryAnswerImageLoadHandler;
img.title = unsetCategoryAnswer.text;
img.style.height = unsetCategoryAnswer.image.height;
img.src = "/trainingdividend/rest/streaming/" + unsetCategoryAnswer.image.fileName;
obj.appendChild(img);
} else {
obj.innerHTML = unsetCategoryAnswer.text;
}
return obj;
}
By way of explanation, this code makes the following changes:
Add a variable remainingAnswerImages to keep track of how many images still need to be loaded.
Add an onload handler for each <img> tag that is created so we can keep track of when it's loaded.
Each time we generate the HTML for an tag with the onload handler, increment remainingAnswerImages.
When you're done adding all the HTML, check the remainingAnswerImages count to see if it's zero (this would only be the case if there were no images or if all images loaded immediately from the browser cache). If so, call resize() immediately.
In the onload handler which will be called for each image, decrement remainingAnswerImages and if the count has reached zero, call resize().
While adding images, add one extra to remainingAnswerImages as a gate to keep from getting to a zero count until we're done adding images. When done adding images, take that one extra out.
I also rewrote the categoryAnswerLiTag() function to just create the DOM objects directly rather than concat a bunch of strings together into HTML. In this case, the code is a lot cleaner to read and maintain.
I also moved the $('#categoryQuestionArea #possibleAnswers') out of your for loop since it resolves to the same thing every time. Better to do it once before the loop. Also, in most cases, this could be simplified to $('#possibleAnswers') since ids are supposed to be unique in the page.
Here is a jquery plugin that checks the images have loaded: https://github.com/alexanderdickson/waitForImages
Sample usage for your case would be:
$('#categoryQuestionArea').waitForImages(function() {
resize();
});
I would also just check for the total height of the <ul> instead of looping through the list items as you would have to manually change the script if either the padding, margins, or borders on the list items changes later on.
If you do have problems with images on the first page load maybe it is due to the fact that they are not cached and therefore not immediately available. So measuring their height will lead to bad results... did you debug the height that was fetched via jQuery (for example
currentHeight = $(this).height();
console.log(currentHeight);
The only way to do that is I think to observe the load events of all images (and probably the error as well) and count whether all request have been finished
try using
$('img').load(function(){
//put code here
});
I guess your HTML is screwed up. Particularly, your <img> tags.
Add the width and height attributes to your <img> tags. Everything will be magically solved.
See this jsfiddle to understand what I mean: http://jsfiddle.net/Ralt/Vwg7P/
Even though there is no image in there, the width and height attributes will occupy the space required for the image. As soon as the DOM is loaded.
This is rather a CSS problem, most probably due a fixed height, with items either floated or absolutely positioned.
There are number of ways to fix this.
Give a min-height instead of fixing a height.
#container { min-height: 100px; }
Clear the float and do not set any heights
#container { overflow: hidden; }
Use Scripts to add up to the height, once every element is added. Like the jQuery snippet below
$("#container").append($("#theimg"));
$("#container").height($("#container").height()+$("#theimg").height());
I think I might have a solution for you.
The main idea of my solution lies in CSS. You want to have 3 columns of the same height, right? You can have something like this: http://jsfiddle.net/agilius/NvzZp/46/
There is quite a lot of CSS there, but the main idea is this:
I simulate a 3 column layout under the actual content, with the .inner and .column classes.
The content is placed over (via z-index 2 > .inner zindex 1), with the same width as the columns that are under.
When content is added to the content zones, the height of the main #container updates.
Since .inner is top,left,right,bottom = 0, it updates, and since the .columns have 100% height, they update their height to match the #containers height.
Observations.
You can have padding, borders, margins in the .column class as you see fit.
No javascript is required.
Another simple Equal Height CSS solution:
LOGIC is very simple -
all of the columns/LI are floated
with .eH{ padding-bottom: X; margin-bottom: -X } and
wrapper/UL is .eW{overflow: hidden}
X= large arbitrary amount of px for factor of safety
EXAMPLE:
http://jsfiddle.net/rahen/TXVYD/4/
This sounds exactly like one of the problems i had when coding SudoSlider.
Below i've copied the code i solved it with. Just call autoheightwidth(i, 0, true) inside your resize() function.
The basic idea is that you do not know when the browser has completed loading the images, so instead of relying on a single height adjustment, you adjust the height every time something happens (mostly just that an image has been loaded).
It should work if you change the references of "obj" and "li" in the first 2 methods.
It's not very readable, but there was a big focus on size when i coded it.
// Automaticly adjust the height and width, i love this function.
// Before i had one function for adjusting height, and one for width.
function autoheightwidth(i, speed, axis) // Axis: true == height, false == width.
{
obj.ready(function() {// Not using .load(), because that only triggers when something is loaded.
adjustHeightWidth (i, speed, axis);
// Then i run it again after the images has been loaded. (If any)
// I know everything should be loaded, but just in case.
runOnImagesLoaded (li.eq(i), falsev, function(){
adjustHeightWidth (i, speed, axis);
});
});
};
function adjustHeightWidth (i, speed, axis)
{
var i = getRealPos(i); // I assume that the continuous clones, and the original element is the same height. So i allways adjust acording to the original element.
var target = li.eq(i);
// First i run it. In case there are no images to be loaded.
var b = target[axis ? "height" : "width"]();
obj.animate(
axis ? {height : b} : {width : b},
{
queue:falsev,
duration:speed,
easing:option[8]/*ease*/
}
);
}
function runOnImagesLoaded (target, allSlides, callback) // This function have to be rock stable, cause i use it ALL the time!
{
var elems = target.add(target.find('img')).filter('img');
var len = elems.length;
if (!len)
{
callback();
// No need to do anything else.
return this;
}
function loadFunction(that)
{
$(that).unbind('load').unbind('error');
// Webkit/Chrome (not sure) fix.
if (that.naturalHeight && !that.clientHeight)
{
$(that).height(that.naturalHeight).width(that.naturalWidth);
}
if (allSlides)
{
len--;
if (len == 0)
{
callback();
}
}
else
{
callback();
}
}
elems.each(function(){
var that = this;
$(that).load(function () {
loadFunction(that);
}).error(function () {
loadFunction(that);
});
/*
* Start ugly working IE fix.
*/
if (that.readyState == "complete")
{
$(that).trigger("load");
}
else if (that.readyState)
{
// Sometimes IE doesn't fire the readystatechange, even though the readystate has been changed to complete. AARRGHH!! I HATE IE, I HATE IT, I HATE IE!
that.src = that.src; // Do not ask me why this works, ask the IE team!
}
/*
* End ugly working IE fix.
*/
else if (that.complete)
{
$(that).trigger("load");
}
else if (that.complete === undefined)
{
var src = that.src;
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
// data uri bypasses webkit log warning (thx doug jones)
that.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; // This is about the smallest image you can make.
that.src = src;
}
});
}
I think the Browser does not know the images' dimensions, because they are not loaded.
Either try to wrap the invocation of resize in a
jQuery(document).load( function funcName() {
...
} )
or give the image width and height attributes in the HTML img tags.
Maybe both
I hate doing this. This is THE small piece to end a large project and my mind is fried...
Here's the code. It checks to see if an element is overflowing and resizes the font. It is supposed to resize it until it doesn't overflow. The condition for the loop seems to be ignored and the browser freezes... I feel that I'm missing something crucial in how jQuery works here.
$.fn.fontBefitting = function() {
var _elm = $(this)[0];
var _hasScrollBar = false;
while ((_elm.clientHeight < _elm.scrollHeight) || (_elm.clientWidth < _elm.scrollWidth)) {
var fontSize = $(this).css('fontSize');
fontSize = parseInt(fontSize.substring(0,fontSize.length-2))*0.95;
$(this).css('fontSize',fontSize+'px');
}
}
Thanks in advance.
Change:
fontSize = parseInt(fontSize.substring(0,fontSize.length-2))*0.95;
to:
fontSize = parseInt(fontSize.substring(0,fontSize.length-2))-1;
Here's a Working Demo. When the font size reached 10px, 10*.95 was 9.5 which the browser was rounding up to 10px. Thus infinite loop.
You need to step through your code in a debugger and actually check your condition values to make sure they are changing how you expect. My guess is _elm.clientHieght and _elm.clientWidth aren't actually changing.
var fontSize = $(this).css('fontSize');
fontSize = parseInt(fontSize, ...
The unit you get from font-size is not necessarily (a) pixels, nor (b) the same unit as you put in.
It's not specified what unit is used to return the length, but in many browsers it is currently points. Since points are smaller than pixels, the integer length will be longer, so you can quite easily keep on *0.95ing it forever.
Even if it were pixels, the browser could round the size up to the nearest pixel, making 95%-size the same size as 100% when you read it back. Or you could hit the minimum-font-size setting and you wouldn't be able to reduce it any more.
So instead of reading the current font size back on each step, keep the pixel size you want in a variable and reduce that variable each time. Then if you reach a predetermined lower bound for the value of that variable, give up.
You are probably running into an endless loop because the font size doesn't actually change. E.g. if the font size found is 10px you will update it to become 9.5px which is probably rounded back to 10px by the browser. In that case nothing changes and the function will keep running forever.
You've got an unrelated problem when you do
$('div').fontBefitting()
This will make the text in the first div fit it's box, then make the font size of all the other divs the same as the first. This does not sound like intended behaviour. You would hope that it would make each div resize its text and only its text to fit.
You need to change your code to this:
$.fn.fontBefitting = function() {
/* $.fn.* runs on a jQuery object. Make sure to return it for chaining */
return this.each(function() {
var fontSize = parseInt($(this).css('fontSize'));
while (this.clientHeight < this.scrollHeight ||
this.clientWidth < this.scrollWidth) {
fontSize--;
$(this).css('fontSize', fontSize + 'px');
}
});
}
You're checking to see if the clientHeight or clientWidth are LESS than the scrollHeight or scrollWidth, and if they are you are REDUCING the font size? It will never converge under those circumstances. You want to INCREASE the font size.
i want to resize the font of all elements of a page
like user can click increaseFont or decreaseFont this will decrease the fonts of all elems
iam doing doing as writen below
please suggest some better way
$.fn.ChangeFont=function(ratio)
{
return this.each(function(){
var _elm=$(this);
var currFontSize = _elm.css('fontSize');
var finalNum = parseFloat(currFontSize, 10);
var stringEnding = currFontSize.slice(-2);
finalNum *= ratio;
finalNum=finalNum + stringEnding;
_elm.css('fontSize',finalNum);
});
}
$(document).find('*').andSelf().ChangeFont(1.2);//a value bigger than 1 for increase
$(document).find('*').andSelf().ChangeFont(.8);//a value smaller than 1 for decrease
Could you not just set the font-size property on the BODY element to be 125% or 150%?
$("body").css("font-size", "150%");
This is dependent on how the rest of your CSS is structured and what units you are using to assign font-sizes in the rest of the page. If you are using em or % then it is enough to do just like Chase Seibert says:
$("body").css("font-size", "150%");
So simply use the correct units in your CSS and then manipulate the main body text-size. Otherwise you can also choose an element like a div which acts as the root element of all textual content on the page you are creating.
As a comment by #praveen also says is that this will not necessarily affect you non-textual content.