jQuery: Execute function as soon as a subset of images have loaded - javascript

I have a slideshow that I need to initialize with jQuery. The initialization requires all slideshow images to be fully loaded, so that I can get their actual width and height. I can not change this part.
Consider the following layout:
<div class="slideshow">
<img />
<img />
<img />
</div>
<img />
<img />
<img />
I need to initialize the slideshow as soon as the three images inside the slideshow container have been loaded. I can NOT wait until the rest of the images on the page have been loaded, as there might be thousands of images.
Any ideas how I could solve this?

As per my comment, you can wrap the checking/load behavior in a jQuery plugin and return a single promise like this:
$.fn.imagesLoaded = function () {
var def = $.Deferred();
var count = this.length;
this.each(function () {
if (this.complete) {
if (!--count) {
def.resolve();
}
} else {
$(this).load(function () {
if (!--count) {
def.resolve();
}
});
}
});
return def.promise();
}
and then simply use like this:
$('.slideshow img').imagesLoaded().done(function () {
alert("loaded");
});

Here's what I ended up using, quite a bit more complicated than I'd hoped:
// Prepare the slideshow as soon as all of its images have been loaded
var slideshowImages = $('.slideshow img');
var slideshowImagesLoadedCount = 0;
// Check how many images have already been loaded
slideshowImages.each(function() {
if(this.complete) {
slideshowImagesLoadedCount++;
}
});
// If all the images have loaded already, prepare the slideshow
if(slideshowImagesLoadedCount === slideshowImages.length) {
prepareSlideshow();
} elseĀ {
// Otherwise wait until all images have been loaded
slideshowImages.load(function() {
slideshowImagesLoadedCount++;
if(slideshowImagesLoadedCount === slideshowImages.length) {
prepareSlideshow();
}
});
}

Related

Detect when a list of images are loaded

I'm using Semantic Ui and jQuery to make an album. I want to write a function that gets called when all images in div class="images" are loaded.
There is a loading cover div class="dimmer" which covers all images. When all images are loaded, the cover should be removed.
How can I determine when all images have been loaded so I can remove the cover?
Here is the code I have so far:
<div id="loader1" class="ui active dimmer">
<div class="ui text loader">Loading...</div>
</div>
<div id="image1" class="ui small images">
<img src="pics/pic_suzhou/sz1.jpg">
<img src="pics/pic_suzhou/sz2.jpg">
<img src="pics/pic_suzhou/sz3.jpg">
<img src="pics/pic_suzhou/sz4.jpg">
<img src="pics/pic_suzhou/sz5.jpg">
<img src="pics/pic_suzhou/sz6.JPG">
<img src="pics/pic_suzhou/sz7.JPG">
<img src="pics/pic_suzhou/sz8.JPG">
</div>
Thanks!
$img.on('load', triggerAction);
function triggerAction() {
//identify the div and put
.style.display = "none";
}
You hide the cover on the window load event, which fires after all images are loaded.
$(window).on('load', function(e){
$('.cover').hide();
});
EDITED!
I found that it won't work the way I offered, so I've made a complete edit, proposing the way it will work. The best way I see is using Promises. This will guarantee that even the even is missed because the handler is attached after the event is fired, we will be able to get its result as success of failure. The other ways may fail because of the cached images and/or async handling. That's why Promises are made for.
Check it out running the snippet below:
$(function(){
function load(img) {
return new Promise(function(resolve, reject) {
img.onload = function() {
console.log(img);
resolve('OK');
};
img.onerror = function() {
reject(Error("Loading error"));
};
});
}
var all = $.map($('#image1 img').get(), function(item) { return load(item); });
Promise.all(all).then(function() {
console.log('All loaded');
}, function() {
console.log('One or more failed');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="image1">
<img src="https://shop.r10s.jp/book/cabinet/5963/4988064935963.jpg?v=30">
<img src="https://images-na.ssl-images-amazon.com/images/I/816L51ge55L._SL1202_.jpg?v=30">
<img src="https://img-shop.mu-mo.net/img/pc_sp/aaa2018_m2th4i/header01.jpg?v=30">
<img src="https://img-shop.mu-mo.net/img/pc_sp/aaa2018_m2th4i/header01.jpg?v=30">
</div>
You don't need jQuery. Modern Javascript provides idiomatic ways to deal with this functionality that are better alternatives to common jQuery functionality.
Important: at the moment there is no 100% reliable way of detecting when images in the HTML have finished loading.
Because of this, the most reliable way would be to dynamically create the image elements, and use the 'load' event. You can also fall-back to the 'load' event on the 'document' which will fire when all resources on the page are loaded.
For example, the following code removes your dimmer div when all images have been loaded, and also has a fallback on window.onload to remove the dimmer which gets triggered even if there are any errors:
const loader = document.getElementById('loader1');
const container = document.getElementById('image1');
const imageURLs = [
"https://lorempixel.com/48/48/abstract/1/",
"https://lorempixel.com/48/48/abstract/2/",
"https://lorempixel.com/48/48/abstract/3/",
"https://lorempixel.com/48/48/abstract/4/",
"https://lorempixel.com/48/48/abstract/5/",
"https://lorempixel.com/48/48/abstract/6/",
"https://lorempixel.com/48/48/abstract/7/",
"https://lorempixel.com/48/48/abstract/8/",
];
// Fallback handler that will run when everything has loaded.
window.addEventListener('load', onDocumentLoaded);
// Create images from list of sources
const images = imageURLs.map(src => {
const image = new Image();
// Return a promise for the loaded image.
return new Promise((resolve, reject) => {
// resolve when loaded
image.addEventListener('load', () => {
console.log(`image loaded ${src}`);
resolve();
});
// reject on error
image.addEventListener('error', (err) => {
console.log(`error loading ${src}`);
reject(err);
});
// Once the handlers are set, set the src and add the images to the DOM.
image.src = src;
container.appendChild(image);
});
});
// Act once all promises are resolved:
Promise.all(images)
.then(onAllImagesLoaded)
.catch(e => {
console.log('something went wrong.');
});
// All images have been loaded -- success case.
function onAllImagesLoaded() {
console.log('all images loaded');
loader.remove();
}
// Everything finished loading -- fallback and error cases.
function onDocumentLoaded() {
console.log('all content loaded');
loader.remove();
}
<html>
<head></head>
<body>
<div id="loader1" class="ui active dimmer">
<div class="ui text loader">Loading...</div>
</div>
<div id="image1" class="ui small images">
</div>
</body>
</html>

How to run code after an image has been loaded using JQuery

I'm trying to load an element only after the img element has been loaded, but I've tried everything I can think of and nothing is working. Right now I'm trying to see where the code stops working and I found that the alert I set up isn't running after the line I need the code to execute from.
$img = $('#picture');
function bubbleOnLoad() {
$(document).ready(function() {
$img.load(function(){
alert('document loaded')
$('#left-bubble').show();
})
})
The $img is defined within a function. The alert works at the document.ready line but not after the $img.load. I have tried to add eventlisteners, jquery .on, .load, and a bunch of others. I'm also calling the function to run within my init function. Can someone explain to me why nothing is working?
function choosePic()
$('.speechbubble').hide();
$('#picture').remove();
var randomNum = Math.floor(Math.random() * samplePics.length);
var img = new Image();
img.onload = function() {
$('.playing-field').prepend(img);
handleImageLoad();
}
img.src = samplePics[randomNum];
img.id = "picture";
}
var samplePics =
"assets/images/barack-obama.jpg",
"assets/images/donald-trump_3.jpg",
"assets/images/dt-2.jpg",
"assets/images/bill-clinton.jpg",
"assets/images/Rose-Byrne.jpg",
"assets/images/pic.jpeg",
"assets/images/priest.jpg",
"assets/images/tb.jpg",
"assets/images/test.jpg",
"assets/images/amy-poehler.jpg",
"assets/images/stephen-colbert.jpg",
"assets/images/aziz-ansari.jpg"
];
You had some syntax errors in your code which I corrected and came up with this:
function bubbleOnLoad() {
$img = $('#picture');
$img.load(function () {
alert('document loaded');
$('#left-bubble').show();
});
}
$(document).ready(function () {
bubbleOnLoad();
});
Here is the JSFiddle demo
In you code you are not even telling the browser it's supposed to run a code after image load. you should do something like this:
$(function(){
// DOM Content is ready, let's interact with it.
$('#picture').attr('src', 'image.jpg').load(function() {
// run code
alert('Image Loaded');
});
});
Also according to docs a common challenge developers attempt to solve using the .load() shortcut is to execute a function when an image (or collection of images) have completely loaded. There are several known caveats with this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Can cease to fire for images that already live in the browser's cache
Try this jQuery plugin waitForImages
https://github.com/alexanderdickson/waitForImages
//Javascript/jQuery
$(document).ready(function(){
var counter = 0,totalImages= $('#preloader').find('img').length;
$('#preloader').find('img').waitForImages(function() {
//fires for all images waiting for the last one.
if (++counter == totalImages) {
$('#preloader').hide();
yourCallBack();
}
});
});
/*css*/
#preloader{
position:absolute;
top:-100px;/*dont put in DOM*/
}
#preloader > img{
width:1px;
height:1px;
}
<!--HTML [add the plugin after jQuery] -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery.waitforimages/1.5.0/jquery.waitforimages.min.js" ></script>
<!--HTML [after body start] -->
<div id=preloader>
<img src="1.jpg" />
<img src="2.jpg" />
<img src="3.png" />
<img src="4.gif" />
<img src="5.jpg" />
</div>
You can only access the HTML element after it was created in DOM.
Also you need to check if the image was loaded before showing.
Thus your code need to look something as below:
$(document).ready(function() {
bubbleOnLoad();
});
function bubbleOnLoad() {
var newSrc = "https://lh5.googleusercontent.com/-lFNPp0DRjgY/AAAAAAAAAAI/AAAAAAAAAD8/Fi3WUhXGOY0/photo.jpg?sz=328";
$img = $('#picture');
$img.attr('src', newSrc) //Set the source so it begins fetching
.each(function() {
if(this.complete) {
alert('image loaded')
$('#left-bubble').show();
}
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="left-bubble" style="display:none">
<img id="picture"/>
</div>

How can I put two functions (one must delay) in one onclick button?

I have a problem to combine to functions in one onclick button. This is how my product have to look like: I want to put a hammer down when I click on an invisible button, when the hammer reaches an icon, the icon has te become another picture.
This is the code I have, but it's in javascript and jquery:
$('.box hammer').on('click', function() {
$(this).toggleClass('clicked');
});
function changeImage1() {
var image = document.getElementById('safari');
if (image.src.match("bulbon")) {
image.src = "safari.png";
} else {
image.src = "safariflat.png";
}
}
You can bind multiple on handlers to the same element and event, that's not a problem.
Delaying one of those handlers can be done in many ways, how is up to you. Libraries like Underscore and lodash offer a number of delay types, such as debounce.
You should be able to use something like:
function immediateHandler() {
alert("Immediate handler!");
}
function lateHandlerImpl() {
alert("Late handler!");
}
var lateHandler = _.delay(lateHandlerImpl, 2500);
$('div.button').on('click', immediateHandler);
$('div.button').on('click', lateHandler);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="button">Click Me!</div>
Replace the alerts with your logic (creating/switching the images) and change the timing to sync the image changes up, and you should be able to create a fairly convincing animation.
You could use setTimeout for this:
$('.box hammer').on('click', function() {
$(this).toggleClass('clicked');
setTimeout(function() {
changeImage1();
// change time below to the time your animation takes
}, 5000);
});
function changeImage1() {
var image = document.getElementById('safari');
if (image.src.match("bulbon")) {
image.src = "safari.png";
} else {
image.src = "safariflat.png";
}
}

Javascript jQuery How to load default image if

I wanted to load the default image if the user's one didn't exist. I tried this but it didn't work.
$('#avatar').load(function(){
// ... loaded
}).error(function(){
// ... not loaded
$(this).attr('src','/images2/no-avatar2.png');
});
Anyone know how I could do this?
Someone uses this:
function loadDefault(f, ff) {
img = document.getElementById(f);
if(!img.complete) {
img.src = ff;
}
if(typeof(img.naturalWidth) != "undefined" && img.naturalWidth == 0) {
img.src = ff;
}
}
function init() {
setTimeout("loadDefault('side_avatar', '/images/default/avatar_player_profile.gif')", 100);
setTimeout("imageResize(740)", 100);
tooltip = new ToolTip();
tooltip.init('hoverBox', 'loading...');
}
How could I use something like this?
It looks like you are missing your load url as the first parameter of the load() function. Also, without seeing your HTML, I'll assume #avatar is an img tag the way you are using it in the error function. I don't think you can load HTML into an img so you may be better off structuring your html and js like so:
HTML Example:
<div id="avatarDiv">
<img id="avatar" src="" alt="" />
</div>
JS Example:
$('#avatarDiv').load('avatar.php', function() {
// ... loaded
}).error(function() {
// ... not loaded
$('img#avatar').attr('src','/images2/no-avatar2.png');
});
Then just make sure your avatar.php file returns the full html of the image object:
<img id="avatar" src="successful-avatar.jpg" alt="" />
Docs: http://api.jquery.com/load/
I'd try something like this:
$.get("get-avatar.php", function(response) {
$("#avatar").attr('src', response);
}).error(function(response) {
$(this).attr('src', '/images2/no-avatar2.png');
})
as long as avatar.php returns a http error message if the image doesn't exist.
This routine uses ajax to see if the image file exists:
HTML:
<img id='avatar' src='/images2/avatar2find.png' alt='No Image Available'></img>
Script:
$(window).load(function() {
var $img = $('#avatar');
$.ajax({
url: $img.attr('src'),
type: 'get',
statusCode: {
404: function() {
$img.attr('src', '/images2/no-avatar2.png');
}
},
error:
function() {
// do something?
}
});
});

jquery event once all images are loaded (including cached images)?

I have the following function which is for ajaxing in a page and the showing it only once all the images are loaded:
$.get('target-page.php', function(data){
var $live = $('#preview_temp_holder').html(data);
var imgCount = $live.find('img').length;
$('img',$live).load(function(){
imgCount--;
if (imgCount==0){
//DO STUFF HERE ONCE ALL IMAGES ARE LOADED
$('#preview_pane').html($live.children()).fadeIn(800);
$live.children().remove();
}
});
});
The problem comes with cached images not firing the .load() event and thus not decrementing the imgCount.
I know i need to implement Nick Craver's solution but am not sure how. Can anyone help me?
I spent a long time looking for solutions for this. The plugin suggested on the jQuery API page (https://github.com/peol/jquery.imgloaded/raw/master/ahpi.imgload.js) did not work in firefox.
My solution was to loop through each image and only add the load event if it was not already loaded (i.e. cached by the browser). The code below includes a counter that I was using to check that load events were only firing for images not already in the cache:
$(document).ready(function () {
var images = $("img.lazy");
$(".image-wrapper").addClass("loading");
var loadedCount = 0;
images
.hide()
.each(function () {
if (!this.complete) {
$(this).load(function () {
loadedCount++;
console.log(loadedCount);
displayImage(this);
});
}
else {
displayImage(this);
}
});
});
function displayImage(img) {
$(img).fadeIn(300, function () {
$(this).parents(".image_wrapper").removeClass("loading");
});
}
I'm no javascript expert so if you spot any problems with this solution please do let me know. As I say, it works in IE8, Chrome and Firefox (not sure about older versions of IE).
Ok, managed to get them merged:
$.get('target-page.php', function(data){
var $live = $('#preview_temp_holder').html(data);
var $imgs = $live.find('img')
var imgCount = $imgs.length;
$imgs.one('load', function() {
imgCount--;
if (imgCount==0){
//DO STUFF HERE
//ALL IMAGES ARE LOADED
$('#preview_pane').html($live.children()).fadeIn(800);
}
})
.each(function() {
if(this.complete){
$(this).load();
}
});
});
note: a 404-ing image would break this.
Change:
$('img',$live).one('load', function(){
...
});
And after above append:
$live.find('img').each(function() {
if(this.complete) $(this).load();
});
Generally I suggest to reuse $live.find('img') from previous count statement.

Categories