4
\$\begingroup\$

For responsive images I have come up with a first draft of how I would like to swap image sources depending on media query. This works fine and with the added debouncing function from Paul Irish the window resize event is also taken care of.

Now what I am wondering is if I could write this even more concise and most of all if there are major flaws in my thinking that could lead to images not loading or the like.

I am also wondering whether to write

var $responsiveImage = $('.responsiveImage');

or

var responsiveImage = $('.responsiveImage');

as I am not entirely sure what the difference between the two is.

With the first case I am making an object that contains all the image elements and I assume with the second case I am generating a variable that holds all the objects that are the image elements. Is this correct?

Here is the fiddle and following is the jQuery code.

// on DOM ready
$(document).ready(function() {

    function responsiveImages () {

        // store all responsive image elements in jQuery object
        var $responsiveImage = $('.responsiveImage');

        // go over each responsive image element
        $responsiveImage.each(function () {

            // check how many responsiveImg objects there are
            console.log( $(this).length );

            // declare variable for each of the images found
            var image = $(this);

            // declare variables for small, medium and large images and
            // store information about the image with .data() method
            var smallImage = image.data('smallImage');
            var mediumImage = image.data('mediumImage');
            var largeImage = image.data('largeImage');


            // function to change the image source
            // there are two arguments for this function, the current image and the replacement image
            function replaceImage (currentImage, replacementImage) {                
                // get the current image 'src' attribute and change it to the replacement image
                currentImage.attr('src', replacementImage);
            } // function replaceImage end


            // from 0   - 400  px media query serve small  images
            // from 401 - 700  px media query serve medium images
            // from 701 - 1100 px media query serve large  images
            enquire.register("screen and (min-width:0px) and (max-width:400px)", {

                deferSetup : true,
                setup : function() {
                    // load content via AJAX
                },
                match : function() {
                    // if the media query matches replace the image with the small image
                    replaceImage(image, smallImage);
                },
                unmatch : function() {
                    // if the media query unmatches remove the image source
                    replaceImage(image, '');
                }  

            }); // enquire "screen and (min-width:0px) and (max-width:300px)" end

            enquire.register("screen and (min-width:401px) and (max-width:700px)", {

                deferSetup : true,
                setup : function() {
                    // load content via AJAX
                },
                match : function() {
                    // if the media query matches replace the image with the medium image
                    replaceImage(image, mediumImage);
                },
                unmatch : function() {
                    // if the media query unmatches remove the image source
                    replaceImage(image, '');
                }  

            }); // enquire "screen and (min-width:301px) and (max-width:600px)" end   

            enquire.register("screen and (min-width:701px) and (max-width:1100px)", {

                deferSetup : true,
                setup : function() {
                    // load content via AJAX
                },
                match : function() {
                    // if the media query matches replace the image with the large image
                    replaceImage(image, largeImage);
                },
                unmatch : function() {
                    // if the media query unmatches remove the image source
                    replaceImage(image, '');
                }  

            }); // enquire "screen and (min-width:601px)" end

        }) // $responsiveImage.each function end

    }; // function responsiveImages end

    // call function responsiveImages
    responsiveImages();

        ////////////////////////////////////////////////////////////////////////////////////////////
    // Smart Resize - Paul Irish
    //http://www.paulirish.com/2009/throttled-smartresize-jquery-event-handler/
    (function($,sr){
      // debouncing function from John Hann
      // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
      var debounce = function (func, threshold, execAsap) {
          var timeout;
          return function debounced () {
              var obj = this, args = arguments;
              function delayed () {
                  if (!execAsap)
                      func.apply(obj, args);
                  timeout = null;
              };
              if (timeout)
                  clearTimeout(timeout);
              else if (execAsap)
                  func.apply(obj, args);
              timeout = setTimeout(delayed, threshold || 50); // set this higher for production, up to 50 maximum
          };
      }
      // smartresize 
      jQuery.fn[sr] = function(fn){  return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
    })(jQuery,'smartresize'); // Smart Resize end
    ////////////////////////////////////////////////////////////////////////////////////////////

    // call the function responsiveImages on resize of the browser window
    // for this the function smartresize from Paul Irish is ideal
    $(window).smartresize(function(){   
        responsiveImages();
        console.log('function responsiveImages executed');
    });    

}); // DOM ready end
\$\endgroup\$
3
+50
\$\begingroup\$

$sigils

I personally like to use $sigils to indicate that a JavaScript variable holds a jQuery object. In my opinion, it's a useful convention that makes the code more readable, especially in code where you need to distinguish jQuery objects from DOM objects. If you use a sigil for $responsiveImage, though, you should also use them consistently and write var $image = $(this);.

Noise

Frankly, I find your comments to be obnoxious.

This comment and debug logging statement could be eliminated — the answer is always 1 because that what jQuery.each() means:

// check how many responsiveImg objects there are
console.log( $(this).length );

Some comments are just wrong:

setup : function() {
    // load content via AJAX
},

This one is the worst:

// call function responsiveImages
responsiveImages();

I think that all of the comments can go away if you write less code and clearer code. Instead of redundantly narrating the code, write just one good comment that summarizes the big picture.

The replaceImage() helper function is too trivial. You would be better off without it. Note that it never uses its currentImage parameter anyway.

Flexibility

You hard-code "small", "medium", and "large", as well as their thresholds. Not only does that make your code inflexible, the lack of generality also leads to a lot of copy-and-pasted code.

Instead, take a cue from the design of many jQuery extensions that accept two parameters: a jQuery object with the affected elements, and some kind of optional config parameter. The function should be renamed from responsiveImages() to a verb such as makeResponsive(…).

Triggering

You register three media queries for each image. So, anytime the browser is resized, enquire.js checks the applicability of 30 media queries. Instead, you should register only 3 queries.

The efficiency improvement from reducing the number of registered queries should be sufficient to eliminate the need for debouncing the window resize handler.

In fact, the smartresize handler actually makes things worse. When the window is being resized, you register another set of 30 media queries every 50 milliseconds.

Suggested solution

$(function() {
    /**
     * Makes elements in the $responsiveImages jQuery object
     * switch their image src based on media queries.
     *
     * config is an optional hash whose keys are CSS media
     * queries and whose values are the name of the data
     * attribute on the <img> element that holds the URL to the
     * image that should be activated when the media query is
     * matched.
     */
    function makeResponsive($responsiveImages, config) {
        if (!config) config = {
           'screen                       and (max-width:400px)': 'small-image',
           'screen and (min-width:401px) and (max-width:700px)': 'medium-image',
           'screen and (min-width:701px)'                      : 'large-image',
        };

        for (var mediaQuery in config) {
           (function(mediaQuery) {
              enquire.register(mediaQuery, function() {
                 console.log("Activated " + mediaQuery);
                 $responsiveImages.each(function() {
                    var $image = $(this);
                    $image.attr('src', $image.data(config[mediaQuery]));
                 });
              });
           })(mediaQuery);
        }
    };

    makeResponsive($('.responsiveImage'), {
       // Override width thresholds for this demo
       'screen                       and (max-width:600px)': 'small-image',
       'screen and (min-width:601px) and (max-width:800px)': 'medium-image',
       'screen and (min-width:801px)'                      : 'large-image',
    });
});
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.3.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/WickyNilliams/enquire.js/master/dist/enquire.min.js"></script>

<div class="gallery">
	<img class="responsiveImage" src=""
	     data-small-image="http://dummyimage.com/200x200/000/ffffff.png&text=Small+200+x+200"
	     data-medium-image="http://dummyimage.com/300x300/000/ffffff.png&text=Medium+300+x+300"
	     data-large-image="http://dummyimage.com/400x400/000/ffffff.png&text=Large+400+x+400">
	
	<img class="responsiveImage" src=""
	     data-small-image="http://dummyimage.com/200x200/000/ffffff.png&text=Small+200+x+200"
	     data-medium-image="http://dummyimage.com/300x300/000/ffffff.png&text=Medium+300+x+300"
	     data-large-image="http://dummyimage.com/400x400/000/ffffff.png&text=Large+400+x+400">
	
	<img class="responsiveImage" src=""
	     data-small-image="http://dummyimage.com/200x200/000/ffffff.png&text=Small+200+x+200"
	     data-medium-image="http://dummyimage.com/300x300/000/ffffff.png&text=Medium+300+x+300"
	     data-large-image="http://dummyimage.com/400x400/000/ffffff.png&text=Large+400+x+400">
	
	<img class="responsiveImage" src=""
	     data-small-image="http://dummyimage.com/200x200/000/ffffff.png&text=Small+200+x+200"
	     data-medium-image="http://dummyimage.com/300x300/000/ffffff.png&text=Medium+300+x+300"
	     data-large-image="http://dummyimage.com/400x400/000/ffffff.png&text=Large+400+x+400">
	
	<img class="responsiveImage" src=""
	     data-small-image="http://dummyimage.com/200x200/000/ffffff.png&text=Small+200+x+200"
	     data-medium-image="http://dummyimage.com/300x300/000/ffffff.png&text=Medium+300+x+300"
	     data-large-image="http://dummyimage.com/400x400/000/ffffff.png&text=Large+400+x+400">
	
	<img class="responsiveImage" src=""
	     data-small-image="http://dummyimage.com/200x200/000/ffffff.png&text=Small+200+x+200"
	     data-medium-image="http://dummyimage.com/300x300/000/ffffff.png&text=Medium+300+x+300"
	     data-large-image="http://dummyimage.com/400x400/000/ffffff.png&text=Large+400+x+400">
	
	<img class="responsiveImage" src=""
	     data-small-image="http://dummyimage.com/200x200/000/ffffff.png&text=Small+200+x+200"
	     data-medium-image="http://dummyimage.com/300x300/000/ffffff.png&text=Medium+300+x+300"
	     data-large-image="http://dummyimage.com/400x400/000/ffffff.png&text=Large+400+x+400">
	
	<img class="responsiveImage" src=""
	     data-small-image="http://dummyimage.com/200x200/000/ffffff.png&text=Small+200+x+200"
	     data-medium-image="http://dummyimage.com/300x300/000/ffffff.png&text=Medium+300+x+300"
	     data-large-image="http://dummyimage.com/400x400/000/ffffff.png&text=Large+400+x+400">
	
	<img class="responsiveImage" src=""
	     data-small-image="http://dummyimage.com/200x200/000/ffffff.png&text=Small+200+x+200"
	     data-medium-image="http://dummyimage.com/300x300/000/ffffff.png&text=Medium+300+x+300"
	     data-large-image="http://dummyimage.com/400x400/000/ffffff.png&text=Large+400+x+400">
	
	<img class="responsiveImage" src=""
	     data-small-image="http://dummyimage.com/200x200/000/ffffff.png&text=Small+200+x+200"
	     data-medium-image="http://dummyimage.com/300x300/000/ffffff.png&text=Medium+300+x+300"
	     data-large-image="http://dummyimage.com/400x400/000/ffffff.png&text=Large+400+x+400">
	
</div>

\$\endgroup\$
  • \$\begingroup\$ Just to let you know I am not using this anywhere before I have actually understood what is happening and why. I will try to invite you to chat with the questions I have concerning this once I have half-way wrapped my mind round what is happening in your code. Thank you very much for your detailed reply!! \$\endgroup\$ – lowtechsun Jun 4 '16 at 16:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.