从缓存数组中检索预加载的图像

Retrieve preloaded image from cached array

我在 JavaScript 中使用多个图像预加载,我正在为每个图像分配一些样式属性,这是可能的,因为这些是 HTML 个图像元素。我使用 Promise 技术,我在 Whosebug 上发现 here

function preloadImages(srcs) {
  function loadImage(imgToLoad) {
    return new Promise(function(resolve, reject) {
        var img = new Image();
        img.onload = function() {
            resolve(img);
        };
        img.onerror = img.onabort = function() {
            reject(imgToLoad.src);
        };
        img.className = 'testimg'; //Test to see if I can retrieve img
        img.src = imgToLoad.src;
        img.style.top = imgToLoad.top;
    });
}
var promises = [];
for (var i = 0; i < srcs.length; i++) {
    promises.push(loadImage(srcs[i]));
}
return Promise.all(promises);
}

"srcs"的取值示例:

srcs[0].top = '15%';
srcs[0].src = 'img/myImage1.jpg';
srcs[1].top = '24%';
srcs[1].src = 'img/myImage2.jpg';

预加载部分工作完美,我能够在过程结束时获取所有预加载图像,看到已应用样式属性,如下所示:

preloadImages(imgToLoad).then(function(imgs) {
    // all images are loaded now and in the array imgs
    console.log(imgs); // This works
    console.log(document.getElementsByClassName('testimg')); //This doesn't
}, function(errImg) {
    // at least one image failed to load
    console.log('error in loading images.');
});

但是,在我的代码后面,我想从预加载图像数组中获取一些元素,这样我就可以附加它们而不必精确设置样式属性。

我的意思不仅仅是 .append('aSrcThatHasBeenPreloaded');我想保留我在预加载期间定义的 class 和样式属性。

这可能吗?如果是这样,我应该如何进行?

我试过 document.getElementsByClassName('myPreloadedClass') 但它 returns 是一个空结果,这意味着预加载的图像不是 DOM 的一部分(这很有意义)。

感谢您的帮助。

您将必须确保能够以某种方式检索您预加载的 img 元素。有很多方法可以做到这一点,但我马上想到了三种。

1。在 DOM

中添加加载图像

在您的示例中,您尝试使用查询选择器检索图像。这失败了,因为 img 元素没有添加到 DOM。您可以将它们附加到对用户隐藏的 DOM 元素。

var 
  images = [
    {
      src: '//placehold.it/550x250?text=A', 
      top: 0, 
      class:'img-a' // unique class name to retrieve item using a query selector.
    }, 
    {
      src: '//placehold.it/550x250?text=B', 
      top: 0, 
      class:'img-b'
    }],
  imageCache = document.getElementById('image-cache');
  
function preloadImages(srcs) {
  function loadImage(imgToLoad) {
    return new Promise(function(resolve, reject) {
        var img = new Image();
        img.onload = function() {
            // The image has been loaded, add it to the cache element.
            imageCache.appendChild(img);
            resolve(img);
        };
        img.onerror = img.onabort = function() {
            reject(imgToLoad.src);
        };
        img.className = 'testimg ' + imgToLoad.class; //Test to see if I can retrieve img
        img.src = imgToLoad.src;
        img.style.top = imgToLoad.top;
    });
  }
  
  var promises = [];
  for (var i = 0; i < srcs.length; i++) {
      promises.push(loadImage(srcs[i]));
  }
  return Promise.all(promises);
}
  
// Load all the images.
preloadImages(images)
  .then(result => {
    // Add img B from the cache to the proof div.
    document.getElementById('proof').appendChild(imageCache.querySelector('.img-b'));
  });
.hidden { 
  display: none;
}
<div id="image-cache" class="hidden"></div>
<div id="proof"></div>

2。将加载的图像添加到文档片段

如果您不想将 img 添加到 DOM,您可以将它们添加到 document fragment。这样网站的访问者将无法轻松访问它们,您仍然可以使用查询选择器来检索图像。

var 
  images = [
    {
      src: '//placehold.it/550x250?text=A', 
      top: 0, 
      class:'img-a' // unique class name to retrieve item using a query selector.
    }, 
    {
      src: '//placehold.it/550x250?text=B', 
      top: 0, 
      class:'img-b'
    }],
  // Create a document fragment to keep the images in.
  imageCache = document.createDocumentFragment();;
  
function preloadImages(srcs) {
  function loadImage(imgToLoad) {
    return new Promise(function(resolve, reject) {
        var img = new Image();
        img.onload = function() {
            // The image has been loaded, add it to the cache element.
            imageCache.appendChild(img);
            resolve(img);
        };
        img.onerror = img.onabort = function() {
            reject(imgToLoad.src);
        };
        img.className = 'testimg ' + imgToLoad.class; //Test to see if I can retrieve img
        img.src = imgToLoad.src;
        img.style.top = imgToLoad.top;
    });
  }
  
  var promises = [];
  for (var i = 0; i < srcs.length; i++) {
      promises.push(loadImage(srcs[i]));
  }
  return Promise.all(promises);
}
  
// Load all the images.
preloadImages(images)
  .then(result => {
    // Add img B from the cache to the proof div.
    document.getElementById('proof').appendChild(imageCache.querySelector('.img-b')); 
    // And try to add img B from the cache to the proof2 div. The image will only be visible once in the site.
    document.getElementById('proof2').appendChild(imageCache.querySelector('.img-b'));
  });
.hidden { 
  display: none;
}
<div id="proof"></div>
<div id="proof2" style="margin-top:1em"></div>

缺点

第二个解决方案与第一个非常相似。您仍然可以使用查询选择器来访问图像。然而,这两种解决方案都有相同的缺点,一旦您将缓存中的图像放置在站点的其他任何位置,您就无法再从缓存中检索图像。这是因为一个元素只能在 DOM 中出现一次,将其附加到另一个元素会将其从其先前的父元素中删除。

3。一段可重用的代码

我建议创建闭包或 ES6 class,您可以在其中放置预加载图像和检索图像的所有功能。

来自下面代码片段的闭包 returns 两种方法:

  1. preloadImages 需要一组图像进行预加载。
  2. getImage 它采用有效的 CSS 选择器,用于从文档片段中检索所需的 img 元素。在返回元素之前,它将克隆它,这样您就可以多次将相同的预加载图像添加到 DOM.

克隆一个元素也会克隆它的ID,这点需要注意。我认为这不是什么大问题,因为 img 是在代码中创建的,因此您可以完全控制它。

我使用文档片段来存储预加载的图像,因为我认为这是比将图像添加到 DOM 更优雅的解决方案。这使得在不使用 getImage 方法的情况下获取 img 元素变得更加困难,从而防止预加载的 img 元素以某种方式从图像缓存中删除。

var
  images = [
    {
      src: '//placehold.it/550x250?text=A', 
      top: 0, 
      class:'img-a' // unique class name to retrieve item using a query selector.
    }, 
    {
      src: '//placehold.it/550x250?text=B', 
      top: 0, 
      class:'img-b'
    }];
        
var imagesPreloader = (function() {
  var imagesCache = document.createDocumentFragment();
  
  function loadImage(imgToLoad) {
    return new Promise(function(resolve, reject) {
        var img = new Image();
        img.onload = function() {
          imagesCache.appendChild(img)
          resolve();
        };
        img.onerror = img.onabort = function() {
            reject(imgToLoad.src);
        };
        img.className = 'testimg ' + imgToLoad.class ; //Test to see if I can retrieve img
        img.src = imgToLoad.src;
        img.style.top = imgToLoad.top;
    });
  }
  
  function preloadImages(srcs) {
    var promises = [];

    for (var i = 0; i < srcs.length; i++) {
        promises.push(loadImage(srcs[i]));
    }

    return Promise.all(promises);  
  }

  function getImage(imageSelector) {
    const
      // Find the image in the fragment
      imgElement = imagesCache.querySelector(imageSelector);
      
    // When the selector didn't yield an image, return null.
    if (imgElement === null) {
      return null;
    }
    
    // Clone the image element and return it.
    const
      resultElement = imgElement.cloneNode();
    return resultElement;
  }

  return {
    getImage,
    preloadImages
  };
})();


imagesPreloader.preloadImages(images)
  .then(result => {
    console.log('Image with class "img-a": ', imagesPreloader.getImage('.img-a'));
    document.querySelector('#proof').appendChild(imagesPreloader.getImage('.img-b'));
    document.querySelector('#proof2').appendChild(imagesPreloader.getImage('.img-b'));
  });
<div id="proof"></div>
<div id="proof2" style="margin-top:1em"></div>