从缓存的数组中检索预加载的图像(Retrieve preloaded image from cached array)

编程入门 行业动态 更新时间:2024-10-25 20:28:47
缓存的数组中检索预加载的图像(Retrieve preloaded image from cached array)

我正在使用JavaScript进行多个图像预加载,并且我为每个图像预分配了一些样式属性,这是可能的,因为这些是HTML图像元素。 我使用Promise技术,我在StackOverflow上找到了这个技术:

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

感谢您的帮助。

I'm using multiple image preloading with JavaScript, and I'm assigning some style attributes to each of them, which is possible since these are HTML Images elements. I use the Promise technique, that I found here on StackOverflow:

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); }

An example of the value of "srcs":

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

The preloading part works perfectly, and I'm able to get all of the preloaded images at the end of the process, seeing that the style attributes have been applied, as following:

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.'); });

However, later in my code, I would like to take some of the elements from the array of preloaded images, so I can append them without having to precise the style attributes.

By that, I mean something more than only .append('aSrcThatHasBeenPreloaded'); . I want to keep the class and style attributes I defined during the preload.

Is that even possible? And if so, how should I proceed?

I tried document.getElementsByClassName('myPreloadedClass') but it returns an empty result, which means the preloaded images are not part of the DOM (which makes perfect sense).

Thank you for your help.

最满意答案

你将不得不确保你可以以某种方式检索你预装的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,可以将它们添加到文档片段中 。 这样,网站的访问者将无法轻松访问它们,您仍然可以使用查询选择器来检索图像。

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类,您可以在其中放置预加载图像和检索它们的所有功能。

下面的代码片段中的闭包返回两种方法:

preloadImages ,它将一组图像预加载。 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> 
  
 

You're going to have to make sure you can somehow retrieve the img elements you've preloaded. There are many ways to do this but three that come to mind straight away.

1. Added loaded image in the DOM

In your example you're trying to retrieve the images with a query selector. This fails because the img elements aren't added to the DOM. You could attach them to a DOM element that is hidden from the user.

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. Added loaded image to a document fragment

If you don't want to add the img to the DOM you could add them to a document fragment. This way a visitor of the website won't be able to easily access them and you can still use a query selector to retrieve the image.

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> 
  
 

The downside

This second solution is very similar to the first one. You can still use a query selector to access the images. However both solutions have the same downside, once you place an image from the cache anywhere else on the site you can no longer retrieve the image from the cache. This is because an element can be in the DOM only once and appending it to another element will remove it from its previous parent element.

3. A reusable bit of code

I would recommend creating either a closure or an ES6 class where you can put all the functionality for preloading the images and retrieving them.

The closure from the snippet below returns two methods:

preloadImages which takes an array of images to preload. getImage which takes a valid CSS selector which is used to retrieve the needed img element from the document fragment. Before returning the element it will clone it so you can add the same preloaded image multiple times to the DOM.

Cloning an element will also clone its ID, something to be mindful of. I think it is not much of an issue here since the img is created in code so you have full control over it.

I used a document fragment to store the preloaded images because I think it is a more elegant solution than adding the images to the DOM. It makes it a lot more difficult to get the img element without using the getImage method and thus prevents that the preloaded img element is somehow removed from the image cache.

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> 
  
 

更多推荐

本文发布于:2023-04-29 12:01:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1336412.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:缓存   组中   图像   加载   Retrieve

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!