I am developing a website and I have a problem with lazy loading images.
It was taking too much time to load the images so I checked the Browser Network Tab and I saw that Safari was loading multiple times the same image, slowing down the loading of the other media.
I reproduced the code to make as simple as possible and when I lazy load images the browser load them twice.
<style type="text/css">
[data-src]{
opacity: 0;
transition: all .4s ease;
}
[data-src].vis{
opacity: 1;
}
</style>
<img data-src="https://via.placeholder.com/150/045b1f/ffff11" src="dot.png">
<img data-src="https://via.placeholder.com/150/045b44/ffff22" src="dot.png">
<img data-src="https://via.placeholder.com/150/045b77/ffff33" src="dot.png">
<img data-src="https://via.placeholder.com/150/045b00/ffff44" src="dot.png">
<script type="text/javascript">
function lazy(){
$('[data-src]').each(function(){
var $img = $(this);
var src = $img.attr('data-src');
var img = new Image();
img.src = src;
$(img).on('load', function(){
console.log('loaded',src);
$img.attr('src',this.src);
$img.addClass('vis');
});
});
}
jQuery(function($){
setTimeout(function(){
lazy();
},3000)
});
</script>
I also made a CodePen https://codepen.io/nbl7/pen/GaoZXW
On my website this happen only on Safari and it loads the same image even 10 times. Probably because I call the lazy load function different times.
On the code I provided it happens also on Chrome.
Should the browser know already which image has been loaded and cache them without making new requests?
Thanks for the help.
Here's a code that doesn't load the images twice:
function lazy() {
$('[data-src]').each(function() {
var img = this;
var src = $(img).attr('data-src');
$(img).attr('src', src);
$(img).on('load', function() {
console.log('loaded', src);
$(img).addClass('vis');
});
});
}
jQuery(function($) {
// lazy();
setTimeout(function() {
lazy();
}, 3000)
});
[data-src] {
opacity: 0;
transition: all .4s ease;
}
[data-src].vis {
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img data-src="https://via.placeholder.com/150/045b1f/ffff11" src="">
<img data-src="https://via.placeholder.com/150/045b44/ffff22" src="">
<img data-src="https://via.placeholder.com/150/045b77/ffff33" src="">
<img data-src="https://via.placeholder.com/150/045b00/ffff44" src="">
You set the img src twice in your loop, and that means that it loads twice:
on this line: img.src = src; (line 6)
then on this line: $img.attr('src',this.src); (line 9)
Related
I have a basic Node js express web application with webix UI on the front-end.
There are a lot of components/tables with a lot of data that needs to be populated in these tables.
I have a loading screen with some background-images changing background image URL every 3 secs until the loading of all the data is complete.
The problem with the loading screen is that the images are not the first thing that get downloaded by the browser. The tables sometimes populate even before the images get downloaded and it beats the whole purpose of having the loading screen.
I was wondering if there was a way I can load these images first thing when the application is opened in the browser.
Here is the HTML Code:
<body>
<div id="container-id" class="container">
<div id="text-id" class="text">Loading...</div>
</div>
</body>
Here is the CSS:
#-webkit-keyframes changeBg
{
0% {background-image: url("/resources/images/one.jpg");}
25% {background-image: url("/resources/images/two.jpg");}
50% {background-image: url("/resources/images/three.jpg");}
75% {background-image: url("/resources/images/four.jpg");}
100% {background-image: url("/resources/images/five.jpg");}
}
.container {
height: 100%;
width: 100%;
background-image: url("/resources/images/one.jpg");
-webkit-animation: changeBg 15s infinite;
background-size: 100%;
background-position: center;
}
Based on this similar question: load specific image before anything else
I tried loading the images using the javascript as first few lines in the index page as follows:
<head>
<script type="text/javascript">
(new Image()).src= window.location.href + "resources/images/one.jpg";
(new Image()).src= window.location.href + "resources/images/two.jpg";
(new Image()).src= window.location.href + "resources/images/three.jpg";
(new Image()).src= window.location.href + "resources/images/four.jpg";
(new Image()).src= window.location.href + "resources/images/five.jpg";
</script>
<link rel="stylesheet" href="webix.css" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="app.css" type="text/css" media="screen" charset="utf-8" />
</head>
But still, the other requests get served way before the images.
Actually scripts are loaded by order they are placed in the head. Browser won't wait for event to being raised then go for other scripts to load (It doesn't make sense). So your script is loaded completely but it's event for image loading are not raised.
So we have to await until all images are loaded then load scripts.
Here is the code:
<script type="text/javascript">
function loadImageAsync(url) {
return new Promise((resolve) => {
let img = new Image();
img.src = url;
img.addEventListener('load', e => resolve(img));
});
};
async function loadImages() {
var im1 = loadImageAsync("imageUrlOne");
var im2 = loadImageAsync("imageUrlTwo");
var image1 = await im1;
console.log(`image one loaded..., url:${image1.src}`)
var image2 = await im2
console.log(`image two loaded..., url:${image2.src}`)
}
function loadScript(url) {
var script = document.createElement("script")
script.type = "text/javascript";
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
loadImages().then(() => {
console.log("loading scripts...");
loadScript("script 1 url here");
loadScript("script 2 url here");
});
</script>
You can use react js for such things, Reactjs makes lots of thing like this easy.
To know more about react .
Visit: https://reactjs.org/
import React from "react";
class ImageWithStatusText extends React.Component {
constructor(props) {
super(props);
this.state = { imageStatus: "loading" };
}
handleImageLoaded() {
this.setState({ imageStatus: "loaded" });
}
handleImageErrored() {
this.setState({ imageStatus: "failed to load" });
}
render() {
return (
<div>
<img
src={this.props.imageUrl}
onLoad={this.handleImageLoaded.bind(this)}
onError={this.handleImageErrored.bind(this)}
/>
{this.state.imageStatus}
</div>
);
}
}
export default ImageWithStatusText;
I'm using the plugin from https://github.com/alexanderdickson/waitForImages to detect when is the image loaded.
Below is my code:
$('.marquee').waitForImages(function() {
console.log('All images have loaded.');
setTimeout(startMarquee);
}, function(loaded, count, success) {
console.log(loaded + ' of ' + count +
' images has ' + (success ? 'loaded' : 'failed to load') + '.');
$(this).addClass('loaded');
});
I will start a marquee scrolling of images when the images is loaded complete.
My problem is, some images had not yet load but just show a small empty square box like this:
,
the plugin also consider it already load. Any idea how to fix it?
Does showing a small empty square box only is consider image loaded?
Just write your own.
<marquee id="marquee" style="visiblity:hidden">
<img src="image1.jpg" onload="countMe(this,1)" onerror="countMe(this,0)"/>
<img src="image1.jpg" onload="countMe(this,1)" onerror="countMe(this,0)"/>
<img src="image1.jpg" onload="countMe(this,1)" onerror="countMe(this,0)"/>
</marquee>
<script>
var imageCount = 0, nofImages=$("#marquee img");
function countMe(img,success) {
if (!success) $(img).hide();
imageCount++;
if (imageCount == nofImages) $("#marquee").show();
}
</script>
If you want to give the image a chance and not load the marquee if permanent error you can try
<marquee id="marquee" style="visiblity:hidden">
<img src="image1.jpg" onload="countMe(this)" onerror="reloadMe(this)"/>
<img src="image2.jpg" onload="countMe(this)" onerror="reloadMe(this)"/>
<img src="image3.jpg" onload="countMe(this)" onerror="reloadMe(this)"/>
</marquee>
<script>
var imageCount = 0, nofImages=$("#marquee img");
function countMe(img) {
imageCount++;
if (imageCount == nofImages) $("#marquee").show();
}
function reloadMe(img) {
var tries = img.getAttribute("tries")?parseInt(img.getAttribute("tries"),10):1;
if (tries) == 3) return; // stop it
tries++;
img.setAttribute("tries",tries);
img.src=img.src;
}
</script>
If you want to wait till all page images loaded before running your marquee code you can use:
$(window).on("load", function() {
// all page html and images loaded
});
More details can be found in this questions:
Official way to ask jQuery wait for all images to load before executing something
I have a page where the images are supplied dynamically and are scaled with javascript to fit within the appropriate dimensions. This was initially being done with an onload attribute in the img tag, but then I noticed that in IE, the height being returned for the image was much less in some cases than the actual height, which ended up distorting the image. I solved this by finding and resizing all the images after $(window).load() was done, which worked fine for the initial page load, but I also have the page set up to add more content with an ajax call. For the ajax content, I tried some code I found on here that improved the problem, but didn't completely solve it. Here is an example of one of my image tags
<img id="img<?php echo $prodModObj->rolloverID; ?>" class="mbImg unsized" src="<?php echo $prodModObj->img; ?>" alt="<?php echo $prodModObj->name; ?>" onerror="swapImage(<?php echo $prodModObj->rolloverID; ?>)" />
The swapImage function just swaps out the image with a placeholder if there is an error while loading. Here is my JS
function swapImage(thisImgID) {
var imgID = 'img#img' + thisImgID;
$(imgID).attr('src', '/images/NoImageAvail.jpg');
}
function checkImage(thisImgID, fitDimension, spaceDimension) {
var imgID = 'img#img' + thisImgID;
var imgHeight = $(imgID).height();
var imgWidth = $(imgID).width();
var displayHeight, displayWidth, newMargin;
if (imgHeight > imgWidth) {
displayHeight = fitDimension;
displayWidth = imgWidth*(displayHeight/imgHeight);
} else if (imgHeight < imgWidth) {
displayWidth = fitDimension;
displayHeight = imgHeight*(displayWidth/imgWidth);
} else {
displayWidth = fitDimension;
displayHeight = fitDimension;
}
$(imgID).css('height', displayHeight);
$(imgID).css('width', displayWidth);
newMargin = ((spaceDimension - displayHeight)/2);
$(imgID).css('margin-top', newMargin);
$(imgID).removeClass('mbImg unsized').addClass('mbImg sized');
}
And then on the page I have
$(window).load(function(){
// Resize product images
$('.mbImg.unsized').each( function() {
var rolloverID = $(this).attr('id').substr(3);
checkImage(rolloverID,250,270);
});
});
And then in the success portion of the ajax call, I have
$('.mbImg.unsized').each( function() {
var rolloverID = $(this).attr('id').substr(3);
if (this.complete) {
checkImage(rolloverID,250,270);
} else {
$(this).on('load', function(){
checkImage(rolloverID,250,270);
});
}
});
Images that have been cached by the browser work fine, and the images in the initial page load work fine, but about 1 in 5 of new ajax images come out distorted. Is there another method I can use to size all the ajax images correctly in IE?
Thanks for your help,
Maybe come at it another way?
I've tried to move away from html4 style tag syntax, to using simple html5 tags and a combination of JavaScript and CSS to control the "view".
Check out this fiddle:
http://jsfiddle.net/zacwolf/s1haq3mz/
A question becomes how you want your images to flow, as using this approach all of the images are technically the same size (as demonstrated by the border). Also note that the .src for the second image I tweeked the url a bit so that it was a 404 for the image file, which triggered the one error image instead.
<img id="one" class="myclass" />
<img id="two" class="myclass" />
<style>
.myclass{
height:270px;
width:250px;
background-position:center,center;
background-repeat:no-repeat;
background-size:contain;
}
</style>
<script>
var one = new Image();
one.onerror=
function(){
this.src='http://leomarketingep.com/wp-content/uploads/Sign-Error-icon.png'
}
one.onload=
function(){
$('#one').css('background-image','url('+one.src+')')
}
one.src='https://cjjulian.files.wordpress.com/2009/04/blah_blah_blah-703369.jpg';
var two = new Image();
two.onerror=
function(){
this.src='http://leomarketingep.com/wp-content/uploads/Sign-Error-icon.png';
}
two.onload=
function(){
$('#two').css('background-image','url('+two.src+')')
}
two.src='https://cjjulian.files.wordpress.com/2019/04/blah_blah_blah-703369.jpg';
</script>
If you have a lot of images, you can populate an array of Image objects, for better referencing, etc.
I want to show a low-res image and start loading the hi-res image after the complete page has rendered. Only when the hi-res image is completely loaded, I want to replace the low-res image by the hi-res image.
I know I should use
$(window).load(function(){
});
But what do I do to actually start loading a certain image? And how do I know when the image is loaded?
(I know about the existence of lazyload, but this image has to be the background of an element, so I have to use .css("background-image", var) and can't use lazyload I guess.)
Use this script to preload the images:
<div class="hidden">
<script type="text/javascript">
<!--//--><![CDATA[//><!--
var images = new Array()
function preload() {
for (i = 0; i < preload.arguments.length; i++) {
images[i] = new Image()
images[i].src = preload.arguments[i]
}
}
preload(
"images-1.png",
"images-2.png",
"images-3.png",
"images-4.png"
)
//--><!]]>
</script>
</div>
Then use low-res images in your html as you need and change to hi-res images when needed. The preloaded image is there for you.
$(window).load(function(){
$(selector).css('background-image',images[0]);
});
In your HTML
<img src='lowres.gif id='mypic'>
<script>
var img = document.getElementById("mypic");
var img2 = new Image();
var img2.onload = function() {
img.parent.insertBefore(img2, img);
// Or hide it.
img.style.display = "none";
};
var img2.src = XXXXX;
</script>
So I've got a header image that upon being clicked animates with transit, fades out, and fades back in with a new image. I'm then able to click on the new image and it will also animate and fade out to blank space. How can I continuously animate/fade through a series of ordered images - one new image per click ad infinitum?
Here's what I've got so far...
$(document).ready(function(){
$("#headuh").click(function(){
$("#headimg").transition({ skewY: '30deg' },1000);
$("#headimg").fadeOut(function() {
$(this).transition({ skewY: '00deg' }) .load(function() { $(this).fadeIn(); });
$(this).attr("src", "/imgurl.jpg");
You can use the javascript function called setInterval(); like this:
setInterval(function(){
//this code will keep on executing on every 2 seconds.
},2000)
The following snippet doesn't solve your problem exactly, but it might put you on the right track, as it randomly cycles through a set of 28 images with fading in and out. You can see it live at http://moodle.hsbkob.de:
<script src="jquery-1.4.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
var g_first_call = true;
var g_pic_old;
$(document).ready(function() {
pickPic();
setInterval("pickPic()", 7500);
});
function randomPic() {
do {
var pic = parseInt(28*Math.random()+1);
} while (pic == g_pic_old); // Don't want same pic twice
g_pic_old = pic;
document.getElementById("_image_").src = "./picfolder/picno" + pic + ".jpg";
}
function pickPic() {
if (g_first_call) {
randomPic();
g_first_call = false;
}
$("#_fadee_").fadeIn(750, function() {
$("#_fadee_").delay(6000).fadeOut(750, randomPic);
});
}
//]]>
</script>
<div id="_fadee_"><img id="_image_" alt="Image" /></div>