As with any type of programming, writing bug-free, efficient scripts that meet your expectations takes a bit of work. The following sections provide some tips and hints to make that work take less time and go more smoothly.
Your scripts should always check the version of the client browser so your content will degrade gracefully if the browser does not support the new features of Microsoft® Internet Explorer version 4 or later. The easiest way to identify whether the browser is Internet Explorer and determine its version number is to use the following JScript® (compatible with ECMA 262 language specification) function:
function msieversion() // Return Microsoft Internet Explorer (major) version number, or 0 for others. // This function works by finding the "MSIE " string and extracting the version number // following the space, up to the decimal point for the minor version, which is ignored. { var ua = window.navigator.userAgent var msie = ua.indexOf ( "MSIE " ) if ( msie > 0 ) // is Microsoft Internet Explorer; return version number return parseInt ( ua.substring ( msie+5, ua.indexOf ( ".", msie ) ) ) else return 0 // is other browser }
This function runs on most browsers and returns either the major version number for any Microsoft Internet Explorer browser or zero for all other browsers. Use of this function ensures that the script will be compatible with future versions of Internet Explorer.
Scripts should always check version numbers greater than or equal to a target version so as to be compatible with future versions. Existing scripts that check for userAgent equal to "MSIE 3" should be changed to check the version correctly so that these scripts will recognize Internet Explorer 4.0, which supports all of the functionality of the previous version.
The following example shows how to correctly check the client browser version.
if ( msieversion() >= 4 ) document.alert( "This is IE4 or later - safe to use all IE4 features" ) else if ( msieversion() >= 3 ) document.alert( "This is IE3 - safe to use ActiveX" ) else document.alert( "This is not IE" )
The following HTML example shows a common scripting mistake related to event handling and canceling the default action.
<HTML> <HEAD><TITLE>Canceling the Default Action</TITLE> <SCRIPT LANGUAGE= "JScript"> function askConfirm() { return window.confirm ("Choose OK to follow hyperlink, Cancel to not.") } </SCRIPT> <BODYonload="b3.onclick=askConfirm"> <!-- Try links with different hookups - should be canceled by "Cancel" to confirm dialog. --> <BR><A NAME=b1 HREF="http://www.microsoft.com" onclick="askConfirm()">1 Without return (won't work)</A> <BR><A NAME=b2 HREF="http://www.microsoft.com" onclick="return askConfirm()">2 With return (works)</A> <BR><A NAME=b3 HREF="http://www.microsoft.com">3 Function pointer (works)</A> </BODY> </HTML>
The first A element in this example does not work properly. Without the return in the onclick JScript expression, the browser interprets the function expression, throws away the resulting value, and leaves the default action unaffected.
The other A elements correctly bind the return value to the event, hence the default action can be canceled when false is returned.
You can prevent a document from being cached by adding the following META tag to the document.
<META HTTP-EQUIV="Expires" CONTENT="0">
Preventing the document from being cached ensures that a fresh copy of the document will always be retrieved from the site, even during the user's current session, regardless of how the user has set the browser's caching options. This is useful if the content of the document changes frequently.
Objects are ActiveX® Controls or other similar components that provide custom capabilities and services for HTML documents. You can add a control to your document by using the OBJECT element, and you can gain access to the capabilities and services of the control by using its properties and methods from script.
When using objects, be aware that Dynamic HTML extends every object by providing these additional properties:
align | classid | code |
codeBase | codeType | |
data | form | |
height | name | |
object | recordset | |
type | width |
If a control has properties with these same names, you will not be able to access the properties unless you preface the name with the object property. For example, assume that an ActiveX control is added to the document by using the following:
<OBJECT ID="MyControl" HEIGHT=100 WIDTH=200 CLASSID="clsid: ... "> </PARAM NAME="width" VALUE="400"> </OBJECT>
In this example, there are two widths: an extended property set within the OBJECT element, and a property belonging to the control that is set using the PARAM element. To access these from script, you use the following code:
alert(MyControl.width); // this is Dynamic HTML's property; displays "200" alert(MyControl.object.width); // this is the object's property; displays "400"
Dynamic HTML provides everything you need to generate animated effects without resorting to custom controls. For example, consider the following script, which is a replacement for the Path control.
var tickDuration; tickDuration = 50; var activeObjectCount; var activeObjects; var itemDeactivated; var tickGeneration; activeObjects = new Array(); activeObjectCount = 0; timerRefcount = 0; itemDeactivated = false; tickGeneration = 0; function initializePath(e) { e.waypointX = new Array(); e.waypointY = new Array(); e.duration = new Array(); } function addWaypoint(e, number, x, y, duration) { e.waypointX[number] = x; e.waypointY[number] = y; e.duration[number] = duration; } function compact() { var i, n, c; n = new Array(); c = 0; itemDeactivated = false; for (i=0; i<activeObjectCount; i++) { if (activeObjects[i].active == true) { n[c] = activeObjects[i]; c++; } } activeObjects = n; activeObjectCount = c; } function tick(generation) { if (generation < tickGeneration) { // alert("Error "+generation); return; } //alert("tick: "+generation); if (itemDeactivated) compact(); if (activeObjectCount == 0) { return; } else { for (i=0; i<activeObjectCount; i++) { moveElement(activeObjects[i]); } window.setTimeout("tick("+generation+");", tickDuration); } } function start(e) { if (itemDeactivated) compact(); activeObjects[activeObjectCount] = e; activeObjectCount++; if (activeObjectCount == 1) { tickGeneration++; tick(tickGeneration); } } function runWaypoint(e, startPoint, endPoint) { var startX, startY, endX, endY, duration; if (e.waypointX == null) return; startX = e.waypointX[startPoint]; startY = e.waypointY[startPoint]; endX = e.waypointX[endPoint]; endY = e.waypointY[endPoint]; duration = e.duration[endPoint]; e.ticks = duration / tickDuration; e.endPoint = endPoint; e.active = true; e.currTick = 0; e.dx = (endX - startX) / e.ticks; e.dy = (endY - startY) / e.ticks; e.style.posLeft = startX; e.style.posTop = startY; start(e); } function moveElement(e) { e.style.posLeft += e.dx; e.style.posTop += e.dy; e.currTick++; if (e.currTick > e.ticks) { e.active = false; itemDeactivated = true; if (e.onpathcomplete != null) { window.pathElement = e; e.onpathcomplete() } } }
To use this script in your document, do the following:
The following sample document show how this works.
<html> <body> <div id=Item1 style="position: absolute; left: 0; top: 0;">Item1</div> <div id=Item2 style="position: absolute; left: 0; top: 0;">Item2</div> <div id=Item3 style="position: absolute; left: 0; top: 0;">Item3</div> <div id=Item4 style="position: absolute; left: 0; top: 0;">Item4</div> <div id=Item5 style="position: absolute; left: 0; top: 0;">Item5</div> <div id=Item6 style="position: absolute; left: 0; top: 0;">Item6</div> <input type=button value="Start" onclick="runWaypoint(Item1, 0, 1); runWaypoint(Item2, 0, 1);"> <div id=Debug>Generation</div> <script src="htmlpath.js"> </script> <script> // need to call initializePath on all objects that will be moved with this mechanism initializePath(Item1); initializePath(Item2); initializePath(Item3); initializePath(Item4); initializePath(Item5); initializePath(Item6); // the 0th waypoint is the intial position for waypoint #1 // syntax is item, waypoint, endx, endy, duration in msecs addWaypoint(Item1, 0, 0, 0, 0); addWaypoint(Item1, 1, 200, 200, 2000); addWaypoint(Item2, 0, 100, 100, 0); addWaypoint(Item2, 1, 400, 100, 4000); addWaypoint(Item3, 0, 400, 400, 0); addWaypoint(Item3, 1, 200, 100, 1000); addWaypoint(Item4, 0, 0, 0, 0); addWaypoint(Item4, 1, 200, 200, 2000); addWaypoint(Item5, 0, 100, 100, 0); addWaypoint(Item5, 1, 400, 100, 4000); addWaypoint(Item6, 0, 400, 400, 0); addWaypoint(Item6, 1, 200, 100, 1000); function endfunction() { // syntax for runWaypoint is Item, start point, end point runWaypoint(Item3, 0, 1); runWaypoint(Item4, 0, 1); runWaypoint(Item5, 0, 1); runWaypoint(Item6, 0, 1); } function endfunction2() { runWaypoint(Item1, 0, 1); } Item1.onpathcomplete = endfunction; Item6.onpathcomplete = endfunction2; </script> </body> </html>