当图片加载完成时,我该如何运行一个JavaScript回调函数?

198

我想知道图像何时加载完成。是否有办法通过回调来实现?

如果没有,总体上有办法吗?


2
大家好,看看这个包:https://github.com/desandro/imagesloaded。 - mangatinanda
12个回答

222

.complete + callback

这是一个符合标准且不需要额外依赖的方法,等待时间不会超过必要的时间:

var img = document.querySelector('img')

function loaded() {
  alert('loaded')
}

if (img.complete) {
  loaded()
} else {
  img.addEventListener('load', loaded)
  img.addEventListener('error', function() {
      alert('error')
  })
}

来源: http://www.html5rocks.com/en/tutorials/es6/promises/


4
如果在img.complete调用和addEventListener调用之间完成了图像,这会出现问题吗? - Thomas Ahle
@ThomasAhle 我不是专家,但那似乎是可能的。让我们看看是否有解决方案。 - Ciro Santilli OurBigBook.com
6
@ThomasAhle它不能,因为浏览器只会在事件队列被旋转时才触发load事件。也就是说,load事件监听器必须在一个else语句中,因为在load事件被触发之前,img.complete可能已经变为true,如果没有else语句,你可能会有可能调用两次loaded(请注意,相对来说img.complete变为true的时机可能会发生改变)。 - gsnedders
“load”事件在哪里指定?我在MDN上好像没有找到它? - user1663023
这里有些不对劲,如果 complete 是 false,那么我的事件监听器就永远不会被调用。Src 属性是有效的。 - jjxtra
显示剩余2条评论

91

Image.onload()通常有效。要使用它,您需要确保在设置src属性之前绑定事件处理程序。

相关链接:

示例用法:

    window.onload = function () {

        var logo = document.getElementById('sologo');

        logo.onload = function () {
            alert ("The image has loaded!");  
        };

        setTimeout(function(){
            logo.src = 'https://edmullen.net/test/rc.jpg';         
        }, 5000);
    };
 <html>
    <head>
    <title>Image onload()</title>
    </head>
    <body>

    <img src="#" alt="This image is going to load" id="sologo"/>

    <script type="text/javascript">

    </script>
    </body>
    </html>


30
根据W3C规范,onload不是IMG元素的有效事件。显然,浏览器支持它,但如果你关心规范并且不能100%确定你想要针对的所有浏览器都支持它,那么你可能需要重新考虑它或至少记在心里。 - Jason Bunting
5
为什么等待5秒钟才设置来源似乎是个坏主意? - quemeful
10
这就是为什么它被称为“例子”。 - Diego Jancic
3
@JasonBunting,你看了什么让你觉得load事件无效?https://html.spec.whatwg.org/multipage/webappapis.html#handler-onload 是 onload 事件处理程序的定义。 - gsnedders
6
@gsnedders - 你应该明白,我在写那条评论时是指现行标准,而不是你提到的那个新近4.5年的标准。如果你当时问过我,我或许可以指出它来着。这就是网络的本质,对吧?事物会变旧、被迁移或修改等等。 - Jason Bunting
显示剩余8条评论

21
您可以使用Javascript image类的.complete属性。我有一个应用程序,在其中将多个Image对象存储在数组中,它们将动态添加到屏幕上,并且当它们正在加载时,我会将更新写入页面上的另一个div中。以下是代码片段:
var gAllImages = [];

function makeThumbDivs(thumbnailsBegin, thumbnailsEnd)
{
    gAllImages = [];

    for (var i = thumbnailsBegin; i < thumbnailsEnd; i++) 
    {
        var theImage = new Image();
        theImage.src = "thumbs/" + getFilename(globals.gAllPageGUIDs[i]);
        gAllImages.push(theImage);

        setTimeout('checkForAllImagesLoaded()', 5);
        window.status="Creating thumbnail "+(i+1)+" of " + thumbnailsEnd;

        // make a new div containing that image
        makeASingleThumbDiv(globals.gAllPageGUIDs[i]);
    }
}

function checkForAllImagesLoaded()
{
    for (var i = 0; i < gAllImages.length; i++) {
        if (!gAllImages[i].complete) {
            var percentage = i * 100.0 / (gAllImages.length);
            percentage = percentage.toFixed(0).toString() + ' %';

            userMessagesController.setMessage("loading... " + percentage);
            setTimeout('checkForAllImagesLoaded()', 20);
            return;
        }
    }

    userMessagesController.setMessage(globals.defaultTitle);
}

我之前在工作中使用过类似的代码。这在所有浏览器中都运行良好。 - Gabriel Hurley
2
检查完成的问题在于,从IE9开始,在所有图像加载完成之前就会触发OnLoad事件,现在很难找到检查函数的触发器。 - Gene Vincent

19

生命苦短,何必使用jquery。

function waitForImageToLoad(imageElement){
  return new Promise(resolve=>{imageElement.onload = resolve})
}

var myImage = document.getElementById('myImage');
var newImageSrc = "https://pmchollywoodlife.files.wordpress.com/2011/12/justin-bieber-bio-photo1.jpg?w=620"

myImage.src = newImageSrc;
waitForImageToLoad(myImage).then(()=>{
  // Image have loaded.
  console.log('Loaded lol')
});
<img id="myImage" src="">


4
这是一个很好的答案,但我认为你甚至不需要那么多代码。通过在JS中添加'src',你只会让它看起来更加混乱。 - Brandon Benefield
2
时间在编程中是一个巨大的限制。从我的经验来看,编写可读性高(有时可能比较冗长)的代码会带来巨大的时间效益。 - Idan Beker
2
为什么要将src存储到一个新变量中,只是为了在下一行使用它?如果简洁是你的目标,那这是一种反模式。myImage.src = https://pmchollywoodlife.files.wordpress.com/2011/12/justin-bieber-bio-photo1.jpg?w=620就可以完成任务。 - Josiah

12

您可以使用jQuery中的load()事件,但如果图像从浏览器缓存中加载,则它不会始终触发。 可以使用此插件解决此问题。


5
如果目标是在浏览器渲染图像后对其进行样式化,您应该执行以下操作:
const img = new Image();
img.src = 'path/to/img.jpg';

img.decode().then(() => {
/* set styles */
/* add img to DOM */ 
});

因为浏览器首先加载压缩版本的图像,然后解码,最后绘制。由于没有“paint”事件,因此您应在浏览器解码img标签后运行您的逻辑。

5

以下是jQuery的等效代码:

var $img = $('img');

if ($img.length > 0 && !$img.get(0).complete) {
   $img.on('load', triggerAction);
}

function triggerAction() {
   alert('img has been loaded');
}

4
不适用于2008年提出问题的情况,但是现在对我来说很有效:
async function newImageSrc(src) {
  // Get a reference to the image in whatever way suits.
  let image = document.getElementById('image-id');

  // Update the source.
  image.src = src;

  // Wait for it to load.
  await new Promise((resolve) => { image.onload = resolve; });

  // Done!
  console.log('image loaded! do something...');
}

我猜你的意思是 image.src = src; - Simbiat

0

这些函数将解决问题,您需要实现DrawThumbnails函数并有一个全局变量来存储图像。我希望能够使用具有ThumbnailImageArray作为成员变量的类对象使其工作,但是我遇到了困难!

调用方式如下:addThumbnailImages(10);

var ThumbnailImageArray = [];

function addThumbnailImages(MaxNumberOfImages)
{
    var imgs = [];

    for (var i=1; i<MaxNumberOfImages; i++)
    {
        imgs.push(i+".jpeg");
    }

    preloadimages(imgs).done(function (images){
            var c=0;

            for(var i=0; i<images.length; i++)
            {
                if(images[i].width >0) 
                {
                    if(c != i)
                        images[c] = images[i];
                    c++;
                }
            }

            images.length = c;

            DrawThumbnails();
        });
}



function preloadimages(arr)
{
    var loadedimages=0
    var postaction=function(){}
    var arr=(typeof arr!="object")? [arr] : arr

    function imageloadpost()
    {
        loadedimages++;
        if (loadedimages==arr.length)
        {
            postaction(ThumbnailImageArray); //call postaction and pass in newimages array as parameter
        }
    };

    for (var i=0; i<arr.length; i++)
    {
        ThumbnailImageArray[i]=new Image();
        ThumbnailImageArray[i].src=arr[i];
        ThumbnailImageArray[i].onload=function(){ imageloadpost();};
        ThumbnailImageArray[i].onerror=function(){ imageloadpost();};
    }
    //return blank object with done() method    
    //remember user defined callback functions to be called when images load
    return  { done:function(f){ postaction=f || postaction } };
}

12
大部分代码都是无关紧要的,包括你整个的 addThumbnailImages 函数。将其精简为相关的代码,我会去掉 -1。 - Justin Morgan

0

这对我起作用:

// Usage
let img = await image_on_load(parent_element_to_put_img, image_url);
img.hidden = true;

// Functions
async function image_on_load(parent, url) {
    let img = element(parent, 'img');
    img.src = url;
    await new Promise((resolve, reject) => {
        element_on(img, 'load', () => {
            resolve();
        });
        element_on(img, 'error', () => {
            reject();
        });
    });
    return img;
}
function element(parent, tag_name, text = '') {
    let result = document.createElement(tag_name);
    element_child_append(parent, result);
    element_html_inner(result, text);
    return result;
}
function element_child_append(parent, result) {
    parent.appendChild(result);
}
function element_html_inner(result, text) {
    result.innerHTML = text;
}
function element_on(e, event, on_event) {
    e.addEventListener(event, async () => {
        await on_event();
    });
}


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