Recently at work, we had need of a basic ‘dashboard’. It’s a pretty simple design: boxes on the left with one level of drop down for menus, a big, central ‘content’ section in the middle.
I was thinking of how I wanted to implement this.
The goal was to use Javascript to create a 100% DOM rendered page that worked in IE and Firefox.
Here’s how I did it after the jump….
<pre>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel=stylesheet type="text/css" href="dashboard.css">
<script type="text/javascript" src="dashboard.js" ></script>
<title>Dashboard</title>
</head>
<body onload="javascript:pageLoad()">
<div id="page_container">
<div id="navigation_bar">
</div>
<div id="content_area">
<div id="titlebar_area">
<div>
<p id="title">A Basic Dashboard Example</p>
</div>
</div>
<div id="main_area">
<h1>Welcome to the dashboard example</h1>
<p> Double-click an item to the left to
expand/collapse.</p>
</div>
<div id="footer">
<p>
<a href="http://validator.w3.org/check?uri=referer"><img
src="http://www.w3.org/Icons/valid-html401"
alt="Valid HTML 4.01 Transitional" height="31" width="88">
</a>
</p>
<p style="text-transform:small;font-variant:small-caps;
font-size:8pt;text-align:right"><em>Version 0.1</em></p>
</div>
</div>
</div>
</body>
</html>
</pre>
You basically have two critical areas in this skeletal bit of HTML: the navigation_bar div and the main_area div. My code is focused on putting “main navigational areas” in the navigation_bar. Updates into the “main view” may need to happen ( it doesn’t really happen in this code ).
To lay out the prettyness of the page, we use CSS:
<pre>
body{
margin-left:0px;
background-color:#AAAAAA;
font-family: arial, helvetica, courier, times;
}
div#page_container{
background-color:#AAAAAA;
margin-right: 10%;
}
div#navigation_bar{
position:absolute;
padding-left:0px;
margin-left:0px;
margin-top:10px;
width: 200px;
width: 300px;
height: 100%;
background-color: #AAAAAA;
}
div.navigation_block{
position:relative;
background-color: white;
margin-bottom: 5px;
margin-left: 5px;
margin-right: 5px;
border-width: 2px 1px 3px 3px;
border-style: solid;
border-color: #222222;
padding-bottom:2px;
width: 11em;
}
div.navigation_block p{
width: 80%
}
div.navigation_block_submenu{
margin-bottom: 5px;
margin-left: 10px;
margin-top: 0;
margin-right: 0px;
margin-left: 5px;
padding: 0px 0px 0px 3px;
margin-bottom:3px;
width: 9em;
background-color:#DDD;
}
.submenu_element{
padding-top:0px;
margin:0px;
font-size: 0.95em;
}
div.navigation_block p{
padding-left: 5px
}
div#content_area{
position:relative;
top:0px;
left:200px;
width:200px;
width:40em;
padding-left:0px;
padding-top:2px;
margin-left:0px;
margin-top:10px;
background-color: #AAAAAA
}
div#titlebar_area{
background-color: white;
border-width: 2px 1px 3px 3px;
border-style: solid;
border-color: #000000;
margin-top: 8px;
height: 52px;
padding:0.5em;
}
p#title{
color:black;
text-align:center
}
div#main_area{
background-color: #FFFFFF;
border-width: 2px 1px 3px 3px;
border-style: solid;
border-color: #000000;
height:370px;
margin-top: 5px;
padding: 2px;
height:100%
}
div#main_area h1{
border-width: 0 0 3px 2px;
border-color: #AAAAAA;
border-style:solid;
color:black;
font-size: 1em;
padding-left: 2px;
margin-right: 2%
}
div#footer{
background-color:#AAA;
padding-top: 10px;
padding-right:0px;
width: 40em
}
/* Stupid hack for stupid internet explorer */
html>body div {
width: 40em
}
/* Make changes that make IE happy, make FF happy */
*
{
-moz-box-sizing: border-box !important;
}
</pre>
That’s the CSS. Now, let’s show the code on how we dynamically update these pages.
/* dashboard.js
AUTHOR: Steven G. Harms (stharms@cisco.com)
CREATION: 8/28/2006
A script that is used with an XML file to use the DOM to create a Web2.0 /
DHTML-y web site
*/
// Global variables, used sparingly
var xmlDoc;
// Function Declaration Area
function add_a_footer(entered_text){
var dom = document.domain;
var myText = document.createTextNode("hello, " + dom);
document.getElementById("footer").appendChild(myText);
var childEl=document.createElement("div");
childEl.className='navigation_block';
childEl.appendChild(myText);
document.getElementById("footer").appendChild(childEl);
}
function addAnEvent(anObject, eventType, callback, capture){
if (window.XMLHttpRequest){ // it's a mozilla-like thing!
anObject.addEventListener(eventType, callback, capture);
return anObject
}else{
anObject.attachEvent('on' + eventType ,callback);
return anObject
}
}
function appendTitleBlock(anObject, myTitle){
//alert('cunning '+aTitle);
if ( myTitle == "" ){
aTitle="ERROR!";
}
var appendTitle = document.createTextNode(myTitle);
var title = document.createElement('p');
title.appendChild(appendTitle);
anObject.appendChild(title);
return anObject;
}
function browser_mutate(){
// Some tricks to help IE render properly
var browser = navigator.appName;
if (browser.match(new RegExp("Microsoft", "i"))){
// Line up the box model
var navbar = document.getElementById('navigation_bar');
navbar.style.marginTop='8px';
//Make sure our submenus are properly whited-out
for ( var divvie in document.getElementsByTagName("div")){
if (divvie.match(new RegExp("submenu","i"))){
var node = document.getElementById(divvie);
node.style.backgroundColor="#FFF"
}
}
}else{
var navbar = document.getElementById('navigation_bar');
navbar.style.marginTop='10px';
}
}
function buildSubmenuFor (aBlock, manipulationClosure){
//alert(aBlock.getAttribute('name'));
//alert(aBlock.childNodes.length);
var childItem;
var theDiv = document.createElement('div');
for (var i=0; i<=aBlock.childNodes.length; i++){
//alert(aBlock.childNodes.item(i));
if (
( aBlock.childNodes.item(i) != null ) &&
( aBlock.childNodes.item(i).nodeName == "name")
){
childItem = aBlock.childNodes.item(i);
//alert ( aBlock.childNodes.item(i));
//alert(childItem.getAttribute('href'));
var theURL = childItem.getAttribute('href');
//alert(childItem.firstChild.nodeValue)
var theTarget= childItem.firstChild.nodeValue
//Create a link item and put it in a paragraph
var linkPara=document.createElement('p');
linkPara.className='submenu_element';
var link = document.createElement('a')
link.setAttribute('href', theURL)
link.setAttribute('class','submenu_element' )
var textual_data = document.createTextNode(theTarget);
link.appendChild(textual_data);
linkPara.appendChild(link);
theDiv.appendChild(linkPara);
}
}//end For
if (manipulationClosure == null ){
return theDiv;
}else{
return manipulationClosure(theDiv)
}
}
/*
This function receives a Javascript "Element" object
*/
function createNavBlock(aBlock){
// This might seem obvious, but this is an HTMLDivElement in
// the API references
var constructedBlock = document.createElement('div');
constructedBlock.className="navigation_block";
constructedBlock.id=aBlock.getAttribute('name');
constructedBlock.setAttribute('id',aBlock.getAttribute('name'));
if ( false ){
alert('Constructed Block Object: ' + constructedBlock + "\n" +
'Class Name: ' + constructedBlock.className + "\n" +
'ID: ' + constructedBlock.id);
}
return constructedBlock;
}
/*
This function creates the main navigational blocks to the left of the
screen.
It also seeds them with the callback function that is used to extract
the fuller drop-down
*/
function createNavMenu(){
var array_of_blocks = xmlDoc.getElementsByTagName('block');
var nav_section = document.getElementById('navigation_bar');
for (var i=0; i<array_of_blocks.length; i++){
var navBlock = createNavBlock(array_of_blocks[i]);
if ( false ){
alert('Constructed Block Object: ' + navBlock + "\n" +
'Class Name: ' + navBlock.className + "\n" +
'ID: ' + navBlock.id);
} //end if
navBlock = addAnEvent(navBlock, 'dblclick', eventHandler, 'false');
navBlock = appendTitleBlock(navBlock, array_of_blocks[i].getAttribute('displaytitle'));
nav_section.appendChild( navBlock );
}// end for
}
function eventHandler(anEvent){
var myEvent = anEvent ? anEvent : window.event;
var thisName = this.id ? this.id : window.event.srcElement.id;
/* Due to a bug in IE's rendering of CSS, it's possible that someone could click
on the title of a box ( even though they're clicking outside of the title )
and thus a 'null' value for thisName will be, quite legally, derived. To
catch this we send return, the user will assume they didn't dblclick properly
*/
if (thisName == ""){
return
}
if (false){
alert ('howdy '+anEvent
+ "\n and..." + this
+ "\n and..." + thisName);
}//end debugging truefalse
if ( thisName == null ){
return
}
var screenBlock = document.getElementById(thisName);
if ( isExpanded(screenBlock) ){
//alert('expanded');
//alert(screenBlock.getElementsByTagName('div')[0])
screenBlock.removeChild(screenBlock.getElementsByTagName('div')[0])
}else{
//alert('not expanded');
var informationBlock = findBlockWithName(thisName);
buildSubmenuFor(informationBlock);
screenBlock.appendChild(buildSubmenuFor(informationBlock, function (aDiv)
{
aDiv.className="navigation_block_submenu";
aDiv.setAttribute('id',thisName + '_submenu');
return aDiv
}));
}
}
function findBlockWithName(aName){
//alert(aName);
var array_of_blocks = xmlDoc.getElementsByTagName('block');
for (var i=0; i<array_of_blocks.length; i++){
// alert('! ' + array_of_blocks[i].getAttribute('name'));
if (array_of_blocks[i].getAttribute('name')==aName){
return array_of_blocks[i];
}
}
}
function importXML(){
if (document.implementation && document.implementation.createDocument) {
xmlDoc = document.implementation.createDocument("", "", null);
xmlDoc.onload = createNavMenu ;
}else if (window.ActiveXObject){
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.onreadystatechange = function () {
if (xmlDoc.readyState == 4) createNavMenu ()
};
}else{
return;
}
xmlDoc.load("navigation.xml");
}
function isExpanded (blockToExamine){
//alert (blockToExamine);
var cNodes =blockToExamine.childNodes;
for (var i=0; i<cNodes.length; i++){
if (cNodes.item(i).tagName == 'DIV'){
return true;
}
}
return false;
}
function pageLoad(){
browser_mutate();
importXML();
}
And lastly here’s the XML file:
<blocks>
<block name="aggregator_block" displaytitle="Aggregator Sites">
<name href="http://www.reddit.com">Reddit</name>
<name href="http://www.digg.com">Digg</name>
</block>
<block name="timewaster_block" displaytitle="Timewasters">
<name href="http://www.youtube.com">Youtube</name>
<name href="http://www.socialitelife.com">Socialite's Life</name>
</block>
</blocks>