BIS Lightweight Application Development Engine

DHTML Guide


Introduction

This is a guide to Dynamic HTML using the BIS Lighweight Application Development Engine. It assumes you have background knowledge in BLADE, CSS, and Object-Oriented Javascript. If you already know DHTML, it will probably be mostly review.

DHTML

Dynamic HTML, or DHTML, isn't a single technology, protocol, or language. Its a term loosely used to refer to manipulating elements on a web page. In modern browsers, this is generally done by using javascript interfaces to CSS and DOM to create new elements and manipulate the properties of existing ones. Althought his can be done with other languages such as vbscript, this document focuses on javascript, CSS, and DOM.

Javascript DOM

The first step to manipulating elements on a web page is to get a javascript object representing those elements. This can be done using Javascript's document object's getElementById function. For example, if you have a div tag with id "div1", you can access it with document.getElementById('div1'). To save you some typing, blade also has a built-in function called elt, which does the same thing: elt('div1')

Once we have the object, we can manipulate it. A lot of what we need to manipulate lies in the object's style array, which holds the element's CSS attributes. For example, to change the font color of div1, we can do elt("div1").style.color = "red". One thing to keep in mind is that javascript does not like -'s in variable names, which creates some conflicts with CSS. Sometimes one js attribute covers two css attributes, e.g. background covers both background-color and background-image.

Javascript's DOM interface can also be used to create objects and change the object hierarchy of a page. The following code should speak for itself:

var elem = document.createElement("img");
elem.src = "images/1px.gif";
elem.style.visibility = "hidden";
document.getElementById("div1").appendChild(elem);
Elements on an HTML page also support functions such as removeChild() and replaceChild().

Showing and Hiding with CSS

Unless content is being dynamically created on the fly, its generally best to avoid using appendChild to make elements appear as if they are being inserted and removed from the page. Instead, we can use the two CSS attributes display and visibility. Visibility controls whether or not an object is visible on the page - it can be "visible" or "hidden". When an element is "hidden", the rest of the page is still formatted as if that element were there. Display controls how an element is actually laid out on the page. Setting display to "none" actually reformats the page as if the element were not there. Setting display to "inline" causes the page to be formatted as if the element were inline text. To show and hide rows in etables, we need to set display to "table-row".

This is, unfortunately the ideal view of the world. IE handles display and visibility in a slightly nonstandard way. There is also a third state for visibility, that of "collapse", which sounds like it should fit the same role as display="none". However, IE does not support it. Additionally, IE only supports setting display to "none" and "inline". When setting a tr tag to inline, IE still formats it like a table row. Netscape, however, formats it like inline text and the data appears merged together and not as separate columns.

BLADE addresses this through several convenience functions: show(), hide(), insert(), and remove(). All the functions take an id as their first argument. Insert also takes an optional array of css styles to try. Show and hide control the visibility of an element, while insert and remove control the display attribute. When inserting, the optional array of css styles is a list of styles to try. Insert tries setting display to be the first value of the array. If an exception is thrown, it moves onto the second value, and so on. If no array is given, it tries to set display="inline". To get netscape compatability, an etable does the following when trying to expand a row:

insert(id, new Array('table-row', 'inline'))
On Netscape browsers, display="table-row" works fine and the insert routine exits. On IE, display="table-row" throws an exception, and the insert routine sets display="inline".

CSS Positioning

Generally, we can control how an element is positioned on the page by using its left and top CSS attributes. These set the element's left and top sides relative to some reference position. The location of this reference position is determined by the position attribute, which can be either absolute or relative. A relative position positions the element relative to where it would otherwise be positioned on the page. For example, position: relative; top: + 5 looks like this. Using an absolute position positions an element relative to its container. By default, this container is the page itself, so a span with position: absolute; left: 50px; top: 100px should show up at 50px over and 100px down from the top left of the page. Click Here to see (will require you to scroll back up to the top of the page).


But what if we don't want to position absolute with respect to the page, but with respect to a different point? Make one of the parent elements have a position attribute of relative. The absolute elements will now be positioned absolute within the parent container that has a position of relative. As an example, we might want a "sandbox" somewhere on the page that we can place other elements in. Below, the box represents a div with position relative. The two text elements are divs with a position of absolute. If you set the divs to a position of relative, the second div will drop down because it would ordinarily be laid out below the first div.
left: 100
left: 200


One more thing: using CSS positioning, we can cause elements to overlap. In the event that they overlap, an element's z-index determines which one is displayed on top of the other. The z-index is an arbitrary number, and can be thought to represent the element's position along the 3rd dimension z-axis. Elements are normally rendered on the XY plane (z-index 0). higher z-indexes are displayed on top of lower z-indexes.

BLADE Support for CSS Positioning

Positiong objects with CSS can sometimes be messy. For example, creating DHTML popup windows that don't affect the layout of a page requires an outer div of position=relative and an inner div of position=absolute. BLADE has two tags to help with this, the absolute and relative tags. They are:

<absolute location="x,y" z="zindex" id="id"/>
<relative location="x,y" z="zindex" id="id"/>
The absolute tag creates a div with position=absolute, left=x, top=y, z-index=zindex. It can be used to quickly position popups or blocks of text over parts of the document. The relative tag is designed for DHTML popups which need to occur next to a certain element. Positioning the popups relative to the page itself wouldn't work unless everything on the page were fixed-width. The relative tag creates a relative div containing an absolute div, so that the popup occurs at the exact spot on the page where the relative tag is placed, but does not affect page layout.

Basic Animations

When the browser loads a page, javascript is executed as it is read, and the browser waits for it to finish executing before moving on. So an infinite for loop in a script tag halfway down the page will prevent the page from rendering properly. Additionally, you can't use document.getElementById to get elements which havent' been rendered yet. This can be tricky for BLADE widgets like the tabstrip, since when the tabstrip is created it has no way of knowing when the tab bodies will be created. A solution to the second problem is to use the BLADE Page object. When building a page using XSL/XML, BLADE automatically creates a single instance of the Page obect, called page. the page object has an arry called onLoad, which holds functions to be executed when the page starts up. To add an anonymous function, we could do:

page.onLoad.push(function() { alert("page loaded")});

The core BLADE components also include an animation package to help with a few tricks. For example, there is a red div below named divred. When you click the button, the following code executes:

trans = new blade.core.animation.Translation(elt("divred"), new blade.core.screen.Coordinate(1,1), 200, 20);
scale = new blade.core.animation.Scaling(elt("divred"), new blade.core.screen.Coordinate(1,1), 200, 20);
trans.begin();
scale.begin();


Event Handling

IE and Netscape, unfortunately, have different ways of handling events, although they are similar to the way any programmer is used to writing event handlers. You attach an event handler to any object on the page or to the document itself. When the event handler is called, it executes, tells the browser whether to keep calling other event handlers or stop the event handling chain, and returns.

Unfortunately, IE's event model is slightly different from the W3C event model that Netscape implements. Under the W3C model, the event handler is passed an event object. In IE, the event handler gets the event object from the window.event variable. Additionally, parameters and values of the objects vary (IE has a relatedTarget, W3C has a fromEvent and a toEvent, even though typically only one is populated/relevant).

To get around this, BLADE has a compatibility layer which consists of an event handler wrapper, a cross-platform event object supporting the overlapping feature set between IE and W3C, and a CSS Position wrapper to make it easier to read and set an element's left and top attributes. An example:

blade.xbrowser.events.addListenerFunction(elt("eventhandle"), "click", function(event) { alert("you clicked me with " + event.button)});

my id is eventhandle - click me!

Related to event handling, here's a taste of how to use BLADE's drag-and-drop javascript library:

<div style="position: relative; width: 252px; height: 252px; border-color: black; border-style: solid; border-width: 1px">
 <div style="position: absolute; left: 0px; top: 0px; width: 50px; height: 50px; background-color: orange" id="dd">
 </div>
</div>

<script>
 // get element reference
 var elem = document.getElementById("dd");
 // create a new draggable, drag on mousedown, release on mouseup, use crossplatformwrapper defined above
 var dd = new blade.core.dragdrop.Draggable(elem, "mousedown", "mouseup");
 // limit the draggable's travel inside its container to (0, 0) and (200, 200)
 dd.setClipRegion(new bldae.core.screen.ClipRegion(0, 0, 200, 200));
</script>