Sunday, November 1, 2009

jQuery plug-in to fix the width=100% problem

While making the layout for a new app I’m writing, I discovered what I call the Box model 100% width bug.

The box model spec states that the resulting width of an element whose width is 100% will equal to:

calculated width = (inner width of parent) + (element's padding) + (element's margin)

That means that given this html:

<div id="container" style="width:400px;">
  <div id="div1" 
     style="background-color:Green; width:100%; margin-left:10px; padding-left:10px;">
     Lorem ipsum dolor sit amet, consectetur adipisicing elit...
  </div>
</container>

The div1 's total width will be 420px; not 400px as you would want.

A solution is to compensate by wrapping div1 in a container that has a padding-left equal to the element's (2 * margin) + (padding):

<div id="container" style="width:400px;">
  <div id="wrapper" style="padding-left:30px;">
    <div id="div1" 
     style="background-color:Green; width:100%; margin-left:10px; padding-left:10px;">
     Lorem ipsum dolor sit amet, consectetur adipisicing elit...
    </div>
  </div>
</container>

Why add an extra element instead of modifying the container div? Because in a real cases, the container div will probably be many levels above the element that you are trying to style. The wrapper div used to compensate should be the immediate parent of that element.

I wrote a jQuery plugin to automatically inject the wrapper div to elements. Here is how to use it:

$(function() {
    $("#div1").fixFullWidth();
})

The plug-in iterates  through at all elements selected by the jQuery selector and wraps each of them in a div that has the correct padding-left value.

Here is the plug-in source. BTW, I'm not a javascript expert so if you come up with a better implementation just let me know and I will update this post.

$.fn.fixFullWidth = function() {
    //create the endsWith function of the String type
    String.prototype.endsWith = function(str) {
        return (this.match(str + "$") == str);
    }

   this.each(function() {
        var elemMarginValue = $(this).css('margin-left');
        var elemMargin = 0;
        if (elemMarginValue.endsWith("px")) {
            elemMargin = parseInt( elemMarginValue.replace('px', ''));
        }
        var elemPaddingValue = $(this).css('padding-left');
        var elemPadding = 0;
        if (elemPaddingValue.endsWith("px")) {
            elemPadding = parseInt( elemPaddingValue.replace('px', ''));
        }
        var padding_left = (elemMargin * 2) + elemPadding;
        if (padding_left != 0) {
            $(this).wrap('<div class="fixFullWidth" style="padding-right:' + padding_left + 'px;"></div>');
        }
    });
    return this;
};

I hope this helps.

1 comment: