35

I have the following CSS class that I'm applying on a <td> tag:

.bg {
   background-image: url('bg.jpg');
   display: none;
}

How can I tell with JavaScript/jQuery that the background image finished loading?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
thedp
  • 8,350
  • 16
  • 53
  • 95

6 Answers6

60

The only way I know of to do this is to load the image using Javascript, and then set that image as the backgroud.

For example:

var bgImg = new Image();
bgImg.onload = function(){
   myDiv.style.backgroundImage = 'url(' + bgImg.src + ')';
};
bgImg.src = imageLocation;
Jamie Dixon
  • 53,019
  • 19
  • 125
  • 162
  • For some weird reason the onload only fires when the imageLocation actually takes some time to load, like from the web. It doesn't work in local testing with 0 loading time. – thedp Dec 23 '09 at 01:07
  • 2
    you could set a timeout for the `bgImg.src = imageLocation;`, but I'd assume most people won't be on your computer, :P – Dan Beam Jan 02 '10 at 10:02
  • This actually has a bug when `bgImg.src` contains special characters like single and double quotes and backslashes. Instead you should make it `'url("' + bgImg.src.replace("\\", "\\\\").replace('"', '\\"') + '")';`. I think that should escape any url properly. – David Goldstein Sep 10 '15 at 03:20
  • I wonder if it would be wise to set `bgImg = null;` after assigning the URL to the background image, since there is no real need for the JS object. The concern being the browser might have 2 copies and waste memory? Implementation dependent, surely. – quickshiftin Apr 06 '19 at 21:45
  • This will try to fetch the image twice, hopefully the browser cache will prevent the second fetch from actually calling the server – Ben Winding Jul 10 '22 at 09:17
4

Give the class to a div with visibility:hidden at the initial page load. That way, it'll already be in the browser cache when you assign the class to your table cell.

Gausie
  • 4,291
  • 1
  • 25
  • 36
2

This article may help you. Relevant section:

// Once the document is loaded, check to see if the
// image has loaded.
$(
    function(){
        var jImg = $( "img:first" );

        // Alert the image "complete" flag using the
        // attr() method as well as the DOM property.
        alert(
            "attr(): " +
            jImg.attr( "complete" ) + "\n\n" +

            ".complete: " +
            jImg[ 0 ].complete + "\n\n" +

            "getAttribute(): " +
            jImg[ 0 ].getAttribute( "complete" )
        );
    }
);

Basically select the background-image and do the check to see it's loaded.

DanielST
  • 13,783
  • 7
  • 42
  • 65
AJM
  • 32,054
  • 48
  • 155
  • 243
2

@Jamie Dixon - he didn't say he wanted to do anything with the background image, just know when it's loaded...

$(function( )
{
    var a = new Image;
    a.onload = function( ){ /* do whatever */ };
    a.src = $( 'body' ).css( 'background-image' );
});
Dan Beam
  • 3,632
  • 2
  • 23
  • 27
  • Will it work if the element on which I applied background has the property - display:none; ? – thedp Dec 18 '09 at 19:52
  • yes, as far as I know this doesn't matter, it's just grabbing the style rules and forcing the image to preload (essentially), when it is done preloading it fires an onLoad event. however you don't want to do this if you want to lazy load a large image (as it may slow things down) – Dan Beam Dec 19 '09 at 00:30
  • I'm having trouble to make it work. Here is my style: background-image: url('../Images/bg/xxx.jpg'); The a.src gives me the image location with the url(''); It seems that the onLoad is never fired. (I also tried onload) – thedp Dec 23 '09 at 00:21
  • hey thedp, I just edited my code. it wasn't working for me, though I swear I was when I posted it. try the new version. – Dan Beam Jan 02 '10 at 12:52
1

One issue with onload is that it fires when the data is ready, not when it is done rendering.

For one project I load a bunch of medium large images. As the images are loading one get the effect of images “popping into existence” in a fragmented way.

Solved this by using a combination of onload and decode.

As for Q: “How can I tell that the background image finished loading?”. It can be interpreted in several ways; but if one is looking for finished rendering this is an attack on that issue.

Simplified:


img.onload = () => {
    some_elm.style.backgroundImage = 'url(' + some_src + ')';
    img.decode().then(some_function).catch(some_function);
}
img.onerror = some_function;
img.src = some_src;

Where some_function simply checks a counter on all the images loaded, but can of course also be used for one image.

Guess it can easily be the overhead of decode() that causes it to flow nice, but have worked very well.

Have found it to work nice both for loading <img> elements, (using decode only), and backgrounds (onload + decode).


Then one can do anything from simply adding a class to a wrapper or add some fancy effects - for example fading in image by image in ordered fashion etc.

Also note Browser Compatibility for decode().

user3342816
  • 974
  • 10
  • 24
0

You also can provide a function that simply replaces the img tag by the div/background so that you benefit from both the onload attribute and the flexibility of the div.

Of course, you can fine tune the code to best suits your need, but in my case, I also make sure that either the width or the height is preserved for a better control of what I expect.

My code as follows:

<img src="imageToLoad.jpg" onload="imageLoadedTurnItAsDivBackground($(this), true, '')">

<style>
.img-to-div {
    background-size: contain;
}
</style>

<script>
// Background Image Loaded
function imageLoadedTurnItAsDivBackground(tag, preserveHeight, appendHtml) {

    // Make sure parameters are all ok
    if (!tag || !tag.length) return;
    const w = tag.width();
    const h = tag.height();

    if (!w || !h) return;

    // Preserve height or width in addition to the image ratio
    if (preserveHeight) {
        const r = h/w;
        tag.css('width', w * r);
    } 
    else {
        const r = w/h;
        tag.css('height', h * r);
    }
    const src = tag.attr('src');

    // Make the img disappear (one could animate stuff)
    tag.css('display', 'none');

    // Add the div, potentially adding extra HTML inside the div
    tag.after(`
        <div class="img-to-div" style="background-image: url(${src}); width: ${w}px; height:${h}px">${appendHtml}</div>
    `);

    // Finally remove the original img, turned useless now
    tag.remove();
}
</script>
Stéphane de Luca
  • 12,745
  • 9
  • 57
  • 95