Implementation

Style Properties

The implementation uses the three different style collection objects. Below is a short description of these and how they can be used.

style

This property directly reflects the inline style attribute as well as style changes set using scripting in the normal fashion.

<element style="width: 100px" />

element.style.width = "100px";

currentStyle

currentStyle returns the current cascaded value for the CSS property. If no inline style attribute is present then global style sheets dictate the value for this. Properties in this collection are read only.

<style type="text/css">

#my-element {
   width: 100px;
}

</style>
<script type="text/javascript">

// this will alert "100px"
alert(document.getElementById("my-element").currentStyle.width);

</script>

runtimeStyle

Setting the runtime style has the highest priority so this allows you to set the CSS value without changing the inline attribute or the global style sheet. This is very similar to the override style in the DOM Level 2 Style which allows you to override all the styles provided by the document and external style sheets.

Setting the Size

The methods that set the width and height are pretty simple. We just add or subtract the padding and borders depending on the box model to use.

function setBorderBoxWidth(n) {
   element.runtimeStyle.width = Math.max(0, n - getBorderLeftWidth() -
      getPaddingLeft() - getPaddingRight() - getBorderRightWidth()) + "px";
}

function setContentBoxWidth(n) {
   element.runtimeStyle.width = Math.max(0, n + getBorderLeftWidth() +
      getPaddingLeft() + getPaddingRight() + getBorderRightWidth()) + "px";
}

The getters for the padding and borders just return the numerical value of the respective currentStyle property.

function getBorderWidth(sSide) {
   if (element.currentStyle["border" + sSide + "Style"] == "none")
      return 0;
   var n = parseInt(element.currentStyle["border" + sSide + "Width"]);
   return n || 0;
}

Notice here how we return 0 if the border style is set to none. Also notice how the || operator is used. The value NaN is treated as false so the second expression is returned whenever the parsInt fails.

Finding the Box Model

First we find the box model used by Internet Explorer by default. This can be found by checking the document.compatMode.

function getDocumentBoxSizing() {
   if (doc.compatMode == null || doc.compatMode == "BackCompat")
      return "border-box";
   return "content-box"
}

Once we start to setting the box-sizing on the element it gets a bit complicated to find the value for this. Internet Explorer allows custom CSS properties but they are not treated exactly like built in properties. For example if we use box-sizing in a style block we need to use currentStyle["box-sizing"] but if the user has set the property using script with style.boxSizing we will have to use boxSizing instead. The next problem is that if we set the property to "" we expect the global value to be used instead. The next problem is that the currenStyle property is not updated fast enough. This gives the following (ugly) code to find the box-sizing.

function getBoxSizing() {
   var s = element.style;
   var cs = element.currentStyle

   if (typeof s.boxSizing != "undefined" && s.boxSizing != "")
      return s.boxSizing;
   if (typeof s["box-sizing"] != "undefined" && s["box-sizing"] != "")
      return s["box-sizing"];
   if (typeof cs.boxSizing != "undefined" && cs.boxSizing != "")
      return cs.boxSizing;
   if (typeof cs["box-sizing"] != "undefined" && cs["box-sizing"] != "")
      return cs["box-sizing"];
   return getDocumentBoxSizing();
}

Updating the Size

The size needs to be updated at load as well as when the CSS changes. At construction we just call updateBorderBoxWidth and updateBorderBoxHeight but we also add an event listener to the propertychange event. This allows us to update the size when the style or className changes.

function updateBorderBoxWidth() {
   element.runtimeStyle.width = "";
   if (getDocumentBoxSizing() == getBoxSizing())
      return;
   var csw = element.currentStyle.width;
   if (csw != "auto" && csw.indexOf("px") != -1) {
      if (getBoxSizing() == "border-box")
         setBorderBoxWidth(parseInt(csw));
      else
         setContentBoxWidth(parseInt(csw));
   }
}

Notice here that we reset the runtimeStyle. This is done so that we can find the runtimeStyle that the user has set.

function checkPropertyChange() {
   var pn = event.propertyName;
   var undef;

   if (pn == "style.boxSizing" && element.style.boxSizing == "") {
      element.style.removeAttribute("boxSizing");
      element.runtimeStyle.boxSizing = undef;
   }

   switch (pn) {
      case "style.width":
      case "style.borderLeftWidth":
      case "style.borderLeftStyle":
      case "style.borderRightWidth":
      case "style.borderRightStyle":
      case "style.paddingLeft":
      case "style.paddingRight":
         updateBorderBoxWidth();
         break;

   case "style.height":
   case "style.borderTopWidth":
   case "style.borderTopStyle":
   case "style.borderBottomWidth":
   case "style.borderBottomStyle":
   case "style.paddingTop":
   case "style.paddingBottom":
      updateBorderBoxHeight();
      break;

   case "className":
   case "style.boxSizing":
      updateBorderBoxWidth();
      updateBorderBoxHeight();
      break;
   }
}

Notice here that in case the boxSizing is set to "" we invalidate that property as good as possible.

Introduction
Implementation
Demo
Download

Author: Erik Arvidsson