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

后端 未结 2 1473
花落未央
花落未央 2020-11-29 10:34

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

var imgs = [\'http://lorempizza.com/380/240\', 
            \'http://dummyimage.com/250/ffffff/00000         


        
相关标签:
2条回答
  • 2020-11-29 10:57

    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.

    0 讨论(0)
  • 2020-11-29 11:03

    One way to do this is to loop over your array of images, create an img element for each, set the element's src to the current image source in your array, then append it to your container. This would look 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/'];
    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);
    }
    

    However, this isn't particularly efficient:

    • We're using an unnecessary loop
    • We're querying the dom on every iteration
    • We're introducing the global i and j variables
    • We're not using JavaScript's wonderful Array methods!

    Instead, let's use a documentFragment and JavaScript's forEach Array method.

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

    This way we're:

    • Only hitting the DOM once
    • Not introducing global variables
    • Maintaining cleaner, easier to read code!

    Then just to make sure your new images look nice, add a bit of CSS to the mix:

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

    Bam! Here's a fiddle

    0 讨论(0)
提交回复
热议问题