Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a list of images to the document from an array of URLs?

Suppose I have an array full of image source URLs, like:

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/'];

How do I grab all of those images and insert them into my page at a particular location? Say...

<div id="imageContainer"></div>
like image 944
monners Avatar asked Apr 07 '15 09:04

monners


1 Answers

Since another bounty is offered (and I must admit that I don't fully agree with some of the arguments in the other answer, which does however use fancy and even arguably somewhat 'heavy' modern methods), I am assuming that Neal has a more obvious answer in mind, I'm going to have a shot at it.

The most straight-forward thing that comes to mind (given that the Q doesn't set any requirements to the image's markup/attributes/etc) is: the Array.prototype.join([separator = ',']) method which joins all elements of an array into a string, separated by anything that is passed as arguments[0] (aka separator) which is coerced to string (if needed) or defaults to the string ',' (comma) when omitted.
joining is/was often a very popular way to concatenate strings because it used to be faster on older and/or less 'popular'/'optimized' browsers. On the later more popular and better optimized browsers, string-concatenation using + currently appears to be faster (which actually makes sense). However, when writing code that is intended to be backwards compatible it often makes sense to choose/use the least slow common denominator (since the speed-monsters are already optimized). See answers (and linked jsperfs) to this and this question on SO for example.

Now that we have a string, we'd use it together with element.innerHTML which is often faster than accessing the DOM to createElement image and add it to the documentFragment's DOM over and over again before moving these elements over to the intended parent element in the layout... Don't get me wrong, documentFragment is a cool and useful thing when we need to do a lot of DOMwork without getting layout-redraw in the way on each and every operation.

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>

There is one tiny problem with this: we get one 'empty' image if our img array is empty. Nothing that a simple if can't handle tough.. Since array.length is 1-based we can even have javascript coerce a length of 0 to false.
In addition we could change/bloat this to an anonymous (unnamed) immediately invoked function expression (IIFE):

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

or even accept arguments for the trailing and leading HTML code surrounding the url's:

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

Etc, etc, etc...

Depending on the actual (browser's) engine implementation (and optimizations), this 'join' (like all other solutions) most probably still has a loop in the background..

Since forEach is commonly seen as a somewhat slow method (we all agree there is still a loop in the background) which tends (as always, depending on engine) to do a lot more work than might be required and it will callBack a function (passing around some (3) arguments) for every element in the array, I'll present a loop-version here, using string-concatenation (for diversity in this answer and 'modern' 'faster' string-concatenation):

(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="'
 , '" />'
));

All of this should work on any browser using HTML4 and HTML5. Also note that the above will overwrite the contents of 'container' (not add to it).

The DOM-methods however are technically required for X(H)ML as the elements don't have an innerHTML property officially (although a lot of browsers don't mind), but we can still use a simple (non-hidden) loop while not leaking anything to the global scope, just by modifying the above function:

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

Note that this example adds to the content in 'container'.

These examples are intended to outline a couple of things: innerHTML, join, there is no need to introduce global/leaking variables and most importantly: nothing 'saves' an 'unnecessary' loop (in fact, depending on what you are doing, the visible loop might actually be faster).

However, one might just name the wrapping functions (IIFE in above examples) and just call them, passing your required arguments. That way you can add to or update/overwrite your image-area dynamically (like when you'd load different image-url-array's over ajax etc..) more than once.
For this final example I've used cloneNode in a 'clever' way (that will g-zip nicely) in order to minimize some lookups (whilst not leaving us with a leftover, but unplaced image-node):

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>

Hopefully the explanations between these examples are what Neal had in mind..
In any case, there should now be enough reference between this and other answer(s) to go around.

like image 183
GitaarLAB Avatar answered Oct 16 '22 16:10

GitaarLAB