jquery masonry image height auto doesn't work, even with imagesLoaded - javascript

I know there are lots of answers to this on here, but none of them are working for me.
I'm trying to load images with a fixed width, but the images have varying heights.
I'm using the Jquery Masonry with imagesLoaded js but I still can't get it to work. If I set the width and height in css to a pixel size it works fine, but if I change height to auto it fails and all the images load the same height.
This is the javascript I'm using to initialise (I have tried many variations of this!)
var container = document.querySelector('#container');
var msnry;
// initialize Masonry after all images have loaded
imagesLoaded( container, function() {
msnry = new Masonry( container, {
// options
columnWidth: 390,
itemSelector: '.item'
} );
});
And this is the css
img {display:block; height:auto; width:390px;}
I've been hacking around with this for days now, somebody must know the answer!

Hopefully this will help a few people in the future.
The only thing I could get up with was to wait until the page was fully loaded for those heights to set and THEN grab those heights afterwards.
For this I used window.load. Maybe there is a better way but this worked for me.
$(window).load(function(){
$('.someDiv').each(function () {
var $this = $(this);
var someDivHeight = $this.height();
console.log(someDivHeight);
});
});

Related

implementing image gallery that lazy loads images of unknown size on scroll and positions them optimally

I need to implement an image gallery with the following spec:
images need to have a max height and width. they keep their aspect ratio within these bounds.
based on the final size of the images after the max size constraints, order them on the page in a way that reduces empty blank spaces.
as the container scrolls down, load more images.
libraries I have researched such as masonry and this lay load lib
all expect width and height to be known ahead of time.
It seems that I may need to resort to loading the images in an invisible state in order to get the width and height params before positioning them on the page.
this will help with the 'masonry' aspect, but contradict the lazy load mechanism.
I would appreciate any pointers in the right direction.
I'm using Masonry right now and I think that it fits to your needs. I have different width&height images and I load with a fixed max-width (with a fixed width or a relative to the page one) and then, the layouts reorders to avoid blank spaces and keep aspect ratio of the images. When I reach the bottom of the page, I ('manually') load more items. This is my code
//Load the first page
loadMore(1);
function loadMore(page){
var div = "";
var html = "";
var item_num = 1 + ((page-1)*10);
$('.loader').show();
$('#container').hide();
$.post( "loadMore.php", {'page':page }, function( data ) {
data=JSON.parse(data);
$.each(data, function (key,value) {
//here create the div with the data
html = html + div;
item_num++;
});
$("#container").append(html).each(function(){
$('#container').masonry().masonry('reloadItems');
});
var $container = $('#container');
$container.imagesLoaded(function(){
$('#container').masonry();
});
$('.loader').fadeOut('fast',function(){
$(this).remove().delay( 1500 );
});
$('#container').show();
});
}
//On bottom page, load more images
$(window).scroll(function () {
if (ready && $(document).height() <= $(window).scrollTop() + $(window).height()) {
ready = false; //Set the flag here
setTimeout(function(){
loadMore(page);
page++;
},1000);
ready = true; //Set the flag here
}
});
You can check the result at http://pintevent.com (is a beta page)
Then, is easy to add LazyLoad to all images, here is a working example:
http://jsfiddle.net/nathando/s3KPn/4/ (extracted from a similar question: Combining LazyLoad and Jquery Masonry )
Also, if it not works for you, here's a bunch of jquery LazyLoad libraries for galleries you could check: http://www.jqueryrain.com/demo/jquery-lazy-load/
Hope it helps to you!

Dynamic columnWidth causing stacked images in Masonry

I am trying to recreate the masonry blog view from Unify in Rails 4.
http://htmlstream.com/preview/unify-v1.8/blog_masonry_3col.html
I bought the theme and included the latest imagesLoaded and Masonry files within my application (using bower-rails).
Masonry PACKAGED v3.3.2
imagesLoaded PACKAGED v3.2.0
When using the with the theme supplied js file all images are stacked on top of each other.
screenshot 1
$(document).ready(function(){
var $container = $('.grid-boxes');
var gutter = 30;
var min_width = 300;
$container.imagesLoaded( function(){
$container.masonry({
itemSelector : '.grid-boxes-in',
gutterWidth: gutter,
isAnimated: true,
columnWidth: function( containerWidth ) {
var box_width = (((containerWidth - 2*gutter)/3) | 0) ;
if (box_width < min_width) {
box_width = (((containerWidth - gutter)/2) | 0);
}
if (box_width < min_width) {
box_width = containerWidth;
}
$('.grid-boxes-in').width(box_width);
return box_width;
}
});
});
});
See this js fiddle: http://jsfiddle.net/sdynfq83/
I noticed following things:
Resizing the window or refreshing does not correct the issue so I figured out it is not an images loaded error. This took me a long time to figure this out.
My html code seems alright since I have the same problems if I copy the HTML code from the theme itself and include the same JS and CSS files.
the ".grid-boxes-quote" boxes don't have the same width as the other grid boxes. Which is strange because they should all be the same since all boxes have the ".grid-boxes-in" class. https://jsfiddle.net/sdynfq83/embedded/result/
When removing the columnWidth code and replacing it by a fixed number (300) + adding width to the grid-boxes-in then it seems to work. This is not what I want since the images sizes are not correct anymore.
css
.blog_masonry_3col .grid-boxes-in {
padding: 0;
margin-bottom: 30px;
border: solid 1px #eee;
/* added width */
width: 300px;
}
js
$(document).ready(function(){
var $container = $('.grid-boxes');
var gutter = 30;
var min_width = 300;
$container.imagesLoaded( function(){
$container.masonry({
itemSelector : '.grid-boxes-in',
gutterWidth: gutter,
isAnimated: true,
/*columnWidth: function( containerWidth ) {
var box_width = (((containerWidth - 2*gutter)/3) | 0) ;
if (box_width < min_width) {
box_width = (((containerWidth - gutter)/2) | 0);
}
if (box_width < min_width) {
box_width = containerWidth;
}
$('.grid-boxes-in').width(box_width);
return box_width;
}*/
columnWidth: 300
});
});
});
js fiddle: http://jsfiddle.net/8c0r06a6/2/
The theme itself supplies an older version of masonry. In which the code seems to work. The images do keep overlapping (this can be fixed by resizing or refreshing the window).
Screenshot 2
screenshot 3
I however want to update to the latest version of masonry and images loaded so I can keep using bower to easily update those files. I am also hoping that using the latest version of everything fixes the overlapping images in screenshot 2. I have a working JS fiddle below with the old code.
/**
* jQuery Masonry v2.1.05
* A dynamic layout plugin for jQuery
* The flip-side of CSS Floats
* http://masonry.desandro.com
*
* Licensed under the MIT license.
http://jsfiddle.net/ytLf3bue/1/
Summarized I have following questions, please bear in mind that I am a beginning hobby coder and I do not have a lot of JS experience:
Is it a smart idea to always use the latest version of the Masonry and ImagesLoaded code or should I just stick with the supplied files?
If 1. is yes => how do I fix the code so the images are not stacked on eachother anymore?
If 1. is no => how do I fix the code so the overlapping images and background bleed in screenshot 2 and 3 are gone?
Anytime you are dealing with masonry, instead of using:
$(document).ready(function(){ ... go masonry ... }
use:
$(window).load(function(){ ... go masonry ... }
http://jsfiddle.net/sdynfq83/2/
$(document).ready triggers as soon as the DOM is completely loaded. That does not include loading of resources like images. Masonry calculates the absolute positioning of images based on their widths and heights. If it runs before the actual image is loaded it sees the image tag as an element with zero width and height. There for it only offsets for the gutter between and the images end up stacked.
$(window).load triggers once all page resources have finished loading. This allows Masonry to get the correct dimensions of all objects before it tries to place them.
David Desandro answered me himself.
$(document).ready( function() {
// init Masonry after all images have loaded
var $grid = $('.grid').imagesLoaded( function() {
$grid.masonry({
itemSelector: '.grid-item',
percentPosition: true,
gutter: 20,
columnWidth: '.grid-sizer'
});
});
});
In Masonry v3, columnWidth no longer accepts a function. Instead, use
element sizing for responsive layouts.
Here's a demo
http://codepen.io/desandro/pen/f3451d70f80c35812b33956785ee152c/
This fixes the issue.

jquery 1.8 and higher, set the height of a div to the same as another div

As of Jquery 1.8 a change was made when getting the height() of an element. I have a CSS div height set to auto with the image inside dictating the height and width of the div by using % and auto), and when the window loads i use Jquery to get the height of the element and make another div next to it the same height. After researching this I have noticed that it is returning the height before the CSS has set the new height that is set by the image. 1.7 allowed this, but 1.8 and up does not. Is ther a work around.
this is the css
#element1{ width:80%; height:auto;}
#element1 img{width:100%; height:auto};//this allows the image to resize with the page responsively.
jQuery...
$(window).ready(
function(){
var x = $("#element").height();
alert(x); // this would return what the height was dynamically set as by the css in 1.7, but 1.8 returns a small number that i am pretty certain is just the padding added to 0px
});
Hopefully this makes sense, and someone has a work around.
Thanks
Instead of listening on $(window).load(), which might stall proper height assignment until all resources have been successfully loaded, you can listen to successful loading on each <img> instance and trigger proper height calculation.
However since in your question you only have one element you are concerned with setting height dynamically, I have reduced my script without the need to loop through all <img> instances on the page. Assuming that you have the following markup:
<div id="element1">
<img ... />
</div>
You can create a new image, check if it is loaded and then instruct jQuery to run the height calculations and set a new height when this is done:
$(function() {
var $img = $('<img />', {
'src': $('#element1 img').attr('src')
});
$img.load(function() {
$('#element2').css('height', $('#element1').height());
});
});
There's a mismatch between your css selector (#element1) and your jquery selector ('#element'). Start by making them both match whatever you have on your html element. You could also wrap this in a timeout so your image will have time to fully load.
$( document ).ready(function() {
setTimeout(function() {
$('#element2').height( $('#element1').height() );
}, 2000});
});

Masonry & LazyLoad doesn't want to work together

I am setting up a site for a photographer. It should be built using the Bootstrap 3 framework, and he wants to have a masonry with over 400 images on one page. For this to work LazyLoad is a must. I have now spent several days trying to get LazyLoad to work with Desandros Masonry but with no success..
I've tried all of the examples that one finds googling, but most posts/sites/forums just redirects you, or have copied this stackoverflow answer:
Combining LazyLoad and Jquery Masonry
I've tried both methods but unfortunately I get nothing but grey hair..... :(
Here is a simplified live version of the page im working on:
http://nr.x10.mx
In this example I have added a fade-in on page-load, but left the LazyLoad out since I can get it to work.
And here you have a FIDDLE of the following
var container = document.querySelector('#ms-container');
imagesLoaded( container, function()
{
var msnry = new Masonry(container,
{ itemSelector: '.ms-item',
columnWidth: '.ms-item',});
});
You can also download the whole pack here, including the jquery.lazyload.js HERE
Any help would be highly appreciated
UPDATE
Here you can have 4 different examples of the different problems that occur.
I also found to my joy that the Bootstrap .img-responsive class is interfering with LazyLoad.
1 - Masonry without LazyLoad
2 - Masonry and Lazyload - Masonry breaks down and LazyLoad has no effect
3 - LazyLoad without Masonry - LazyLoad has no effect
4 - LazyLoad without Masonry and Bootsrap "img-responsive" removed
5 - Masonry & LazyLoad using first method of SO answer mentioned above
6 - Masonry & LazyLoad using second method of SO answer mentioned above Both of the last ones gives the following error: [Error] TypeError: 'undefined' is not a function (evaluating '$container.imagesLoaded')
global code (5.html, line 110)
Updated zip HERE
Again, any asisstance would be highly appreciated, thank you
I made your 5.html work by using the javascript files of the fiddle present on the SO link you posted, answered by Nathan Do. So you probably just had bad versions of the scripts.
The scripts linked on that page are: http://cdn.jsdelivr.net/masonry/2.1.08/jquery.masonry.min.js and http://cdn.jsdelivr.net/jquery.lazyload/1.8.4/jquery.lazyload.js
Here's your original fiddle updated with that page's method: http://jsfiddle.net/L9RLe/
In your case, though you are creating masonry and adding lazyload effect, the images get overlapped.
You need to follow steps as :
Create Dynamic HTML structure for images along with their respective aspect ratio height and width. Note: images should have the required attributes for applying lazy load effect i.e. class="lazy" data-original="actual image url" and src="dummy imageurl".
Apply lazy load effect.
Then create Masonry.
Lets have an example :
Suppose I am having a javascript array with some image related data as,
var gallery_itemList= [
{url: "images/example1.jpg", width:"1170", height:"460"},
{url: "images/example2.jpg", width:"800", height:"320"},
{url: "images/example3.jpg", width:"1200", height:"870"}];
And below prototype for creating dynamic html, applying lazyload effect and creating Masonry effect as :
var masonryGallery = {
gallery :'', // gallery to create
genarateGallery : function() {
// generate each item html
var inHTML="", i;
for(i=0;i<gallery_itemList.length;i++){
var iWidth, iHeight, fHeight=0;
iWidth=parseInt(gallery_itemList[i].width);
iHeight=parseInt(gallery_itemList[i].height);
fHeight = Math.round((iHeight*300)/iWidth);
inHTML+='<div class="item" style="height:'+fHeight+'px">';
inHTML+='<img class="lazy" src="images/loading.gif" data-original="'+gallery_itemList[i].url+'"/>';
inHTML+='</div>';
}
//add generated html to gallery
$(masonryGallery.gallery).append(inHTML);
},
applyLazyload : function(){
$("img.lazy").lazyload();
},
createMasonry : function(){
// create Masonry
$(masonryGallery.gallery).masonry({
columnWidth: 350,
itemSelector: '.item',
isFitWidth: true,
isAnimated: !Modernizr.csstransitions
}).imagesLoaded(function() {
$(this).masonry('reload');
});
},
init : function(givenGallery) {
masonryGallery.gallery = givenGallery; // set gallery
masonryGallery.genarateGallery(); // generate gallery html
masonryGallery.applyLazyload(); // apply lazyload effect
masonryGallery.createMasonry(); // apply masonry effect
}
};
/* Gallery Intialisation */
(function(){masonryGallery.init('div#content');})();
If you have the problem images get overlapped, I found the solution at the site below, although it is in Japanese.
http://www.webdesignleaves.com/wp/jquery/1340/
The point is use following;
$('img.lazy').load(function(){ ... })
HTML
<div id="works_list">
<div class="work_item">
<img class="lazy" src="images/dummy.gif" data-original="images/works/thumb/001.jpg" alt="">
<p>title 1</p>
</div><!-- end of .work_item-->
<div class="work_item">
<img class="lazy" src="images/dummy.gif" data-original="images/works/thumb/002.jpg" alt="">
<p>title 2</p>
</div><!-- end of .work_item-->
....
</div><!-- end of #works_list -->
jQuery
$("img.lazy").lazyload({
effect: 'fadeIn',
effectspeed: 1000,
threshold: 200
});
$('img.lazy').load(function() {
masonry_update();
});
function masonry_update() {
var $works_list = $('#works_list');
$works_list.imagesLoaded(function(){
$works_list.masonry({
itemSelector: '.work_item', 
isFitWidth: true, 
columnWidth: 160
});
});
}
Just want to contribute my solution to this thorny problem.
Basically you need to call masonry's layout() function every time an image is lazyloaded, because that's when you know what it's dimensions are going to be. So you setup lazyload's load() function to call masonry.layout(). This can cause a new problem, because on your initial page load, all of your images will have a zero or near-zero height (depending on your css), and thus stack up in the top of the viewport. When you initialize lazyload, it will see all these images in the viewport, and try to load them all at once. This causes you to download tons of images, and even worse, calls masonry dozens or hundreds of times. Not very speedy.
My solution is to force a minimum height on unloaded images until they are lazyloaded. This restricts the number of images that will be found in the viewport initially, limiting the number of lazyload and masonry calls to a reasonable amount.
Once an image is loaded, you remove the .unloaded class in order to deconstrict the height and allow it to conform to the height of the image.
HTML
<div id="masonry-container">
<div class="masonry-item unloaded"><img class="lazy" data-original="image.jpg"></div>
<!-- Repeat as needed -->
</div>
CSS
.masonry-item {
width: 33.3%; // I'm doing a 3-column masonry layout
}
.unloaded {
min-height: 200px;
// set a minimum default height for image containers
// this prevents too many images from appearing in the viewport
// thus causing lazy load to load too many images at once
// we will use the lazyload .load() callback to remove this fixed
}
JS
$( document ).ready(function() {
// Initialize Masonry
var container = document.querySelector('#masonry-container');
var msnry = new Masonry( container, { transitionDuration: 0 } );
msnry.layout(); // run masonry for first time
function doMasonry() { msnry.layout(); } // gives us a function handler to call later with window.setTimeout()
// Initialize lazyload
$("img.lazy").lazyload({
effect : "fadeIn", // optional
load : function() {
$(this).parent().removeClass("unloaded"); // select the div containing the image and remove the "unloaded" class, thus deconstricting the min-height
window.setTimeout(doMasonry,100); // trigger masonry again with a 100 ms delay, if we do it too soon, the image may not have loaded, and masonry will layout with the wrong image dimensions (this might be a bit of a hacky way to do it)
}
});
});

Flexslider 2.1 Inline style problems

Im using flexslider2 to show my slides.
But i have the problem, that the width of my li-elements are computed wrong at the first page load.
when i open firebug and click on the inline style, the width changes and everything is displayed correctly. Also when I just resize my browser, everything is correct.
Why do I have this problem?
When i look into the flexslider code, i find this:
slider.computedW = slider.itemW - slider.boxPadding;
which should be alright .... because on resize, it only uses the computedW:
if (vars.smoothHeight) methods.smoothHeight();
slider.newSlides.width(slider.computedW);
slider.setProps(slider.computedW, "setTotal");
please help me!!!
Do you have your flexslider inside a container? If so you can set flexslider to automatically adjust to that specific height when the page loads.
$(document).ready(function(){
var containerheight = $(".flexcontainer");
var borderWidth = $('.flexslider').css('height', containerheight.outerWidth());
});
Also, if your container is slightly another height than your flexslider should be you can use below. (The "5" is by how many pixels you want to change the height with)
$(document).ready(function(){
var containerheight = $(".flexcontainer");
containerheight = containerheight -5;
var borderWidth = $('.flexslider').css('height', containerheight.outerWidth());
});

Categories