I have a classified style website with 50-100 images per page all loading from different sources at once. Sometimes I get a 404 and I'm trying to handle that.
On each element containing images I have this script:
$el.find('img').one('error', function() {
console.log('broken image detected');
// Replace broken image with something else
}).each(function() {
if(this.complete || $(this).height() > 0){
$(this).load();
}
});
But it seems to fire at random, not picking up all 404 load errors - especially at the beginning of the page where literally none of the 404s are detected. Towards the end of the page it looks a bit better.
What's going on?
I should add, that this piece of JavaScript is not initialised BEFORE the entire DOM has finished loading, but I was under the impression that the .each part would cater for this?
Another idea would be to insert an inline load error detector:
<img src="http://example.com/image.jpg" onerror="window.errorHandler(this);">
But since my JavaScript is modular in design I would prefer to avoid this.
--- EDIT ---
I have solved this problem by inserting an inline script on the onerror handler. It's not super elegant, but it works because the listener is inserted at the same time as the src attribute. Also it is not dependent on jQuery.
I have done a slight work around in my front-end code to cater for this, by exposing a function to window.
All listeners to handle load or error events must be set BEFORE src property of img is setted.
You may implement lazy loading (i.e. insert <img> paths in a data attribute, put a fake pixel in the src attribute, and load the real images on DOM load with jQuery or JavaScript). You then shouldn't face any timing problems.
HTML:
<div id="gallery">
<img id="img1"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAI="
data-src="imagethatgives404.gif"
width="200" height="200">
<img id="img2"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAI="
data-src="http://static.jsbin.com/images/dave.min.svg"
width="200" height="200">
</div>
JS:
$(document).ready(function(){
var $el = $('#gallery');
$el.find('[data-src]')
.one('error', function(e) {
console.log(e.target.id + ' is broken');
// Replace broken image with something else
})
.each(function(index,element){
element.src = $(element).data('src');
});
});
The width and height on the <img> tags are used to show how dimensions are preserved.
Example here.
Related
When I try to learn Promise from developers.google.com I found there is a code like this.
<img id="img1" src="images1.jpg">
<script>
var img1 = document.querySelector("#img1");
img1.addEventListener("load", function(){
alert("yes");
});
img1.addEventListener("error", function(){
alert("no");
});
</script>
This code is not exactly the same with the one on google page, but should be function the same.
When I saw this I am curious as the script tag is at the very bottom of the page, "it should load the img first then load the js file so after the js file works it could not detect whether the img is loaded or not" I suppose, so I simulate this code in my editor, it turns out it works.
I try to search in google and found that it is surely HTML load from top to bottom, maybe not the same in some new browsers, then I open dev tool in chrome and try to learn sth from the networking, it is also the sequence it should be, which is, html, image file, js.
I need help, I do not understand, am I missing some basic loading knowledge here? or "load" listener doesn't work as I imagine?
Thank you.
Well, that requires some insight in how a browser reads in a HTML document and displays it to the user. Here is your code, but slightly expanded
<p>start page</p>
<img id="img1" src="images1.jpg">
<script>
var img1 = document.querySelector("#img1");
img1.addEventListener("load", function(){
alert("yes");
});
img1.addEventListener("error", function(){
alert("no");
});
</script>
<p>end page</p>
I try to search in google and found that it is surely HTML load from top to bottom
Yes, it reads in the above content in the top to bottom order. The incoming stream is being parsed as the order it comes in. So the first paragraph with text start page is read first, then the image, then the script and then the last paragraph. If an element contains child elements, these get read from top to bottom too until the next element is being read.
But when it reads an element, such as <img>, it initiates a new request to retrieve the image, defined in the src attribute. That's done in a separate task.
After the image element, it finds a <script> tag. Here, its content is then send to the browser's VM to run the script code. The VM executes the code step by step. In your script, you have instructed to
1. find the img1 element
2. listen to its "load" event
3. listen to its "error" event.
Yet, this is done so fast that the browser is able to parse the script before the image is being loaded. Step 1 always succeeds because the element is present and the script is running after. If you place the image element after the script, it would fail.
When step 2 succeeds depends of the size of the image file, network, the document's content, when the script is being handled ect ... If you wrap that code in a function and use setTimeout() with an elapse time, then the alert wouldn't show up because the image file is already displayed before those event listeners are being hooked. Here below are two interactive examples with timeouts:
w/o timeout
<p>start page</p>
<img id="img1" src="images1.jpg">
<script>
console.log('without timeout');
function f() {
console.log('alert?');
var img1 = document.querySelector("#img1");
img1.addEventListener("load", function() {
console.log("yes");
});
img1.addEventListener("error", function() {
console.log("no");
});
}
f();
</script>
<p>end page</p>
with timeout
<p>start page</p>
<img id="img1" src="images1.jpg">
<script>
console.log('with timeout');
function f() {
console.log('alert?');
var img1 = document.querySelector("#img1");
img1.addEventListener("load", function() {
console.log("yes");
});
img1.addEventListener("error", function() {
console.log("no");
});
}
window.setTimeout(f, 1000);
</script>
<p>end page</p>
Browsers load images asynchronously. Here are the steps the browser will execute with your example:
parse the img tag and fire the HTTP request to get the image;
parse the script tag and register all the defined eventHandlers;
when the request to get the image finally completes, it will fire the image's load event;
your eventHandler is then called.
I have a page with lots of images on it. My images are loaded from an external server. Usually this server it's loading tooooooooo slow, so my page stay loading until the external server starts running again. I want to put a blank image loaded from mine when the external server is down.
Is there any method to do something like this?
if ($("img").load=false) {
$("img").src="cantload.png";
}
Thank you so much, in advance!
If you're using jquery, and this piece of code happens before the binding of the image.
$("img").error(function() {
alert("Could not load image");
});
If not, then you could do something like this, which will always work, but will need to be on each image:
<img src="image.gif" onerror="alert('Could not load image.')">
If you want to activate this function after the elements are already in the DOM, you can use something like this.
var defaultSource = 'cantload.png';
$('img').each(function() {
var originalSource = $(this).attr('src');
$(this)
.attr('src', defaultSource)
.error(function(){
$(this).attr('src', defaultSource );
})
.attr('src', originalSource);
});
The above code would bind the inner function to handle the images' loading errors, and then make them all reload. The pictures that are already reloaded won't be actually reloaded again, but those with the errors will trigger the error handler and change the source attribute into cantload.png
jsFiddle Demo
I am getting an inconsistent error with my script. Often times everything works fine, however, every now and then I am getting the following error: ReferenceError: fadeIn is not defined
Here is the relevant code:
In the <head>
<script type="text/javascript" charset="utf-8">
window.fadeIn = function(obj) {
var item = $(obj).parent();
item.fadeIn(1000);
}
</script>
In the <body>
<div class="item" style="display:none"><img onload="fadeIn(this)" src="/uploads/thumbs/{{image.url}}"><br>{{ image.description }}</div>
Again, The images are loading and fading in most of the time but every now and then I get the error and the images do not fade in.
Is there a better way to approach this? Or just something I'm missing?
You need to make sure your code is loaded after the DOM is ready.
window.onload = function(){
var fadeIn = function(obj) {
var item = $(obj).parent();
item.fadeIn(1000);
};
};
... that's a partial fix, but not really, because in all likelihood, your <img/> tags with the onload hardcoded into them is going to try to fire that method before it's available.
Since you're using jQuery anyhow, you should look at something like this:
$(function() {
$(".item img").load(function(){
$(this).fadeIn(1000);
});
});
and get rid of the hardcoded onload from your img tags.
It should be noted, also, that there are caveats listed on the .load() API page:
From the docs (http://api.jquery.com/load-event/):
Caveats of the load event when used with images
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
A better solution might be to hide the images with CSS, then after the load event is fired, fade them in.
(Even better than that might be to let browser behavior be, as your results may be mixed, and users seeing a blank page might instead hit the reload button thinking their browser glitched, before your animations are complete. ... just a friendly warning that these kinds of DOM manipulations can sometimes have unintended side-effects on user behavior!)
You can try defining the Handler directly in the Script instead of assigning it in HTML..
Javascript
<script type="text/javascript" charset="utf-8">
$(function() {
$('img').load(function() {
var item = $(this).parent();
item.fadeIn(1000);
});
});
</script>
HTML
<div class="item" style="display:none">
<img onload="fadeIn(this)" src="/uploads/thumbs/{{image.url}}">
<br>{{ image.description }}</div>
You're missing a document ready, so jQuery is'nt loaded, and the fadeIn function is'nt defined. Even if the image is loaded, there is no guarantee that jQuery is loaded aswell.
You're also actually calling your function fadeIn, while jQuery already has a function called fadeIn, and even though they have a different namespace it does seem like a bad idea to me.
nPlease ensure you have correct link to JQuery javascript file. You can use Google's hosted library for an example or create your own copy of this .js file:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
min.js file can be found at download section of JQuery official website. Save it and place into the website folder. And link it like:
<script src="/js_folder/jquery-1.8.2.min.js"></script>
Another option with Jquery:
<div class="item" style="display:none">
<a href="http://docs.jquery.com/">
<img src="http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif">
</a>
<p>image name</p>
</div>
Script:
<script type="text/javascript">
$(document).ready(function() {
$('.item').delay('1000').fadeIn('1000');
});
</script>
http://jsfiddle.net/CYGNp/1/
I'm a creating a loading screen for website I am making. The website loads many images, scripts, etc. The HTML and CSS part is great, but I need a way to guarantee that the "loading..." image will be loaded before anything else.
I'm using jQuery, and everything is initiated within $(function () { ... });. I imagine that the code for this would need to be called before/outside that block, and the code to remove the loading screen will be called at the very end of that block. Currently, the loading image is set as a DIV background, which is the way I prefer it. However, if it's completely necessary, I will settle for an IMG tag.
Update: (solution)
I was able to answer my own question by using a combination of Robin and Vlad's responses. Both were very good, and excellent answers, however the problem is that they were aimed to load an image before another image, rather than load an image before anything else. (CSS, JS, etc...)
Here's the dirty version of what I came up with:
var files = [new Image(), document.createElement('link'), document.createElement('script')];
files[0].setAttribute('src', 'images/loading.gif');
files[1].setAttribute('rel', 'stylesheet');
files[1].setAttribute('type', 'text/css');
files[1].setAttribute('href', 'test.css');
files[2].setAttribute('type', 'text/javascript');
files[2].setAttribute('src', 'js/jquery-1.5.1.min.js');
window.onload = function (e) {
document.getElementsByTagName('head')[0].appendChild(files[1]);
document.getElementsByTagName('head')[0].appendChild(files[2]);
}
Taking a look at the load sequence on the network tab of Chrome's developer console shows that 'loading.gif' is loaded first, then 4 dummy images, then 'test.css', and then 'jquery.1.5.1.min.js'. The CSS and JS files don't begin to load, until they've been inserted into the head tag. This is exactly what I want.
I'm predicting that I may begin to have some problems, however, when I begin to load a list of files. Chrome reports that sometimes the JS file is loaded first, but the majority of the time the CSS file is loaded first. This isn't a problem, except when I begin to add files to load, I will need to ensure that jQuery is loaded before a script file that uses jQuery.
If anyone has a solution for this, or a way to detect when the CSS/JS files are finished loading, using this method, then please comment. Though, I'm not sure that it's going to be a problem yet. I may need to ask a new question in the future about this, if I start to run into problems.
Thank you to every who has helped with this issue.
Update: (glitch fix)
I ended up running into a lot of problem with this method, because the script files were being loaded asynchronously. If I would clear the browser cache, and then load the page, it would finish loading my jquery dependent files first. Then if I refreshed the page, it would work, because jquery was loaded from cache. I solved this by setting up an array of files to load, then putting the load script into a function. Then I would step through each array item using this code:
element.onload = function() {
++i; _step();
}
element.onreadystatechange = function() {
if (("loaded" === element.readyState || "complete" === element.readyState)) { ++i; _step(); }
}
You can reuse resource prealoding browser support.
I'm not sure it works across all browsers but in my case this approach helps me to load images first. Also it allows to define concrete images so UI specific could be skipped
First define in header what resource you want to preload and define resource priority
<link rel="preload" href="link-to-image" as="image">
or
<link rel="preload" href="link-to-image">
Second line allow to increase loading priority across all object types (scripts / images / styles). First line - only through images.
Then define in body link to image as usual:
<img src="link-to-image" alt="">
Here is my working example
https://jsfiddle.net/vadimb/05scfL58/
As long as the "loading..." image is positioned before any other html elements, it should load first. This of course depends on the size of the image. You could put the loading div right after the tag and position it using 'position:absolute'.
Regarding the code to remove the loading screen, one method is to do the following.
Put all the images, scripts that need to be loaded in a hidden div (display: none)
Set up a variable that will hold the total of the images / scripts to be loaded
Set up a counter variable
Attach to each image / script the "onload" event
Everytime the "onload" event is triggered it will call a function that will increment the counter variable and check if the value of the counter equals the value of the total variable
If all resources have been loaded, fire a custom event that will show the div with the images, and hide the div with the loading screen.
The code below isn't tested so it might not work. Hope it helps
var totalImages = 0;
var loadCounter = 0;
function incrementLoadCounter() {
loadCounter++;
if(loadCounter === totalImages) {
$(document).trigger('everythingLoaded');
}
}
function hideLoadingScreen() {
$('#loadingScreen').hide();
$('#divWithImages').show();
}
$(document).ready(function(e) {
$('#loadingScreen').bind('everythingLoaded', function(e) {
hideLoadingScreen();
});
var imagesToLoad = $('img.toLoad');
totalImages = imagesToLoad.length;
$.each(imagesToLoad, function(i, item) {
$(item).load(function(e) {
incrementLoadCounter();
})
});
})
I'm not sure if it's possible to enforce.
If it is, try adding this in the head-tag:
<script type="text/javascript">
if(document.images)
(new Image()).src="http://www.image.com/example.png";
</script>
In theory that may load and cache that image before anything else.
I think if you place the IMG tag at the top of your html body it will be loaded first. If you do not want to move your div just use a copy of the image tag. Once the images is loaded it will be shown in every image tag which shows the same picture.
Or you could use spin.js as loading image. It display this "loading cycle image" via javascript.
Check it out under:
http://fgnass.github.com/spin.js/
I'm loading image tags via AJAX and inserting them with the conventional .html(content) function in jQuery alongside a bunch of other HTML. However, this question still applies if you're loading a page from scratch. Now, I have a background image placeholder to be put there while the image loads. I want this background image to go away when the image loads.
Problem:
If I attach a conventional .load(function) event listener, I am concerned that the image might load before the hook is applied (putting the hook in a small JS <script> right after the image instead of in a $(function(){}) block might help a bit). I have yet to encounter such behaviour, but I know of nothing in the specification that prevents this from happening (since the image tag ought to be fully parsed before the hook is applied).
My current solution. Put the command in an inline onload= property within the image tag.
Is there a better way?
Up until a week or so ago I would have been lost too. Thankfully this answer to another question will help you out:
Basically put this in $():
$(function(){
var img = $("#idofimage").load(function () {
/* your magic to swap out placeholder */
});
if (img[0].complete) {
// Trigger the load handler if the image
// is already loaded
img.trigger('load');
}
});
You don't need jQuery for this, you can do it with CSS.
.my-img-loader-class { background:url('placeholder-or-progress'); }
Or if you don't want to change your HTML:
#container img { background:url('placeholder-or-progress'); }
To show placeholders while images are loading in a specific div.
The way it works is the image element will show the placeholder image as its background, and when the src loads it will appear above the placeholder, so as to replace it nicely.