javascript loaded img still has 0 height - javascript

This problem is annoying me because it works with one set of images and not with another.
I have a this object:
function PreLoader(toLoad, parent, images) {
var errored = 0;
var loaded = 0;
var toLoad = toLoad;
function allLoaded() {
// reset the counters so it can be used again
loaded = 0;
errored = 0;
// determine which img is the tallest
var l = 0;
for (var i = 0; i < images.length; i++) {
l = (l > images[i].height()) ? l : images[i].height();
}
// set the height of the container to the tallest
// unless it's already bigger
// height() is from jQuery
if (parent.obj.height() < l)
parent.obj.css("height", l);
}
this.load = function() {
++loaded;
if (loaded + errored == toLoad)
allLoaded();
};
this.error = function() {
++errored;
if (loaded + errored == toLoad)
allLoaded();
};
}
I have been using it in a similar way to this:
var parent = {obj: $("#some-img-container")};
var slabLoader = new PreLoader(2, parent, [external.slab, internal.slab]);
external.slab.src = "2.png";
external.slab.onload = slabLoader.load;
external.slab.onerror = slabLoader.error;
internal.slab.src = "1.png";
internal.slab.onload = slabLoader.load;
internal.slab.onerror = slabLoader.error;
The problem is, sometimes it doesn't work. I have multiple sets of images that are absolute positioned because they are layered on top of each other, but they can be different heights. Obviously I can't hard code the heights because I don't know what they are before the page loads... and because absolute positioned elements don't affect their parents size I can't rely on things like height: 100%.
var l in the allLoaded() function sometimes returns 0 even though the images should be loaded by the time that is called.
Am I correct in this statement or am I doing something wrong and it only works sometimes because of luck?
The html looks like this:
<div class='wrapper-hover'>
<div class='wrapper-content'>
<a herf='#' id='some-img-container' class='viewport'>
<!-- this is where the image goes, added by the script -->
</a>
<div class='wrapper-caption'>
<label class='wrapper-caption-content'>Some Image</label>
</div>
</div>
</div>
Sorry if the question is a bit difficult to understand.
UPDATE:
If I use .naturalHeight instead of jQuery .height()I get the height of the actual image on disk and not the height that it would take up after it has been scaled by css (I'm using width: 100% and leaving the height undefined). However it still does nothing for the images that returned 0 before.
I can confirm that all the images claim that .complete is true before I try to access their heights.

The working version of the resize function is as follows:
function resize( parent, images ) {
// The largest height seen so far.
var l = 0;
// Loop through all the images to
// determine which is the tallest.
for ( var i = 0; i < images.length; ++i ) {
// Create a variable to hold the current value.
// This just means that we can save time on multiple
// array access calls.
var c = images[i];
// If the current image is actually
// visible to the user.
if ( c.offsetParent !== null )
// If the largest so far is the biggest then
// keep it, otherwise change it to the new
// biggest value.
l = ( l > c.height ) ? l : c.height;
}
// If the value of 'l' is 0 then it is likely
// that the all the images are hidden. In
// which case skip the resizing.
if ( l === 0 )
return;
// Set the height of the parent to 'l'
// this ensures that the parent will resize
// even if the window is made smaller.
// adding 2 pixels for good luck :P
parent.setAttribute( "style", "height:" + (l + 2) + "px" );
}

Related

How to preload an image and examine its properties before display

This is for a carousel side show. I'd like to place the fwd arrow at a fixed location, which would be the location of the widest picture plus an increment. So I'd like to find the width of the widest picture in the array before displaying any of them.
This does not work. What should I do instead? I'm not using JQuery or Angular. Barebones.
I see a previous answer to a similar question, but that will not work for me because it assumes that the number of images is fixed. In my case, they will vary from site to site.
To be included in js.config for every web site:
var pictures = ["CodeTalker.jpg", "challenger.jpg", "Hotline.jpg", "EarliestDigraphic.jpg", "Bombe.jpg", "SIGABA.jpg", "SIGCALYSign.jpg", "Cray.jpg"];
var maxWidth = 0;
var image = new Image()
for (picture in pictures) {
image.src = picture;
image.loading = "eager";
if (image.naturalWidth > maxWidth) {
maxWidth = image.naturalWidth;
}
}
var currentPic = 0;
When executed, the natural length always comes back zero.
Hi #Rin,
The issue with your code is the image.naturalWidth property is always returning 0. As Mr.#Dave Newton described in the above comment, The image object is not properly being loaded before its properties are being accessed.
for (var i = 0; i < pictures.length; i++) {
var image = new Image();
image.src = pictures[i];
image.onload = function() {
if (this.naturalWidth > maxWidth) {
maxWidth = this.naturalWidth;
}
}
}
onload event handler function when image is fully loaded. To ensure that the image is fully loaded before its properties are accessed.

Restart a for loop after the end of an array

I working in JavaScript and am trying to add some images to a site.
I have a list of blog posts on a page. And I have a bunch of small stand-alone images (think small icon images) that I want to place on either side of the blog post feed. They are kind of just like random background images. As the feed gets longer, more images will automatically be placed.
Here is some example code of what I have so far. Right now I am just appending text to the page to get this working.
I need help figuring out how to restart this loop once the end of the array is reached. For example, after 6url.jpg is printed, I want to print 1url.jpg and so on if my imageCount is more than 6. I played around with continue and while loops but not sure how to implement that.
var blogIcons = ["1url.jpg", "2url.jpg", "3url.jpg", "4url.jpg", "5url.jpg", "6url.jpg"];
var blogFeedHeight = $(".blog-feed").height();
var imageDistance = 400;
// Determining how many images to place based on the blog feed height;
var imageCount = Math.ceil(blogFeedHeight/imageDistance);
// the number of images that should be inserted.
for(var i = 0; i < imageCount; i++){
$('blog-feed').append('<div class="blog-icon">' + blogIcons[i] +'</div>')
}
What you are looking for is called modulo, the rest of the euclidian division of 2 numbers.
var blogIcons = ["1url.jpg", "2url.jpg", "3url.jpg", "4url.jpg", "5url.jpg", "6url.jpg"];
var imageCount = 10;
// the number of images that should be inserted.
for (var i = 0; i < imageCount; i++) {
console.log(blogIcons[i % blogIcons.length])
}
I simplified your problem so it can run on StackOverflow. but you'll get the proper index by using "%"
So your script should look like that:
var blogIcons = ["1url.jpg", "2url.jpg", "3url.jpg", "4url.jpg", "5url.jpg", "6url.jpg"];
var blogFeedHeight = $(".blog-feed").height();
var imageDistance = 400;
// Determining how many images to place based on the blog feed height;
var imageCount = Math.ceil(blogFeedHeight/imageDistance);
// the number of images that should be inserted.
for(var i = 0; i < imageCount; i++){
$('blog-feed').append('<div class="blog-icon">' + blogIcons[i % blogIcons.length] +'</div>')
}
You don't need to restart a loop. Instead you can use modulo division to fetch an item within bounds from the array
var blogIcons = ["1url.jpg", "2url.jpg", "3url.jpg", "4url.jpg", "5url.jpg", "6url.jpg"];
for (var i = 0; i < 14; i++) {
console.log(blogIcons[i % blogIcons.length]);
}
When doing i % blogIcons.length you will get a number between 0 and blogIcons.length

Loading images in js for loop loads nonsequentially

Currently I have 3 or 4 galleries loaded on one page. It's a photographers site, so each gallery has about 40 images. I originally pre-loaded the images, where I would just load the first two images so the page loads quickly, then use JS in the background to cycle through a loop and load the rest. It worked well, except that it didn't honor the order, and the order is important. I have seen some examples where you hide the images until they load, but since there are about 120 large images being loaded in the page it causes the page to load slowly. Is there a way I can use my code, and sort the images once they're loaded? Or, should I load them through an array, would it still ignore the load order? Here's my jsfiddle: http://jsfiddle.net/drrobotnik/YKPEu/
The piece of js i am looking at mostly is this part:
var stack = [];
for (var i = 3; i <= 59; i++) {
var img = new Image(782,521);
img.src = 'http://anikalondon.com/Images/weddings/weddings-3-' + i + '.jpg';
$(img).bind('load', function() {
stack.push(this);
var len = stack.length+2;
$(".numbers").text(($(".enggal img:visible").index()+1)+"/"+len);
if(i>=58){$(".numbers").css("visibility", "visible");}
});
}
again, in this example it's loading the images out of order (probably whichever ones load first, not sure).
well idk if it helps but here's a sorting algorithm that you could use to order them before inserting them into the DOM
stack.sort(function(a,b){
return a.src.substring(a.src.lastIndexOf("-")+1,a.src.lastIndexOf(".")) - b.src.substring(b.src.lastIndexOf("-")+1,b.src.lastIndexOf("."))
})
What if you replaced you simplify things and use this for loop
for (var i = 3; i <= 59; i++)
{
$(".enggal").append("<img src='http://anikalondon.com/Images/weddings/weddings-3-" + i + ".jpg' />");
}
This inserts all of your images in order. You may want to add in the width/height attributes.
Without knowing the deal with how stack is used, I'm not totally sure this would work, but why don't you just save the index information to a store right away, rather than on the callback when it's done loading?
And you should also set src after you bind load or it might miss.
[totally revised from original answer]
var img,i,stack = [];
var imageLoaded=function(e) {
var len = stack.length+2,
index = parseInt(this.src.split('-')[2])-3
$(".numbers").text(($(".enggal img:visible").index()+1)+"/"+len);
if(index>=58){$(".numbers").css("visibility", "visible");}
}
for (i = 3; i <= 59; i++) {
img = new Image(782,521);
$(img).bind('load', imageLoaded);
stack.push(img);
}
// Just to make sure that the first image doesn't finish loading before the loop isdone
// throwing off the count in the imageLoaded function
for (i = 3; i <= 59; i++) {
stack[i-3].src = 'http://anikalondon.com/Images/weddings/weddings-3-' + i + '.jpg';
}

how to get the actual box bigger than the others?

i'm new to javascript and i'm having a problem. I want the actual (function updateBoxes) box [boxIx] to be bigger than the other ones but i can't seem to find a code that works. i've tried box[boxIx].size ="100px"; box[boxIx].style.size ="100px"; without result. This is my code;
function init() {
box = document.getElementById("boxes").getElementsByTagName("div");
for (var i=0; i<box.length; i++) {
box[i].style.left = 70*i+"px";
} // End for
boxIx = box.length - 8;
updateBoxes();
} // End init
function browseLeft() {
if (boxIx > 0) boxIx = boxIx - 1;
updateBoxes();
}
// End browseLeft
function browseRight() {
if (boxIx < box.length-1) boxIx = boxIx + 1;
updateBoxes();}
// End browseRight
**function updateBoxes() {
box[boxIx].style.backgroundColor ="#CCC";
box[boxIx].style.top = "20px";
box[boxIx].style.zIndex = 9;**
var z = 8;
for (var i=boxIx-1; i>=0; i--) {
box[i].style.backgroundColor ="#666";
box[i].style.top = "0px";
box[i].style.zIndex = z;
z = z - 1;
} // End for
z = 8;
for (var i=boxIx+1; i<box.length; i++) {
box[i].style.backgroundColor = "#666";
box[i].style.top = "0px";
box[i].style.zIndex = z;
z = z - 1;
} // End for
} // End browseLeft
As thirtydot pointed out, you have two instances of "**" in your sample code that I've removed in the assumption that this is a markdown glitch when editing.
Your example shows only the JavaScript code. The HTML markup and CSS styling you're using would be most helpful. I've created a fiddle for discussion and to resolve this for you here: http://jsfiddle.net/bhofmann/zkZMD/
A few things I noticed that might be helpful:
You're using a magic number 8 in a few places. Can we assume this is the number of boxes? I would store that in a variable for use in the functions.
You used a lot of direct styling. Your code might be cleaner if you used CSS classes to alter the appearance of the boxes.
Unless you're altering the default styling of DIV, you won't see much change by simply setting the left offset.
PS. I took the liberty of invoking the init function on page load because I saw nothing else to invoke it. I don't know what would invoke browseLeft and browseRight but I'll leave that to you.

jQuery - piece of code causing conflict problems on pages the selector doesn't exist

Okay so I am developing a WordPress theme. On the single post page I have a comments div which floats down the page using some jquery. I am also running a modal popup form to log in. This is completely fine on the single page when the #commentWrapper (selector for the jquery floating effect) exists. However on pages where there is no #commentWrapper to float, the modal form doesn't work. I pinned down the problem to this line in my general jQuery call (by removing each line and testing).
Call in general.js, very last call:
jQuery('#commentWrapper').stickyfloat({ duration: 300, easing : 'easeInQuad' });
Actual plugin it refers to:
$.fn.stickyfloat = function(options, lockBottom) {
var $obj = this;
var parentPaddingTop = parseInt($obj.parent().css('padding-top'));
var startOffset = $obj.parent().offset().top;
var opts = $.extend({ startOffset: startOffset, offsetY: parentPaddingTop, duration: 200, lockBottom:true }, options);
$obj.css({ position: 'absolute' });
if(opts.lockBottom){
var bottomPos = $obj.parent().height() - $obj.height() + parentPaddingTop; //get the maximum scrollTop value
if( bottomPos < 0 )
bottomPos = 0;
}
$(window).scroll(function () {
$obj.stop(); // stop all calculations on scroll event
var pastStartOffset = $(document).scrollTop() > opts.startOffset; // check if the window was scrolled down more than the start offset declared.
var objFartherThanTopPos = $obj.offset().top > startOffset; // check if the object is at it's top position (starting point)
var objBiggerThanWindow = $obj.outerHeight() < $(window).height(); // if the window size is smaller than the Obj size, then do not animate.
// if window scrolled down more than startOffset OR obj position is greater than
// the top position possible (+ offsetY) AND window size must be bigger than Obj size
if( (pastStartOffset || objFartherThanTopPos) && objBiggerThanWindow ){
var newpos = ($(document).scrollTop() -startOffset + opts.offsetY );
if ( newpos > bottomPos )
newpos = bottomPos;
if ( $(document).scrollTop() < opts.startOffset ) // if window scrolled < starting offset, then reset Obj position (opts.offsetY);
newpos = parentPaddingTop;
$obj.animate({ top: newpos }, opts.duration );
}
});
};
If I add an if command to see if the selector exists, it all works. I would like to know what the problem is for future website however.
Well, your stickyfloat() method assumes many things, like always being called on a jQuery object that contains at least one element, or that element always having a parent. For instance, consider the following code:
var $obj = this;
// ...
var startOffset = $obj.parent().offset().top;
If the jQuery object your method is called on is empty, or if its first element has no parent (the method was called on $("html")), the code will fail because parent().offset() will be null.
If you want your method to be more robust, you should not assume anything about the object it's called on. A good first step is to make the method chainable, which is always beneficial to your users and will get rid of the first problem. The recommended way of doing that is:
$.fn.stickyfloat = function(options, lockBottom) {
return this.each(function() {
var $obj = $(this);
// The rest of your code.
});
};
Since the code now runs sequentially on each element (if any) through an anonymous function, testing for the parent element's existence can be dealt with by returning early:
var $obj = $(this);
var $parent = $obj.parent();
if (!$parent.length) {
return; // No parent, continue with next element, if any.
}
// Parent element is safe to use.
var parentPaddingTop = parseInt($parent.css('padding-top'));
var startOffset = $parent.offset().top;

Categories

Resources