I'm currently using Galleria for multiple image galleries on a page. Each gallery is created like so:
<div class="gallery">
<img src="/path/to/thumb.jpg" data-big="/path/to/full.jpg">
<img src="/path/to/thumb.jpg" data-big="/path/to/full.jpg">
</div>
...and at the end I have some Javascript:
<script type="text/javascript">
Galleria.loadTheme('/galleria/themes/twelve/galleria.twelve.min.js');
Galleria.run('.gallery',{
responsive: true,
height: $(window).height() * 0.80,
imageCrop: false,
wait: true,
extend: function(options) {
Galleria.log(this) // the gallery instance
Galleria.log(options) // the gallery options
// listen to when an image is shown
this.bind('image', function(e) {
Galleria.log(e) // the event object may contain custom objects, in this case the main image
Galleria.log(e.imageTarget) // the current image
// lets make galleria open a lightbox when clicking the main image:
jQuery(e.imageTarget).click(this.proxy(function() {
this.openLightbox();
}));
});
}
});
So I've taken some code snippets that allow for clicking on the image to show the zoomed version, but the import part this question is about is the height. Notice I set the height to 80% of the current web browser viewport.
I've found other questions/tutorials that show something like this for resizing that viewport on browser resizes:
$(window).resize(function(){
Galleria.get(0).resize();
});
but I can't seem to get this (or variations of it) to work. One issue is that I have multiple galleries and they're specified by class. I feel like I'd need to iterate through each gallery (not just to a get(0)) and resize all of them. But I'm inspecting the Javascript objects and I'm not sure how to do that.
So basically, the gallery does size properly on page load; I'd just like to resize them on browser resize to match the browser viewport.
Related
I am currently using lazy loading of images, in which all the images are loaded while page is loading and displayed after an image is loaded. In this method, page is shown loading at the tab section of browser till all the images are downloaded.
Now, I want to load the images separate from page loading. Like in google images, page is loaded within 1 second, and images are loaded one by one without showing the page loading in the tab of browser.
How can I achieve this?
So, the final question: How to first load the page fully, without downloading the images from server, and then download the image when the scroller is reached there. Till then, the image may be a grey box.
Loading an image when it comes into view.
Loading attribute
We could use native lazy loading with the loading attribute.
Here's a demo.
<div><img src="https://placeimg.com/410/410/any" loading="lazy" width="410" height="410"></div>
Currently, the latest version of Chrome, Firefox and Edge support native lazy loading.
Be aware that the distance threshold does vary in browsers.
Chrome
The distance threshold after which the deferred content will start
loading-in depends on factors including the current effective
connection type, and whether Lite mode is enabled. The distance is
chosen so that the deferred content almost always finishes loading by
the time it becomes visible.
Firefox
Tells the user agent to hold off on loading the image until the
browser estimates that it will be needed imminently. For instance, if
the user is scrolling through the document, a value of lazy will cause
the image to only be loaded shortly before it will appear in the
window's visual viewport.
Intersection Observer
We could also use the IntersectionObserver API to determine when your images are in view. This offers better compatibility across browsers and offers greater control for when and how our images load.
CodePen
HTML
<div>
<img src="data:,"
data-src="https://placeimg.com/410/410/any"
class="lazy"
alt=""
width="410"
height="410" />
</div>
JS
document.addEventListener("DOMContentLoaded", lazyLoad);
function lazyLoad() {
const images = [].slice.call(document.querySelectorAll("img.lazy"));
var config = {
// If the image gets within 500px in the Y axis, start the download.
rootMargin: "500px 0px",
// detect when visibility passes the 1% mark
threshold: 0.01
};
if ("IntersectionObserver" in window) {
const observer = new IntersectionObserver(showImage, config);
images.forEach(function (image) {
observer.observe(image);
});
}
}
function showImage(entries, observer) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
const image = entry.target;
image.src = image.dataset.src;
observer.unobserve(image);
}
});
}
Existing libraries
There are also existing JavaScript libraries, such as lazySizes.
I have a toggle button which basically shows/hides a list of filters on a Product Index page. Desktop is simple, when clicking the button the panel is shown or hidden with the following script:
$(".filter-toggle").click(function(){
$(".grid").toggleClass("hide-filters");
});
When the browser is below 1024px I use the mmenu plugin to duplicate the filters content and move it into an off-canvas panel. The code for that looks like this:
$(document).ready(function() {
$(".filters").mmenu({
// Options
navbar: {
title: "Filters"
}
}, {
// Configuration
clone: true,
offCanvas: {
pageSelector: ".page"
},
classNames: {
selected: "active"
}
});
var API = $(".filters").data("mmenu");
var $icon = $(".filter-toggle");
$icon.on("click", function() {
API.open();
});
});
Obviously (at the minute) when the toggle button is clicked it will show/hide the content on the page but also trigger the slide-out menu at the same time.
My question is how can I only run the relevant script when it's needed. Is it also possible to do this on resize on not just on pageLoad/refresh?
I tried using matchMedia. Here's a quick CodePen showing where I'm at...
https://codepen.io/moy/pen/wymvjN
It looked like it worked to begin with. Above 1024px the toggle worked. Shrinking the browser down meant the button triggered the slide-out panel - great! But when scaling back up to desktop the slide-out menu is triggered whenever the toggle is clicked. On refresh it works again ...until you shrink the browser down and back up again.
Any ideas?
You can listen for window resize events, and fire functions accordingly. For example, in jQuery:
$(window).on('resize', function(e) {
var windowWidth = $(window).width();
if (windowWidth < 1024) {
// initialize small viewport functionality
} else {
// initialized large viewport functionality
}
});
But, you'll need a way to disable that mmenu plugin when switching back to desktop, and include it in whatever script you run during that condition.
Alternatively, the mmenu "Responsive Layout" documentation provides an example for using CSS media queries to hide the cloned menu at certain breakpoints.
I am using Justified-Gallery
to layout my gallery thumbnails. I want to implement a preloader so the images only show once they have been formatted by Justified-Gallery.
I saw this option in the plugin but I couldn't get it to work, waitThumbnailsLoad.
Currently I am hiding the div containing the thumbnails, and then showing it once the plugin has completed. Is this the best way or is there a better way to do this?
HTML
<div id="justify-gallery" class="hidden">
// thumbnails go here
</div>
JS
// Justify Gallery
$("#justify-gallery").justifiedGallery({
rowHeight: 100,
fixedHeight: true,
captions: false,
margins: 3,
lastRow: 'nojustify'
});
$('#justify-gallery').justifiedGallery().on('jg.complete', function (e) {
$(this).fadeIn();
});
Yes you're in the right track, if your current code sort of works, then you can add a parent container to the hidden class, and add a loading animation to it, then use css to position absolute the images or hide the loader, up to you.
<div class="parent-with-loading-animation">
<div class="loading-animation"></div>
<div id="justify-gallery" class="hidden">
// thumbnails go here
</div>
</div>
Just give the parent div a min-height of whatever you reckon would be the average height of images and a width 100% depending on your layout of course.
$('#justify-gallery').justifiedGallery().on('jg.complete', function (e) {
$('.loading-animation').fadeOut();
$(this).fadeIn();
});
Oh boy, on a second visit to the site I found an article on performance; where it states if you add a class of .justified-gallery it will only show the thumbnails once loaded. In all fairness this vital info should have been put in a more prominent spot.
http://miromannino.github.io/Justified-Gallery/performance-tips/
We have some jQuery graphs that load into a fancybox iframe. Recently, some of these graphs are too tall for the fancybox frame, and the design requirement is that there is no scroll bar unless the height is over 700px AND there is no more than 10px of whitespace above and below the graph.
I tried this:
afterLoad : function(){
//(...)
$('.fancybox-inner').height($('.fancybox-iframe').contents().height() + 20);
console.log($('.fancybox-inner').height());
}
The console.log() correctly displays the desigred height.
But the fancybox is still displayed with the default height.
Putting a breakpoint on the console.log() line, the page displays the fancybox frame on the top of the window, graph rendered and iframe with the correct height. When I release the debugger stop, fancybox moves the frame to the center of the viewport, and it is back to the default height (the same behavior when not in debug mode).
How can I make fancybox use the desired height? It depends on the graph, so I cannot set it in the options.
Dealing with iframes make things a bit difficult but you may try something like
$(document).ready(function () {
$("a.fancybox").fancybox({
type: "iframe",
fitToView: false, // we need this
autoSize: false, // and this
maxWidth: "95%", // and this too
beforeShow: function () {
// find the inner height of the iframe contents
var _newHeight = $(".fancybox-iframe").contents().find("html").innerHeight();
this.height = _newHeight
}
}); // fancybox
}); // ready
Notice we disable fitToView and autoSize to be able to set the preferred height, however we also need to set a maxWidth to avoid fancybox going off screen dimensions.
Also notice we used the beforeShow callback to set the this.height setting.
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)
}
});
});