【问题标题】:Retrieve preloaded image from cached array从缓存数组中检索预加载的图像
【发布时间】:2017-09-27 14:38:00
【问题描述】:

我正在使用 JavaScript 预加载多个图像,并为每个图像分配一些样式属性,这是可能的,因为这些是 HTML 图像元素。我使用了 Promise 技术,我在 StackOverflow 上找到了 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'); 。 我想保留我在预加载期间定义的类和样式属性。

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

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

感谢您的帮助。

【问题讨论】:

  • 你能举一个srcs的值的例子吗?
  • 我编辑添加了一个简单的例子。但是,我很确定这部分是正确的,因为在该过程结束时,我想要的每张图像都已加载并且我返回的承诺是正确的。我的问题更多的是关于预加载在浏览器中的工作方式以及以后检索预加载数据的方法。
  • 要使用getElementsByClassName(),图像必须附加到DOM。如果不这样做,您总是可以遍历数组并直接检查 imgs[n].className(或使用 imgs[n].classList.contains(classname))。您当然会将 img 存储在可以再次访问它们的地方。
  • 嗨,我无法访问代码部分中的数组,但也许这是要走的路,通过我稍后将使用的变量访问它。谢谢,我会试试这个想法。
  • @Driblou 是的,您通常会将对数组的引用存储在全局范围内。

标签: javascript html image preloading


【解决方案1】:

您将必须确保您能够以某种方式检索您预加载的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 类,您可以在其中放置用于预加载和检索图像的所有功能。

下面 sn-p 的闭包返回两个方法:

  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>

【讨论】:

    猜你喜欢
    • 2011-11-23
    • 2014-01-07
    • 2013-08-31
    • 2016-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-27
    • 2016-07-27
    相关资源
    最近更新 更多