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:
remainingAnswerImages
to keep track of how many images still need to be loaded.<img>
tag that is created so we can keep track of when it's loaded.remainingAnswerImages
.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.remainingAnswerImages
and if the count has reached zero, call resize()
.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.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.$('#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.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With