如何从 URL 数组向文档添加图像列表?

IT技术 javascript html
2021-03-13 12:52:23

假设我有一个充满图像源 URL 的数组,例如:

var imgs = ['http://lorempizza.com/380/240', 
            'http://dummyimage.com/250/ffffff/000000', 
            'http://lorempixel.com/g/400/200/', 
            'http://lorempixel.com/g/400/200/sports/'];

如何获取所有这些图像并将它们插入到我页面的特定位置?说...

<div id="imageContainer"></div>
2个回答

一种方法是循环遍历图像数组,img为每个图像创建一个元素,将元素的 src 设置为数组中的当前图像源,然后将其附加到容器中。这看起来像:

var imgs = ['http://lorempizza.com/380/240', 
            'http://dummyimage.com/250/ffffff/000000', 
            'http://lorempixel.com/g/400/200/', 
            'http://lorempixel.com/g/400/200/sports/'];
var container = document.getElementById('imageContainer');

for (var i = 0, j = imgs.length; i < j; i++) {
    var img = document.createElement('img');
    img.src = imgs[i]; // img[i] refers to the current URL.
    container.appendChild(img);
}

但是,这并不是特别有效:

  • 我们使用了一个不必要的循环
  • 我们在每次迭代中查询 dom
  • 我们正在引入全局ij变量
  • 我们没有使用 JavaScript 美妙的 Array 方法!

相反,让我们使用documentFragment和 JavaScript 的forEach Array 方法。

var imgs = ['http://lorempizza.com/380/240', 
            'http://dummyimage.com/250/ffffff/000000', 
            'http://lorempixel.com/g/400/200/', 
            'http://lorempixel.com/g/400/200/sports/'];
var container = document.getElementById('imageContainer');
var docFrag = document.createDocumentFragment();

imgs.forEach(function(url, index, originalArray) {
    var img = document.createElement('img');
    img.src = url;
    docFrag.appendChild(img);
});


container.appendChild(docFrag);

这样我们:

  • 只访问 DOM 一次
  • 不引入全局变量
  • 维护更干净,更易于阅读的代码!

然后只是为了确保您的新图像看起来不错,在混合中添加一些 CSS:

#imageContainer img {
    width: 20%;
    height: auto;
}

砰!这是一把小提琴

好答案!+1 我真的很喜欢你的 DocumentFragment 实现。我想指出的Array.prototype.forEach()是,在 IE8 上不可用。如果有人被迫支持那个古老的浏览器,请记住:-)
2021-04-22 12:52:23
我喜欢假装IE不存在
2021-05-09 12:52:23
@HowardRenollet 好点。有时我只是想假装IE8及以下不存在。
2021-05-18 12:52:23

由于提供了另一个赏金(我必须承认,我不完全同意另一个答案中的一些论点,但它确实使用了花哨的甚至可以说有些“沉重”的现代方法),我假设 Neal 有一个考虑到更明显的答案,我将尝试一下。

想到的最直接的事情(假设 Q 没有对图像的标记/属性/等设置任何要求)是:Array.prototype。join ([separator = ','])方法将数组的所有元素连接成一个字符串,由作为arguments[0](aka separator传递的任何内容分隔,该字符串被强制转换为字符串(如果需要)默认为字符串 ' ,'(逗号) ) 省略时。
joining 通常是/曾经是一种非常流行的连接字符串的方式,因为它曾经在较旧和/或不太“流行”/“优化”的浏览器上更快。在后来更流行和更好优化的浏览器上,字符串连接使用+目前似乎更快(这实际上是有道理的)。然而,当编写旨在向后兼容的代码时,选择/使用最慢的公分母通常是有意义的(因为速度怪物已经被优化)。例如,在 SO 上查看这个这个问题的答案(和链接的 jsperfs)

现在,我们有一个字符串,我们可以使用它连同element.innerHTML往往更快比访问DOM来createElement的图像,并将其添加到documentFragment了布局移动这些元素所需的父元素之前的DOM一遍又一遍。 .. 不要误会我的意思,当我们需要做大量的 DOM 工作时,documentFragment 是一个很酷而且很有用的东西,不会在每次操作时都进行布局重绘

var imgs = ['http://lorempizza.com/380/240', 
            'http://dummyimage.com/250/ffffff/000000', 
            'http://lorempixel.com/g/400/200/', 
            'http://lorempixel.com/g/400/200/sports/'];

document.getElementById('imageContainer')
        .innerHTML = '<img src="' + imgs.join('" /><img src="') + '" />';
<div id="imageContainer"></div>

这有一个小问题:如果我们的img数组是空的,我们就会得到一个“空”的图像没有什么是简单的if不能处理困难的..由于array.length是基于 1 的,我们甚至可以让 javascript 强制转换为0to的长度false
此外,我们可以将其更改/膨胀为匿名(未命名)立即调用的函数表达式(IIFE):

(function(container, arr){
  if(arr.length) container.innerHTML = '<img src="' + arr.join('" /><img src="') + '" />';
}( document.getElementById('imageContainer')
 , imgs
));

甚至接受围绕 url 的尾随和前导 HTML 代码的参数:

(function(container, arr, leadStr, trailStr){
  if(arr.length) container.innerHTML = leadStr + arr.join(trailStr+leadStr) + trailStr;
}( document.getElementById('imageContainer')
 , imgs
 , '<img src="'
 , '" />'
));

等等等等等等...

根据实际(浏览器的)引擎实现(和优化),这个“加入”(像所有其他解决方案一样)很可能在后台仍然有一个循环..

因为forEach通常被视为一种有点慢的方法(我们都同意在后台仍然有一个循环)它往往(一如既往,取决于引擎)做比可能需要的更多的工作,它会回调一个函数(传递围绕数组中的每个元素的一些(3)个参数),我将在这里展示一个循环版本,使用字符串连接(为了这个答案的多样性和“现代”“更快”字符串连接):

(function(container, arr, leadStr, trailStr){
  var i=0, L=arr.length, r='';
  if(L){
    for(; i<L ; ++i){
      r += leadStr + arr[i] + trailStr;
    }
  container.innerHTML = r;
  }
}( document.getElementById('imageContainer')
 , imgs
 , '<img src="'
 , '" />'
));

所有这些都应该适用于任何使用 HTML4 和 HTML5 的浏览器。另请注意,上述内容将覆盖“容器”的内容(不添加)。

然而,DOM 方法在技术上是 X(H)ML 所必需的,因为元素没有innerHTML正式属性(尽管很多浏览器不介意),但我们仍然可以使用一个简单的(非隐藏)循环,而不会向全局范围泄漏任何内容,只需修改上述函数即可:

(function(container, arr){
  var i = 0, L = arr.length, docFrag = document.createDocumentFragment();
  if(L){
    for(; i<L; ++i){
      docFrag.appendChild(document.createElement('img')).src = arr[i];
    }
    container.appendChild(docFrag);
  }
}( document.getElementById('imageContainer')
 , imgs
));

请注意,此示例添加到“容器”中的内容。

这些示例旨在概述一些事情:innerHTML, join,无需引入全局/泄漏变量,最重要的是:没有什么可以“保存”“不必要的”循环(实际上,取决于您在做什么,可见循环实际上可能更快)。

然而,人们可能只是命名包装函数(上面示例中的 IIFE)并调用它们,传递您所需的参数。这样你就可以动态地添加或更新/覆盖你的图像区域(比如当你通过 ajax 加载不同的图像 url-array 时等等)不止一次。
对于这最后一个示例,我cloneNode以一种“聪明”的方式使用(这将很好地进行 g-zip)以最小化一些查找(同时不会给我们留下剩余但未放置的图像节点):

var imgs = ['http://lorempizza.com/380/240', 
            'http://dummyimage.com/250/ffffff/000000', 
            'http://lorempixel.com/g/400/200/', 
            'http://lorempixel.com/g/400/200/sports/'];

function addPics(container, arr){
  var i = 0, L = arr.length, docFrag = document.createDocumentFragment(), img;
  if(L){
    for( docFrag.appendChild(img=document.createElement('img')).src = arr[i]
       ; ++i<L
       ; docFrag.appendChild(img.cloneNode(false)).src = arr[i]
       );
    container.appendChild(docFrag);
  }
}

addPics( document.getElementById('imageContainer'), imgs );
<div id="imageContainer"></div>

希望这些例子之间的解释是尼尔想到的。
无论如何,现在应该有足够的参考来解决这个问题和其他答案。

嗯,想通了.. ( 1 , 2 )。至少我不是唯一一个不明白这一点的人(..'一个或多个答案是模范的,值得额外的赏金'并不意味着'每个人都可以有机会获得它(赏金)有一个很好的答案').. NP,我仍然支持我的答案的本质。
2021-04-22 12:52:23
呵呵 .. 几个小时前我开始输入这个答案时,其他悬赏(由@Neal 提供)去哪儿了,开放了 5 天?编辑:不介意我没有得到它,只是想知道 5 天的...
2021-04-24 12:52:23