61

I want to set an image as a background, however the image name might be either bg.png or bg.jpg.

Is there any non-javascript way to create a fallback to an alternative image if the default background doesn't exist?

body{
    background: url(bg.png);
    background-size: 100% 100%;
    width: 100vw;
    height: 100vh;
    margin: 0;
}
3
  • 1
    What's the desired behavior if the image doesn't exist? Commented Jun 2, 2016 at 9:37
  • i will add a file uploader and the uploader will accept png or jpg. based on the image that will be uploaded it will set the file as bg.png or bg.jpg Commented Jun 2, 2016 at 9:40
  • you can add data-fallback attribute to body tag and then check var body = $(document.body); if(!(body.css('background'))) {body.css('background', body.data('fallback ')) } Commented Jun 2, 2016 at 13:21

5 Answers 5

106

You can use multiple backgrounds if there is no transparency involved and they occupy all available space or have the same size:

div{   
     background-image: url('http://placehold.it/1000x1000'), url('http://placehold.it/500x500');
     background-repeat:no-repeat;
     background-size: 100%;
     height:200px;
     width:200px;
}
<div></div>

If the first doesn't exit, the second will be displayed.

div{   
     background-image: url('http://placehol/1000x1000'), url('http://placehold.it/500x500');
     background-repeat:no-repeat;
     background-size: 100%;
     height:200px;
     width:200px;
}
<div></div>

Sign up to request clarification or add additional context in comments.

11 Comments

The images are stacked, if the top one has transparency will see the bottom image through
What is the browser compatibility?
@CrystalMiller You can see here: caniuse.com/#feat=multibackgrounds
Unfortunately, on chrome - network tab - I can see it loads both images from the server. Even when the first is successful.
Browser loads both images. Would be nice if the second gets only loaded if the first one failed. Afaik, not possible with this solution.
|
21

To specify multiple possible backgrounds, you could do:

  background-color: green;
  background-image: url('bg.png'), url('bg.jpg');

This will set the background to bg.png if it exists. If it doesn't exist, it will be set to bg.jpg. If none of those images exist, the background will be set to the static green color.

Note that it will prioritize whatever image is specified first. So if both images exist, it will display the bg.png on top of the bg.jpg. If the png has any transparency, both images will be partially visible.

Check out the demo here. Test it by breaking any of the image urls'.

11 Comments

its not the solution that I need but I just up-voted for the alternative solution.
@djsony90 Oh, so the image will always exist, but it will either be a jpg OR png?
As @blonfu mentioned in a different answer, both images are loaded and stacked. It does not just load the first successful file.
@J.Hendrix right, but there is no other way to solve that without JavaScript. OP asked for a "non-javascript way" and this is probably as good as it gets with css-only. Unless I'm missing something (if so please specify what), I think the down-vote isn't quite justified since there is no other way, really, to solve this.
This actually really worked well for me and turned out to be exactly what I was looking for. Thank you so much!
|
4

Although this solution does involve JS, it will download the first image using CSS, and will only use JS to download the fallback image if the main image failed to download.

First, set the main image with CSS as usual, for example:

.myImage {
    background-image = "main-image.png";
}

And add the following JS that will only act if loading the main image fails (otherwise, fallback image will never be downloaded).

var img = document.createElement("img");
img.onerror = function() {
    var style = document.createElement('style');
    style.innerHTML = '.myImage { background-image = url("http://fallback_image.png"); }';
    document.head.appendChild(style);
}
img.src = "main_image.png";

Note that you can add multiple rules to the CSS block added with javascript, for example if you need to do this with multiple elements.

Comments

1

You can have background image with css and use the same image URL in a hidden image tag and onError, change the image URL and make it show it

<div
  style={{ backgroundImage: `url(${imageSrc})` }}
>
  <img
    style={{ display: "none" }}
    src={imageSrc}
    onError={(e) => {
      e.currentTarget.src = brokenImageFallbackUrl;
      e.currentTarget.style.display = "block";
      // or keep this image hidden and set the backgroundImage of the parent div
    }}
  />
</div>

Comments

0

I wanted to have a solution that doesn't load the images twice. For example CDN with a fallback is not very good if it always loads the original images also. So I ended up writing a Javascript to manipulate the loaded CSS DOM.

var cdn = "https://mycdn.net/"; // Original location or thing to find
var localImageToCssPath = "../"; // Replacement, Css are in /css/ folder.

function replaceStyleRule(allRules){
  var rulesCount = allRules.length;

  for (var i=0; i < rulesCount; i++) 
  {
    if(allRules[i].style !== undefined && allRules[i].style !== null &&
        allRules[i].style.backgroundImage !== undefined &&
        allRules[i].style.backgroundImage !== null &&
        allRules[i].style.backgroundImage !== "" &&
        allRules[i].style.backgroundImage !== "none" &&
        allRules[i].style.backgroundImage.indexOf(cdn) > -1
            )
      {
        var tmp = allRules[i].style.backgroundImage.replace(cdn, localImageToCssPath);
        //console.log(tmp);
        allRules[i].style.backgroundImage = tmp;
      }
      if(allRules[i].cssRules !== undefined && allRules[i].cssRules !== null){
        replaceStyleRule(allRules[i].cssRules);
      }

  }
}
function fixBgImages(){
  var allSheets = document.styleSheets;
  if(allSheets===undefined || allSheets===null){
    return;
  }
  var sheetsCount = allSheets.length;
  for (var j=0; j < sheetsCount; j++) 
  {
    var allRules = null;
    try{
        allRules = allSheets[j].cssRules;
    } catch(e){
        // Access can be denied
    }
    if(allRules!==undefined && allRules!==null){
        replaceStyleRule(allRules);
    }
  }
}

// use fixBgImages() in e.g. onError

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.