如何检查背景图片是否已加载?

191
我想在body标签上设置一个背景图片,然后运行一些代码 - 就像这样:
$('body').css('background-image','http://picture.de/image.png').load(function() {
    alert('Background image done loading');
    // This doesn't work
});

如何确保背景图片完全加载?


4
将具有onload事件的Image()对象分配相同的URL。 - Shadow The Spring Wizard
2
请将 CSS URL 包裹在 url() 中。 - bluescrubbie
Imagesloaded可以完成这样的任务 https://imagesloaded.desandro.com/ - Orthemes
10个回答

315

试试这个:

$('<img/>').attr('src', 'http://picture.de/image.png').on('load', function() {
   $(this).remove(); // prevent memory leaks as @benweet suggested
   $('body').css('background-image', 'url(http://picture.de/image.png)');
});

这将在内存中创建一个新图像,并使用load事件检测src何时被加载。

编辑:在纯JavaScript中,可以像这样实现:

var src = 'http://picture.de/image.png';
var image = new Image();
image.addEventListener('load', function() {
   body.style.backgroundImage = 'url(' + src + ')';
});
image.src = src;

可以将它抽象为一个方便的函数,返回一个 Promise:

function load(src) {
    return new Promise((resolve, reject) => {
        const image = new Image();
        image.addEventListener('load', resolve);
        image.addEventListener('error', reject);
        image.src = src;
    });
}

const image = 'http://placekitten.com/200/300';
load(image).then(() => {
   body.style.backgroundImage = `url(${image})`;
});

13
似乎没有必要加载两次图片。 - HyderA
54
它们只会被加载一次,因为它们会保留在浏览器内存中。这就像把隐藏的图片放在页面顶部并在 CSS 中设置——这样图片就可以在 CSS 文件之前开始下载——这被称为预加载。 - jcubic
6
我不太确定,但我认为你指的是缓存。我不认为有所谓的浏览器内存来存储资源。如果你指的是缓存,请记住这意味着添加额外的HTTP请求,并且并非所有客户端都启用了缓存。 - HyderA
7
在任何有 jQuery 的页面中输入以下内容:javascript:void($('<img/>').attr('src', 'http://farm6.static.flickr.com/5049/5220175127_5693faf952.jpg').load(function() { $('html').css('background-image', 'url(http://farm6.static.flickr.com/5049/5220175127_5693faf952.jpg)'); })),然后在 Firebug 中检查 HTTP 请求。如果我在另一个标签页中打开了带有此图片的 Flickr 页面,则不会进行任何 HTTP 请求,只会立即显示这张图片。当我清除缓存并重新运行时,会发出一次请求。 - jcubic
28
对比这两个测试 this testthis one,可以发现你需要在 $('<img/>') 对象上调用 .remove() 来避免内存泄漏。第一个测试应该会看到稳定的内存消耗,而第二个测试则会看到内存增加。在 Chrome 28 上运行几个小时后,第一个测试占用 80MB,第二个测试占用 270MB。 - benweet
显示剩余11条评论

23

我有一个名为waitForImagesjQuery插件,它可以检测背景图片何时下载完成。

$('body')
  .css('background-image','url(http://picture.de/image.png)')
  .waitForImages(function() {
    alert('Background image done loading');
    // This *does* work
  }, $.noop, true);

这个插件还可以通过each回调来展示加载进度,非常棒。 - Soviut
1
@alex,我该怎么为类中的每个元素检查其背景图像?我尝试了这个方法,但似乎并不正确。 $('.user_main_photo_thumb').waitForImages(function() { $(this).parents('.photoThumbFrame').delay(1000).slideDown(); }, function(loaded, count, success) { }); - Relm
@Relm 你没有正确使用它。第二个回调是针对每个图像的。另外,delay()不是这样工作的。 - alex

23

使用纯JS编写一个预加载器,并设置背景图片,然后将其与事件侦听器一起进行垃圾回收的解决方案:

简短版:

const imageUrl = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png";
let bgElement = document.querySelector("body");
let preloaderImg = document.createElement("img");
preloaderImg.src = imageUrl;

preloaderImg.addEventListener('load', (event) => {
    bgElement.style.backgroundImage = `url(${imageUrl})`;
    preloaderImg = null;
});

带有不错的透明度过渡的略长一些:

const imageUrl = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png";
let bgElement = document.querySelector(".bg-lazy");
bgElement.classList.add("bg-loading");
let preloaderImg = document.createElement("img");
preloaderImg.src = imageUrl;

preloaderImg.addEventListener('load', (event) => {
  bgElement.classList.remove("bg-loading");
  bgElement.style.backgroundImage = `url(${imageUrl})`;
  preloaderImg = null;
});
.bg-lazy {
  height: 100vh;
  width: 100vw;
  transition: opacity 1s ease-out;
}

.bg-loading {
  opacity: 0;
}
<div class="bg-lazy"></div>


7
被低估的解决方案。 - Tom Thomson
1
@TomThomson 谢谢Tom,我发现图片转换没有正常工作,已经修复了。 - godblessstrawberry
1
这是一个非常好的答案。 - bob
有趣的解决方案。您认为使用<link>标签进行预加载如何:<link rel="preload" href=cat.jpg as=image imagesrcset="cat.jpg 1x, cat-2x.jpg 2x"> - bytrangle
@bytrangle 这将增加加载优先级,这很少需要。 - godblessstrawberry

19

对于 CSS 资源,没有 JS 回调函数可以使用。


3
谢谢快速回答。有没有解决方法的想法? - Peter
1
但是对于那些正在寻找的人,有JS解决方案。https://dev59.com/Km445IYBdhLWcg3wDWKc#54462733 - godblessstrawberry

16

像这样:

var $div = $('div'),
  bg = $div.css('background-image');
  if (bg) {
    var src = bg.replace(/(^url\()|(\)$|[\"\'])/g, ''),
      $img = $('<img>').attr('src', src).on('load', function() {
        // do something, maybe:
        $div.fadeIn();
      });
  }
});

你好...这似乎有效,尽管我将bg.replace设置为bg.replace( ... , my_src )。但我的问题是:一旦$('<img>')加载完成,那个<img>会发生什么...我的意思是它实际上并不真实 - 它只是一个虚拟占位符,对吗? - dsdsdsdsd
没错。<img> 标签从未被插入到 DOM 中;它只是用来“欺骗”浏览器将图像文件加载到缓存中。 - Colin
如果您不想调用jQuery,以下内容可能会有所帮助。 - vwvan

7
我找到了一个对我更有效的解决方案,而且可以用于多个图像(本示例未说明这种情况)。
来自@adeneo在this question上的答案:

If you have an element with a background image, like this

<div id="test" style="background-image: url(link/to/image.png)"><div>

You can wait for the background to load by getting the image URL and using it for an image object in javascript with an onload handler

var src = $('#test').css('background-image');
var url = src.match(/\((.*?)\)/)[1].replace(/('|")/g,'');

var img = new Image();
img.onload = function() {
    alert('image loaded');
}
img.src = url;
if (img.complete) img.onload();

6

这是我制作的一个小插件,可以让您实现这一点,它还适用于多个背景图像和多个元素:

阅读文章:

http://catmull.uk/code-lab/background-image-loaded/

或者直接转到插件代码:

http://catmull.uk/downloads/bg-loaded/bg-loaded.js

只需要引入插件,然后在元素上调用它:

<script type="text/javascript" src="http://catmull.uk/downloads/bg-loaded/bg-loaded.js"></script>
<script type="text/javascript">
   $('body').bgLoaded();
</script>

显然下载插件并将其存储在您自己的主机上。

默认情况下,它会在背景加载完成后向每个匹配的元素添加一个额外的“bg-loaded”类,但您可以通过传递不同的函数来轻松更改它,例如:

<script type="text/javascript" src="http://catmull.uk/downloads/bg-loaded/bg-loaded.js"></script>
<script type="text/javascript">
   $('body').bgLoaded({
      afterLoaded : function() {
         alert('Background image done loading');
      }
   });
</script>

这里有一个 CodePen,演示了它的工作原理。

http://codepen.io/catmull/pen/Lfcpb


3
我使用纯JavaScript技术进行了黑客攻击,以实现此功能。
<div class="my_background_image" style="background-image: url(broken-image.jpg)">
<img class="image_error" src="broken-image.jpg" onerror="this.parentElement.style.display='none';">
</div>

或者

onerror="this.parentElement.backgroundImage = "url('image_placeHolder.png')";

CSS:

.image_error {
    display: none;
}

3
这里是一个简单的纯净版hack ~
(function(image){
  image.onload = function(){ 
                   $(body).addClass('loaded-background');
                   alert('Background image done loading');
                   // TODO fancy fade-in
                 };
  image.src    = "http://picture.de/image.png";
})(new Image());

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接