[code review] sizeit.js - js utility for responsive web design

I’ve created a javascript utility for detecting screen size and dynamically loading css. Works kinda like media queries. I posted the code and examples on github. It is open source and free for anyone to use as they like. You can read more information about it on [URL=“https://github.com/johnpolacek/sizeit.js#readme”]github or on the [URL=“http://johnpolacek.com/sizeit/”]sizeit.js website.

I have a couple simple demos posted: demo1 and [URL=“http://johnpolacek.com/sizeit/sizeit.js/demo1.html”]demo2

My background is first in art/design, then Flash/AS3 design and development. Getting into JavaScript has been fun. Always trying to get better, so feedback is appreciated.

The code for the utility itself is:


(function(window) {
    
    var config = {};
    var screenW = window.innerWidth || document.body.clientWidth;
    
    function update() {
        
        screenW = window.innerWidth || document.body.clientWidth;
        var newSize;
        
        for (var i = 0; i < config.settings.length;  i++) {
            
            var setting = config.settings[i];
            
            if (screenW < setting.max) {
                newSize = setting;
                break;
            } else if (!setting.max) {
                newSize =  setting;
            }
        }
        
        if (!config.size || newSize.name != config.size.name) {
            config.size = newSize;
        } else {
            newSize = null;
        }
        
        if (config.size.css && newSize) {
            
            // if there is a stylesheet, update it, otherwise make new
            if (config.css) {
                
                var links = document.getElementsByTagName("link");
                
                for (i = 0; i < links.length; i++) {
                    
                    if (links[i].getAttribute("title") == "sizeit") {
                        config.css.href = config.size.css;
                        break;
                    }
                }
            
            } else {
                
                config.css = window.document.createElement("link");
                config.css.href = config.size.css;
                config.css.type = "text/css";
                config.css.rel = "stylesheet";
                config.css.title = "sizeit";
                window.document.getElementsByTagName("head")[0].appendChild(config.css);
            }
        }      
    }
    
    window.sizeit = {
        
        configure : function() {
            
            config.settings = arguments;
            
            // sort by breakpoints, lowest to highest
            for (var i = 0; i < config.settings.length; i++)
            {
                for (var j = i + 1; j < config.settings.length; j++)
                {
                    if (config.settings[i].max > config.settings[j].max)
                    {
                        s = config.settings[j];	
                        config.settings[j] = config.settings[i];
                        config.settings[i] = s;
                    }
                }
            }
            
            update();
            
            if (window.addEventListener) {
                window.addEventListener("resize", update, false);
            } else if (window.attachEvent) {
                window.attachEvent("resize", update);
            } else {
                window.onresize = update;
            }
            
        },
        
        size : function() {
            return config.size.name;
        },
        
        width : function() {
            return screenW;
        }
    };
    
})(this);

You use it like this:


sizeit.configure(
    {
        max: 600,
        css: "css/small.css",
        name: "small"
    },
    {
        max: 1024,
        css: "css/medium.css",
        name: "medium"
    },
    {
        css: "css/large.css",
        name: "large"
    }
);

Nice job bud but there was a major issue with your code which stopped it from working in Internet Explorer, i have submitted 2 forks to your GitHub project so you can see the changes i made.

Thanks Sgt! Coming from flash, having to deal with IE issues has been a new thing for me, so I appreciate your help. I merged your changes into the repo.

I have three things that stand out to me from this script:

  1. Why does the user need to supply a name for the script? It looks like you’re using the name internally to see if the script is already loaded, but you could just as well take the index of the CSS you’re loading in the list. E.g.

screenW = windowSize();
var newSize;
var newIndex;
var i;

for (i = 0; i < config.settings.length; i++) {
   var setting = config.settings[i];

   if (screenW < setting.max) {
      newSize = setting;
      newIndex = i;
      break;
   } else if (!setting.max) {
      newSize = setting;
      newIndex = i;
   }
}

if (!config.size || newIndex != config.index) {
  config.size = newSize;
  config.index = newIndex;
}

(not tested)

Sure it makes the code a bit more verbose, but it prevents the extra parameter ‘name’ that I don’t feel should be there in the first place (plus, it’ll prevent questions from others asking “what do I set for the name? does it matter what I set? Can I give two scripts the same name? etc… etc…”)

  1. It would be cool if instead of one CSS file, the script could accept an array of CSS files, so you can add multiple CSS files at once

{
   css: ["style1.css", "style2.css"],
   max: 1024
}

  1. This is a bit picky maybe, but I wouldn’t expect a function called ‘configure’ to actually do something, other than just configuring the object so I can later call another function that actually makes the object do it’s thing.
    A more appropriate name for that function would IMHO be something like ‘apply’, ‘initialize’, ‘go’, etc.

Other than that it looks good to me, nice job :tup:

Thanks for your feedback Scallio

  1. I have the name parameter so I can get the current size setting from other scripts (for example, to use with Mustache to add html to the page based on the size). I think you have a good point though, so I think I will make the param optional. Maybe I’ll have it return the index instead if it isn’t set. I’ll think some more about this one…

  2. I like that. Will do.

  3. The configure function both configures the settings and starts ‘doing its thing’, all in one function. There is no second function call to start it working.

Glad I posted this here. Just the type of responses I was hoping for.