Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

CSS Box Model and Positioning

4.94/5 (13 votes)
29 Sep 2013CPOL10 min read 70.6K   312  
The article presents basic information on CSS Box model and positioning
CSS Box Model Image

Introduction

The CSS Box Model and CSS Positioning are two fundamental concepts in web design. Understanding these concepts is essential to mastering page layout and other web design issues.

In this article, I describe a demo web page (can be run from here) with some JavaScript code to aid in the understanding of the W3C Box model and CSS positioning. The demo works properly in the latest versions of FireFox and Chrome.

As I was presenting these concepts in a web engineering & design course at KFUPM (where I teach), I felt the need for some interactive process to aid in understanding these concepts, and that is how the idea for this sprung up.

This article follows the same spirit and provides explanation and some useful examples.

The article is organized into three parts:

  1. How the Demo works
  2. The CSS Box Model
  3. CSS Positioning

How the Demo Works

The following is the source HTML for the demo page:

HTML
<html>
<head>
   <title>CSS BOx Model</title>

<script type="text/javascript" >

  window.onload = function () { getStyleInfo();  placeShadowDiv(); };

  lastX=0; lastY=0;

function getStyleInfo()
{
  var elem = document.getElementById("B");

  //alert(elem);

  var styleInfo = window.getComputedStyle(elem);

   outstr = " width:"+styleInfo.width;
   outstr += "; height:"+styleInfo.height;
   outstr += "; left:"+styleInfo.left;
   outstr += "; top:"+styleInfo.top;
   outstr += "; margin:"+styleInfo.marginLeft; // margin is "" for FireFox
   outstr += "; padding:"+styleInfo.paddingLeft; // padding is "" for FireFox
   outstr += "; border:"+styleInfo.borderLeftWidth;

 //document.write(elem.currentStyle);
 //alert(document.getElementById("styleInfo"));

  document.getElementById("styleInfo").innerHTML =outstr ;
}

function handleClick(oEvent)
{
    //alert(this.tagName);

    var elem = document.getElementById("txt1");

    var distX = Math.abs(oEvent.pageX- lastX);
    var distY = Math.abs(oEvent.pageY- lastY);

    var distString = "";
    if (lastX!=0) distString = ";
    dist = (" +  distX +  "," + distY + ")";

    lastX = oEvent.pageX; lastY = oEvent.pageY;

   elem.value += "\n At (" + oEvent.pageX + ",
   " + oEvent.pageY + ")" + distString;
}

function setPadding(e)
{
  //alert("button click");

   document.getElementById("B").style.padding=
   document.getElementById("padding").value +"px";
   getStyleInfo();
   placeShadowDiv();
   e.stopPropagation(); // event.stopPropagation() works in chrome but fails in firefox
  //  window.event.cancelBubble = true; // this  dose not work in firefox
}

function setMargin(e)
{  document.getElementById("B").style.margin=document.
   getElementById("margin").value +"px";
   getStyleInfo();
   placeShadowDiv();
  e.stopPropagation();
  // e.cancelBubble = true;
}

function setBoxSizing(boxSize)
{  var is_firefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
   var elem = document.getElementById("B");

    if (is_firefox) elem.style.MozBoxSizing = boxSize; // For FireFox
    else elem.style.boxSizing = boxSize; // Works in Chrmoe

   placeShadowDiv();
}

function placeShadowDiv()
{   var BstyleInfo = window.getComputedStyle( document.getElementById("B") );

    var B_width =parseInt(BstyleInfo.width);
    var B_height =parseInt(BstyleInfo.height);

   // alert(B_width + "/" + B_height);

    var B_topMargin = parseInt(BstyleInfo.marginTop);
    var B_leftMargin = parseInt(BstyleInfo.marginLeft);

    var B_bottomMargin = parseInt(BstyleInfo.marginBottom);
    var B_rightMargin = parseInt(BstyleInfo.marginRight);

    var B_topPadding = parseInt(BstyleInfo.paddingTop);
    var B_leftPadding = parseInt(BstyleInfo.paddingLeft);

    var B_bottomPadding = parseInt(BstyleInfo.paddingBottom);
    var B_rightPadding = parseInt(BstyleInfo.paddingRight);

    var B_border = parseInt(BstyleInfo.border ); // In FireFox, this gives NaN

    if (isNaN(B_border)) B_border = parseInt(BstyleInfo.borderLeftWidth);

   var width =  B_width +  B_leftPadding +  B_rightPadding + 2 * B_border +
		B_leftMargin + B_rightMargin;

   var height =  B_height  +  B_topPadding +  B_bottomPadding +  2 * B_border +
		B_topMargin +  B_bottomMargin;

   // alert( width + "/" + height);

   DivB_BoxSizing =BstyleInfo.boxSizing;

    // alert("DivB_BoxSizing: " + DivB_BoxSizing);

   if (DivB_BoxSizing == "padding-box") // padding is parts of width/height
   {  width = B_width + 2 * B_border + B_leftMargin + B_rightMargin;
      height =  B_height +  2 * B_border + B_topMargin +  B_bottomMargin;
   }


   if (DivB_BoxSizing == "border-box") // padding and border are parts of width/height
   {   width = B_width + B_leftMargin + B_rightMargin;
      height =  B_height +   B_topMargin +  B_bottomMargin;
   }

    document.getElementById("shadowDiv").style.left=  BstyleInfo.left;

    document.getElementById("shadowDiv").style.top= BstyleInfo.top;
    document.getElementById("shadowDiv").style.width= width  +"px";
    document.getElementById("shadowDiv").style.height= height  +"px";
}

</script>

<style type="text/css" >

 body, td { font-size:14pt;  }

 td { line-height:2.2em; }

 select { font-size:.9em; }

 input { margin:0; margin-left:16px; font-size:11pt; font-weight:bold; margin-top:6px;}

 input[type=button] { padding:4px 8px; width: 120px; }

 p { font-size:14pt; font-family:Tahoma; }
 
 div#A {z-index:-1;position:absolute; left:50px;top:320px;height:250px;
 width:600px;border: 1px solid #000; background-color:#CCC}
 div#B { -moz-box-sizing: content-box; box-sizing:content-box;
 z-index:2;position:absolute;left:50px;top:30px;width:400px;height:120px;
 border:10px solid  lightblue;background-color:green;margin:30px; padding:20px;}

  div#shadowDiv {z-index:0;position:absolute;margin:0; padding:0;
  background-color:maroon; width:160px; height:150px}

  div#topDiv { position:absolute;margin:0; padding:0; top:0;
  left:100px; height:30px; border-left:1px solid black;}

  div#leftDiv { position:absolute;margin:0; padding:0; left:0;
  top:60px; width:50px; border-bottom:1px solid black;}

</style>
</head>
<body onClick="handleClick(event)" >

<div id="A" >

  <div id="B">
     <div style="background-color:yellow; margin:0;
     padding:0;width:100%;height:100%" >
        Yellow area is content of <b>div#B</b>
        (shows the extent of width and height of <b>div#B</b>
        for default box-sizing)<br/><br/>
        Green area is padding of <b>div#B</b>
     </div>
   </div>

  <div id="shadowDiv"></div>

  <div id="topDiv"> top (30px)</div>

  <div id="leftDiv"> left (50px)</div>

</div>

<div onclick="event.stopPropagation()"
style="border-bottom:1px solid gray" >
<table>
<tr>
<td>
<textarea style="font-size:16pt;font-weight:bold;width:440px;
height:160px;" id="txt1" rows="5" cols="30"></textarea>
</td>
<td>Margin and padding for div#B:
<br/>
<input id="margin" size="5"  /><
input type="button" onclick="setMargin(event)"
value="Set Margin" />
<br/>
<input id="padding" size="5"  /><
input type="button" onclick="setPadding(event)"
value="Set Padding" />
<br/>
<label>Box Sizing
<select onChange="setBoxSizing(this.value)" >
<option  value="content-box" >Content Box (default)</option>
<!--<option  value="padding-box" >Padding Box</option>-->
<option  value="border-box" >Border Box</option>
</select>
</label>

</td>
</tr>
</table>

<p><b>Style Info for div#B:</b><
span id="styleInfo"></span></p>
<p><b><i>Note:</i></b> Offset (left,top, ..)
is measured <b>from </b><i>border of container</i> <b>to </b>
<i>start of margin</i> (and not border) of the enclosed element.
Thus, the position of the "Margin Box"
of the enclosed block is not affected by margin or padding of the container.
</p>

</div>
</body>
</html>

In the HTML, we define two divisions: div#A and div#B (a child division), both are absolutely positioned. Thus (read the explanation given later), the top and left pixel values of div#B are offsets from the top and left borders, respectively, of div#A.

In the JavaScript code, the function handleClick() tracks the position of mouse-clicks and prints the (x,y)-coordinates to the textarea (at top-left side of the page). It also prints the horizontal/vertical distance between two successive positions. This way, the user can verify the positioning of div#B and the dimensions of its four boxes. For example, the coordinates shown in the above figure are obtained by clicking on opposite ends of the yellow area (content box of div#B); thus, the "dist = (400,120)" corresponds to the width and height of div#B.

Basic Technique Used

To distinguish the content box of div#B from its padding, I set the content of div#B to another division (with padding=margin=0 and width=height=100%) having a background color different from that of div#B.

To track the margin box of div#B, I use an auxiliary division (with id="shadowDiv"). The left, top, width and height of the shadowDiv are computed by the function placeShadowDiv() (the function is first called when the page is loaded and then every time the margin or padding of div#B is changed).

Finally, the function getStyleInfo() is used to compute (and display) style information for div#B. This function is called first when the page is loaded and then whenever padding/margin/box-sizing are changed.

The CSS Box Model

Box model image

The CSS Box Model defines the layout of HTML block elements such as divisions (<div>s) and paragraphs (<p>s). It does not apply to inline elements (<span>s, anchors (<a>s), and a bunch of others).

Every block element is laid out (by the browser) as a rectangular box with four parts (boxes). The innermost part (box) is the content (or content box) which is surrounded by padding (or padding box). The border (or border box) encloses the padding; the outermost part is the margin (margin box). The padding is used to specify the amount of spacing surrounding the content within the border box, while the margin is used to specify the spacing separating the element from its surroundings. For screen display, padding and margin are normally specified in pixels.

By default (if no width is set), a block element takes up the whole width of the browser window and is resized when the browser window is resized. The element's height (if no height is set) adjusts to accommodate its content (the user may have to scroll).

When a block element is assigned a width (or height), it is applied to the content box only and not border-to-border. Although this might be counterintuitive, it is the default behavior (default W3C Box model). This behavior can be changed but only by setting the CSS box-sizing property. In CSS3, this property can be set to "content-box", "padding-box" or "border-box".

For the default box model, the area occupied by a block element will have the following total width and height:

Total width = width + left padding + right padding + left border + right border + left margin + right margin

Total height = height + top padding + bottom padding + top border + bottom border + top margin + bottom margin

CSS Positioning

The following table gives basic information about the CSS position property and other related properties.

PropertyValue and Description
positionstatic: default position; the element is placed according to normal flow.
The properties top/left/bottom/right are ignored.
relative: the element is placed at some offset from its normal static position.
absolute: the element is placed at a fixed position within its containing element.
fixed: the element is placed at a fixed position within the browser window
(it will not scroll out of view when the page is scrolled).
top, bottom, left, rightThese properties determine the positions of the element (margin box) corners.
The values are interpreted as offsets (displacements).
top: a vertical offset (+ move down, - move up)
left: a horizontal offset (+ move right, - move left)
bottom: a horizontal offset (+ move up, - move down)
right: a vertical offset (+ move left, - move right)
floatThis property is normally used with block elements with "position:static";
it is used to float the element to the left or right of its container with other
content wrapping around the floated element.
(If this property is set, position and offset setting will be ignored.)

Note that the value of an offset property (top, left, right, bottom) defines the distance that a particular edge of the element (its margin box) is to be offset from the corresponding edge (inner border) of its container. For instance, the value of top [left] defines the distance the top [left] edge of the positioned element to the top [left] edge of its containing block. The sign (+ or -) specifies the direction of the displacement (bottom/right are opposite of top/left). Thus, positive values move the element toward the center of the containing block; negative values move the element out of the containing block.

Absolute Positioning

Absolute positioning is used to place the positioned element at some fixed position in relation to some enclosing container block. The top/left/right/bottom are interpreted as offsets form the borders of the container. The container block is the nearest enclosing block with a position value other than static; if no such block is found, then the container is assumed to be the browser window.

Absolute positioning takes the element out of the "normal flow"; that is, the absolutely-positioned element will occupy the area designated for it regardless of other elements that might happen to be there. As a result, the absolutely-positioned element might overlap with (or cover) other elements. In this case, the front-back display order can be controlled by using the z-index property.

Absolutely-positioned elements have borders and padding. They can also have margins (in relation to their parent container as specified above).

Illustration of Absolute Positioning

As an illustration of absolute positioning, consider the following example of HTML+CSS code and the associated figure.

HTML
div#A {position: absolute; top: 25px; left: 40px;
width: 300px; height: 120px; border: 1px solid #000; background-color:#CCC}
div#B {position: absolute; top: 20px; left: 50px; right: 30px;
bottom: 40px; border: 1px solid #000; background-color:#666}

<div id="A">
   <div id="B">
   </div>
</div>

Absolute Positioning Example 1

In this example, we have two divisions: div#A and div#B, where div#B is a child division. Both divisions are positioned absolutely and, therefore, each will appear at some fixed position with the top/left/right/bottom offsets being relative to the borders of some enclosing container.

It is assumed that div#A is not contained in any other block with a non-static position. Thus, the interpretation of the top/left/right/bottom offsets for div#A will be relative to the browser window. On the other hand, the positioning of div#B will be relative to div#A.

Some Examples of Absolute Positioning

The following is a good example of employing CSS positioning. The idea is to overlap two divisions to create a shadow (or embossed) effect as in the following figure:

Absolute Positioning Example 2

This can be produced using the following HTML+CSS.

HTML
<style>
div { font-size:24pt; font-family:georgia; font-style:italic;color:indigo; }

</style>

<div style="position:relative;" >
<div style="position:absolute;left:0; top:0;" >
CSS Positioning is great!</div>
<div style="z-index:-1;position:absolute;left:2px;
top:2px;color:gray" >CSS Positioning is great!</div>
</div>

To achieve the preceding result, we need one of the overlapping divisions to have its left/top offset slightly (2 pixels down and right) from that of the other. The process is made easier when the top and left are measured relative to a division that encloses both overlapping divisions. The enclosing division needs to have its position set to absolute or relative (the latter is more appropriate to have the enclosing division part of the normal flow).

Relative Positioning

Relative positioning is used to place the positioned element (an inline or a block element) at some offset position from where it will normally appear (in normal flow) using some settings for top/left/right/bottom (interpreted as offsets form their normal positions). If no offset value is given, the element will remain in its normal position as determined by the browser. However, when offset values are specified, the element will move in accordance with the offsets given (normally, one of "left, right" and one of "top, bottom" need to be specified to avoid conflicts). The area that is originally occupied by the element will be kept blank (this is a distinctive feature of relative positioning).

For example, A superscript 2 can be expressed in HTML as (this can be an alternative to using <emp><sup> tag):

HTML
A<span style="position:relative; top:-.7em;font-size:smaller" >2</span>

Using relative positioning, we can have text placed on top and bottom of other text, as in the following example:

Relative positioning example

This is produced using the following HTML+CSS.

HTML
<style>
div#myDiv { font-size:24pt; font-family:georgia; margin:1em; }
span.smallText { font-size:70%;}
</style>

<diV id="myDiv" >
∑ x<sub>i</sub><span  class="smallText"
style="position:relative; top:-1.2em;
left:-2.4em;" ><i>n</i></span>
<span class="smallText" style="position:relative;
top:1.2em;left:-3.2em;" ><i>i</i>=1</span>
</div>

Common Uses of Relative Positioning

One common use of relative positioning is to change the origin of the coordinate system (for top/left/bottom/right) from the browser window to some other element. This utilizes the following fact.

If a block element is set to "position:relative" (or any value other than static), it becomes the coordinate system for its absolutely-positioned child elements.

This is illustrated by the following example, which shows how to pin down four child divisions to the four corners of their parent. The HTML+CSS code is as follows:

HTML
<style>
   div.childDiv { position: absolute; border:1px solid green;
   width:150px;height:150px; text-align:center;line-height:150px; }
</style>

  <div style="position:relative; width:300px; height:300px;
  border:1px solid black;margin-left:30px;" >

     <div class="childDiv" style="background:#FEE;
     left:0;top:0" >left:0;top:0</div>

     <div class="childDiv" style="background:#EEF;
     right:0;top:0" >right:0;top:0</div>

     <div class="childDiv" style="background:#EFE;
     left:0;bottom:0" >left:0;bottom:0</div>

     <div class="childDiv" style="background:#EEE;
     right:0;bottom:0" >right:0;bottom:0</div>

 </div>

The preceding code renders the following:

Relative positioning example

Let us emphasize few important points in regard to the preceding example.

  • If the position of the parent division is not specified (or set to "static"), then the coordinates of the child divisions will be in reference to the browser window (its current height will determine the "bottom" offset); The child divisions will scroll with the page.

  • If the child divisions were given a position value of "fixed", then they (irrespective of the positioning of their parent) will be pinned down to the four corners of the browser window and they will not scroll with page.

  • Note the CSS styles used for centering the text within each of the child divisions. The "text-align:center" will center the text horizontally and "line-height:150px" will center the text vertically.

Another good use of relative positioning is to notch an element to a certain position relative to its container, as shown by the following figure.

Relative positioning example

The above is produced using the following CSS+HTML code.

HTML
<style>

 div.roundDiv { margin:40px 40px; font-size:14pt; font-family:Tahoma; -moz-border-radius:.5em;
      -webkit-border-radius:.5em; border-radius:.5em;
    }

 div#A { border: 1px solid #e6db61; padding:8px; background:#fcf3c5; width:450px;
     background: -webkit-gradient(linear, 0 0, 0 100%, from(#fcfcfc), to(#fcf3c5));
     background: -moz-linear-gradient(top, #fcfcfc, #fcf3c5);
  }

div#B { position:relative; font-weight:bold; left:16px;
top:-32px; border:1px solid lightgray;
        margin:0; padding:4px; background-color:#CEFEFE;
        width:200px;height:40px;line-height:40px;
    background: -webkit-gradient(linear, 0 0, 0 100%,
    from(#fcfcfc), to(#e6e6e6));
    background: -moz-linear-gradient(top, #fcfcfc, #e6e6e6);
  }

</style>

<div id="Div1" class="roundDiv" >
 <div id="Div2"  class="roundDiv" >
 Relative Positioning</div>
  <p style="margin-top:-10px;" >
  The div for the title is positioned
     relatively (left=16px; top=-32px)
  </p>
 </div>

Without any setting for top and left, div#B will be placed near the top-left corner of div#A (the padding of div#A determines the offset amount). The setting of "top:-32px" moves it 32 pixels upward and the setting of "left:16px" moves it 16 pixels to the right.

Note that the paragraph element within div#B is set with a negative top-margin to move it into the area that became vacant when div#B is moved.

Useful Links

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)