How to load all images before showing the page in React? - javascript

I am trying to find a more modern solution that doesn't use jQuery as I am using React (Gatsbyjs specifically).
I have a website with multiple image carousels that contain high res images.
The issue is the each image carousel only show one image at a time, so only when the user navigates to the next image does the image get fetched, this results in a choppy loading appearance.
I have tried researching online with onLoad and load event listeners but none seem to have worked so far because they only load the image that is currently being shown by the carousel, instead of all of the images in the carousel.
If there is a way to first load all the images, then set the state to true, and only after the state is true, then the rest of the DOM appears onto the screen, that would be perfect.
Any suggestions? Thanks.
Website in question: https://dev--yachtgamechanger.netlify.com/

As Lonnie Best said, I ended up using Promise.all() to capture the loads of the images.
Just in case anyone else want to check it out:
https://codesandbox.io/s/react-image-preload-ptosn

I guess that your lib is using lazy load approach. And it's really good when dueling with high res images.
You still can change the approach to load all the images before rendering the UI, by going into the carousel component, change the rendering behavior by checking whether all images be loaded or not before render.
Updated: Because you are using gatsby-image, so just use this property:
loading: "eager". For EX:
<Img
fixed={data.file.childImageSharp.fixed}
alt="Gatsby Docs are awesome"
loading="eager"
/>
https://www.gatsbyjs.org/packages/gatsby-image/

You can ensure that an image is pre-downloaded 100% well before it gets displayed to the screen.
Simply create an Image element and set its src property to the URL where the image is located. Then use the image's onload event to detect when it is ready for instant display.
// Image to Pre-Download:
var image = new Image();
console.time("Image Fully Downloaded in");
image.src = "https://www.nasa.gov/sites/default/files/thumbnails/image/pia24870.jpeg";
image.onload = function()
{
console.timeEnd("Image Fully Downloaded in");
console.log("Image is ready for viewing.");
clearInterval(interval);
progress.parentNode.replaceChild(btn, progress);
}
// Fake Progress Indicator:
let progress = document.createElement("progress");
progress.value = 3;
progress.max = 100;
progress.textContent = "Fake Progress";
document.body.appendChild(progress);
let interval = setInterval(()=>
{
if (progress.value === 99)
{
progress.value = 0;
}
else
{
progress.value = progress.value + 1;
}
},50)
// Button to Replace Progress Indicator:
let btn = document.createElement("button");
btn.textContent = "View Entire Image Instantly";
btn.addEventListener('click',function()
{
this.parentNode.replaceChild(image, this);
});
img { max-width: 100%; }
<p>Only after the image is completely downloaded, will you see the button to view it:</p>
Based on this concept, you could pre-load/queue as many images as you like into image objects, so that they are ready to be displayed instantly (well before the user decides to view the next image). Here's an example where I'm switching between preloaded images automatically (so fast that it looks like an animated gif -- but it is actually 27 separate images from 27 different URL locations): See statue example and view source.

Related

Load images separated from page loading

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.

What is the best way to handle: onload not being triggered for images with size zero in JavaScript?

To make sure an image is load on our page and then proceed to the next state, we have used the onload method as mentioned here:
image = new Image();
image.onload = function(){
// go to the next state
};
image.src = image_url;
The issue is the event is not triggered for images with size zero and as a result the page stays in the halt mode and does not progress to the next state.
What is the best way to handle the issue with onload function not being triggered for images with size zero?
Related questions:
1- new Image(), how to know if image 100% loaded or not?
If you want your process to proceed when the image either finishes loading or fails to load, you can add your event handler to the .onerror event:
var image = new Image();
image.onload = image.onerror = function () {
// go to the next state
};
image.src = image_url;
If you really want to do this with 0 size files, try to make it a script. 0 size script is valid. See:
Trying to fire the onload event on script tag
Or if you really have some real images, but some are "nothing", then use a 1x1 "clear_pixel.gif". You can also make it invisible with css (though then you won't see the real images either), maybe there's a way to detect from js in the onload what's the real size of the image.
Can you give the images a non-0 size and then use position:absolute to place them off the page where they are not visible?
height:1px
width:1px
position:absolute
left:-1000000px
(I'm assuming here that they are actual images with non-zero size, but you made the img element have size 0 in order for the images not to be visible. I'm not exactly sure that's the case.)

Issue with high resolution images in chrome(windows) loading from javascript

I am having a problem with a site. My problem is I am loading several images progressively - starting with a small resolution image for fast loading I am then ajaxing a bigger image in (normally the original size that a user uploads).
The code below works perfectly. HOWEVER when done on chrome on windows. if the bigger image is a really high res (lets say 4400 x 4000). The screen would go white and the image would disappear. The white bursts out of the container (which has overflow:hidden) on it and covers the screen. Only the elements with a higher z-index over the image displays.
If I inspect the element that is white. It shows that it is the element, and the image is loaded - the URL is fine and if I click the image to open in another tab it loads fine.
Does anyone have any idea why this is happening?
if(href){
var img = document.createElement('img');
img.className = 'openLBFullView hidden';
img.onload = function(){
loadBiggerImg(this);
};
$(img).data('url',$currentImg.data('url'));
img.src = href;
img.id = 'my-image';
}
var loadBiggerImg = function(img){
var originalImg = $('#my-image');
//append the img to the document
originalImg.before(img);
// append original styles and classes to new image
$(img).attr('style',originalImg.attr('style'));
$(img).attr('class',originalImg.attr('class'));
// fix for windows IE adding attributes
$(img).removeAttr('width').removeAttr('height');
//fade in new image over the top and remove old one
$(img).fadeIn(200,function(){
originalImg.remove();
});
}
One of the possible solutions - large images dont render in chrome
This not neccesarily will fix your issue though - I'd try using lowres jpegs scaled to highres and preload big one - once loaded fade lowres one and show the big one (in a way fake the progressive JPEG) - Hope that helps man.

Add spinner while new image gets loaded completely

The role of the script is to change image.jpg with newimage.gif and vice versa when clicked.
Now i want to add a spinner while the newimage.gif loads.
Something like on 9gag.com/gif .
Can someone help me please?
This is the code:
html
<img src="/image.jpg" id="pic" onclick="change()"/>
<a id="first" href="/newimage.gif" style="display:none;"></a>
<a id="second" href="/image.jpg" style="display:none;"></a>
javascript
function change(){
var imag = document.getElementById('pic').src;
var img = imag.slice(-3);
var img1 = document.getElementById('second').href;
var imag2 = document.getElementById('first').href;
if(img == 'gif') {
document.getElementById('pic').src=imag2;
// here the newimage.gif changes to image.jpg
} else {
// here the image.jpg changes to newimage.gif
// i think here should be the code that will display a spinner while the image.gif loads completely
document.getElementById('pic').src=img1;
}
}
The easiest way might be to set the CSS background-image property of the images to a loading spinner graphic.
HTML:
<img src="path/to/real/image.jpg" class="loading">
CSS:
img.loading {
background: transparent url(path/to/loading.gif) no-repeat scroll center center;
}
Once the actual image is downloaded, it covers up the "loading" animated GIF background image.
If you have GIFs or JPGs saved with Progressive display, you'll want to resave those images without that option so the real image is invisible until the full image is downloaded allowing your spinner graphic to show through in the meantime.
Have a look at this: http://www.appelsiini.net/projects/lazyload.
Images outside of viewport are not loaded until user scrolls to them, and you have the aboility to have a small image placeholder untill it's loaded...
I've actually used it here, on a testing site, some time ago: http://dev.thomaskile.me/?page=test-zone&module=Masonry.
I found a kind of way, the only problem is that after the image.gif gets loaded the spinner still appears.
if(img == 'gif') {
document.getElementById('pic').src=imag2;
// here the newimage.gif changes back to image.jpg
document.getElementById("spinner").style.display = "none";
} else {
document.getElementById('pic').src=img1;
var image = new Image();
image.src = document.getElementById('second').href;
var x = image.complete;
if(x) {
document.getElementById("spinner").style.display = "none";
} else {
document.getElementById("spinner").style.display = "block";
}
}
}
This is here i use the script http://www.2lol.ro/load/poze_haioase_miscatoare/20?ref=stk
Have you checked out this site for a configurable spinner without additional graphics that can be attached to any dom element ? starting / stopping the spinner boils down to 2 js function calls.
the code comes under the mit license, the minified js standalone file takes 9kb, glue code for use as a jquery plugin is provided and there is a vml fallback for, err... steampunk browsers ;-).
i'm not affiliated in any way with code or author.

Javascript load background-image asynchrously

Is it possible to load a background-image asynchronously?
I've seen many jQuery plugins to load normal image in an asynchronous way, but I can't find if it's possible to preload / asynchronously load a background-image.
EDIT
I clarify my problem. I've been working on this test site http://mentalfaps.com/
The background image is loaded randomly from a set of images refreshed each hour by a chron job (which takes random images on a flickr catalog).
The host is free and slow at the moment, so the background image takes some time to load.
The positioning of the first overlay (the one with the PNG transparent mentalfaps logo) and width are regulated by a function created in the jQuery(document).ready construct.
If you try to refresh the page many times, the width of the right overlay div is 0 (and so you see a "hole" in the layout)
Here is the method to set my positions:
function setPositions(){
var oH = $('#overlay-header');
var overlayHeaderOffset = oH.offset();
var overlayRightWidth = $(window).width() - (overlayHeaderOffset.left + oH.width());
if (overlayRightWidth >= 0) {
$('#overlay-right').width(overlayRightWidth);
} else {
$('#overlay-right').width(0);
}
var lW = $('#loader-wrapper');
lW.offset({
left: (overlayHeaderOffset.left + oH.width() - lW.width())
});
}
The problem is that the $(window).width() is lower then the effective window width! so the check fails and the script goes for $('#overlay-right').width(0);
any ideas?
Not sure whether I really understand your question, but background images (and all other images) are already loaded asynchronously - the browser will start separate requests for them as soon as it encounters the URL while parsing the HTML.
See this excellent answer for background on loading order: Load and execution sequence of a web page?
If you meant something else, please clarify.
The trick to loading something in the background is to load it once, so the next time when it is loaded it already is in the cache.
Put the following at the end of your html:
<script>
var img = new Image();
img.onload = function () {
document.getElementsByTagName('body')[0].style.backgroundImage = 'background.png';
};
img.src = 'background.png';
</script>
You could use a prefetch link in the head.
<link rel="prefetch" href="/images/background.jpg">
You should be able to add these links to the head via JavaScript.
I like to use CSS to fill the background with a color for page load.
After DOM ready event, I use jQuery to modify the CSS and add a background image. That way, the image isn't loaded until after page loads. Not sure about async, but this method gives the user a decent experience.
Example: http://it.highpoint.edu/
The right side navigation links have a background image. The page initializes with a background color. It is replaced with a background image after page load, via jQuery.
changes in this file jquery.ImageOverlay.js
set your height and width and enjoy this...
imageContainer.css({
width : "299px",
height : "138px",
borderColor : hrefOpts.border_color
});
As it is already mentioned, the background image is loaded asynchronously. If you need to load the background image from JQuery code you may also set the addClass() method to set a CSS class or attr("style=\"background-image:url('myimage.png')\"")
Ive found the answer myself, it was a problem due to the .offset() method that gived sometimes the wrong values.
I had the write values using the .position() :
var overlayHeaderOffset = oH.position();

Categories