从缓存的数组

时间:2017-09-27 14:38:01

标签: javascript html image preloading

我使用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);
}

&#34; srcs&#34;的价值示例:

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的一部分(这很有道理)。

感谢您的帮助。

1 个答案:

答案 0 :(得分: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类,您可以在其中放置预加载图像和检索图像的所有功能。

以下代码段中的闭包会返回两种方法:

  1. preloadImages,它会将一系列图像预加载。
  2. getImage,它采用有效的CSS选择器,用于从文档片段中检索所需的img元素。在返回元素之前,它将克隆它,以便您可以多次向DOM添加相同的预加载图像。
  3. 克隆一个元素也会克隆它的ID,这是值得注意的。我认为这不是一个问题,因为img是在代码中创建的,所以你可以完全控制它。

    我使用文档片段来存储预加载的图像,因为我认为它比将图像添加到DOM更优雅。这使得在不使用img方法的情况下获取getImage元素变得更加困难,从而防止预先加载的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>