Section V.5. Common Positioning Tasks


V.5. Common Positioning Tasks

I'll conclude with examples of two common positioning tasks: centering objects and flying objects. A third task, user-controlled dragging of objects, is kept on hold until Online Section VI, where we discuss browser event models. All of these tasks rely on the DHTML API from Example V-1.

V.5.1. Centering an Object

The common way to center an element within a rectangle is to calculate the half-way point along each axis for both the element and its containing rectangle (positioning context). Then, subtract the element value from the container value for each axis. The resulting values are the coordinates for the top and left edges of the element that center the element.

The element being centered in the browser window is a div element with a yellow background and one word of large-sized red text. The goal is to center the div element both horizontally and vertically in the browser window, bringing the contained paragraph along for the ride. Example V-2 shows the complete page listing, which is backward compatible to Version 4 browsers.

Example V-2. A page that centers an element upon loading

 <html> <head> <style type="text/css"> #banner {     position: absolute;     visibility: hidden;     left: 0;     top: 0;     background-color: yellow;     font-size: 36pt;     color: red; } </style> <script language="JavaScript" type="text/javascript" src="/books/2/570/1/html/2/DHTML3API.js"></script> <script language="JavaScript" type="text/javascript"> // Global 'corrector' for IE/Mac et al., but doesn't hurt others var fudgeFactor = {top:-1, left:-1}; // Center a positionable element whose name is passed as // a parameter in the current window/frame, and show it function centerIt(layerName) {     // 'obj' is the positionable object     var obj = DHTMLAPI.getRawObject(layerName);     // set fudgeFactor values only first time     if (fudgeFactor.top == -1) {         if ((typeof obj.offsetTop == "number") && obj.offsetTop > 0) {             fudgeFactor.top = obj.offsetTop;             fudgeFactor.left = obj.offsetLeft;         } else {             fudgeFactor.top = 0;             fudgeFactor.left = 0;         }         if (obj.offsetWidth && obj.scrollWidth) {             if (obj.offsetWidth != obj.scrollWidth) {                 obj.style.width = obj.scrollWidth;             }         }     }     var x = Math.round((DHTMLAPI.getInsideWindowWidth( )/2) -         (DHTMLAPI.getElementWidth(obj)/2));     var y = Math.round((DHTMLAPI.getInsideWindowHeight( )/2) -         (DHTMLAPI.getElementHeight(obj)/2));     DHTMLAPI.moveTo(obj, x - fudgeFactor.left, y - fudgeFactor.top);     DHTMLAPI.show(obj); } // Special handling for CSS-P redraw bug in Navigator 4 function handleResize( ) {     if (DHTMLAPI.browserClass.isNN4) {         // causes extra re-draw, but gotta do it to get banner object color drawn         location.reload( );     } else {         centerIt("banner");     } } function init( ) {     DHTMLAPI.init( );     centerIt("banner"); } window.onresize = handleResize; window.onload = init; </script> </head> <body> <div >Congratulations!</div> </body> </html> 

No matter what size the browser window is initially, or how the user resizes the window, the element always positions itself dead center in the window space. Notice that the positionable element is initially loaded as a hidden element positioned at 0,0. This allows a script (triggered by the onload event handler) to use a known reference point to determine the current height and width of the content, based on how each browser (and operating system) calculates its fonts (initial width and height are arbitrarily set to 1). This is preferable to hardwiring the height and width of the element, because the script is not dependent on the precise text size.

The centerIt( ) function begins by getting a valid reference to the positioned element whose ID is passed as an argument. Some initial activity works with the element object itself, rather than its style property. Hence the use of DHTMLAPI.getRawObject( ) from the DHTML API (Example V-1) to acquire that reference.

Next comes a workaround for an unfortunate implementation bug in IE 5 for the Macintosh. The crux of the bug is that the browser assigns an incorrect default value to a positioned element's offsetTop propertysomething other than the zero it should be. When it comes time to position the element, the script must take this "fudge factor" into account. While we're at it, we should make it a generalizable workaround, in case future (or other) browsers have this problem not only for the vertical measure, but horizontal, as well. The script initializes an object (fudgeFactor) with two properties set to -1. These values act as flags of their own, indicating that the object has not yet had its values set algorithmically. In the centerIt( ) function, if the values are still their original -1, the object properties are set to the offsetTop and offsetLeft properties of an element whose values are greater than the desired zero. Otherwise, the object values are set to 0. It's important to set these values only once, when the page loads. Because this function can be called later if the user resizes the window, the script must make use of the first set of calculated values.

One more one-time-only activity (controlled by the fudgeFactor.top==-1 condition) affects only IE 4, which automatically sizes positioned elements to the full width of the body. Later browsers correctly apply the CSS box model to elements, restricting their default widths to the space needed for the content. IE 4 provides a property for the needed space (the scrollWidth property). If scrollWidth is not the same as the reported offsetWidth of the element (the actual rendered width), the element's style.width gets set to its scrollWidth value.

The balance of the centerIt( ) function calculates the coordinates for centering the element within the window, based on current sizes. A call to the API's DHTMLAPI.moveTo( ) function (correcting for the fudgeFactor, which for most browsers is 0) puts the element into position. Then DHTMLAPI.show( ) puts it into view.

An onresize event handler invokes the handleResize( ) function whenever the browser window changes its size (although the event is not supported in Opera 5). For most browsers, another call to centerIt( ) is sufficient. For Navigator 4, however, the lack of automatic page reflow requires a document reload, in which case the onload event handler runs again, eventually invoking centerIt( ).

Many of the concepts shown in Example V-2 can be extended to centering nested elements inside other elements. The primary differences involve replacing the document's positioning context with that of the centered element's container.

V.5.2. Flying Objects

Moving element objects around the screen is one of the features that can make Dynamic HTML pay off for your pageprovided you use the animation to add value to the presentation. Gratuitous animation (like the example in this section) more often annoys frequent visitors than it helps convey information. Still, I'm sure you are interested to know how animation tricks are performed with DHTML, including cross-platform deployment.

The straight-line path example in this section builds somewhat on the centering application in Example V-2. The goal of this demonstration is to have a banner object fly in from the right edge of the window (centered vertically in the window), until it reaches the center of the currently sized window. The source code for the page is shown in Example V-3.

Example V-3. A page with a "flying" banner

 <html> <head> <style type="text/css"> body {overflow:hidden} #banner {     position: absolute;     visibility: hidden;     left: 0;     top: 0;     background-color: yellow;     font-size: 36pt;     color: red; } </style> <script language="JavaScript" type="text/javascript" src="/books/2/570/1/html/2/DHTML3API.js"></script> <script language="JavaScript" type="text/javascript"> // ** Global variables ** // // Final left position of gliding element var stopPoint = 0; // Repetition interval ID var intervalID; // 'Corrector' positioning factor for IE/Mac et al., but doesn't hurt others var fudgeFactor = {top:-1, left:-1}; // Set initial position offscreen and show object and // start timer by calling glideToCenter( ) function startGlide(layerName) {     // 'elem' is the positionable object     var elem = DHTMLAPI.getRawObject(layerName);     // set fudgeFactor values only first time     if (fudgeFactor.top == -1) {         if ((typeof elem.offsetTop == "number") && elem.offsetTop > 0) {             fudgeFactor.top = elem.offsetTop;             fudgeFactor.left = elem.offsetLeft;         } else {             fudgeFactor.top = 0;             fudgeFactor.left = 0;         }         if (elem.offsetWidth && elem.scrollWidth) {             if (elem.offsetWidth != elem.scrollWidth) {                 elem.style.width = elem.scrollWidth;             }         }     }     var y = Math.round((DHTMLAPI.getInsideWindowHeight( )/2) -         (DHTMLAPI.getElementHeight(elem)/2));     stopPoint = Math.round((DHTMLAPI.getInsideWindowWidth( )/2) -         (DHTMLAPI.getElementWidth(elem)/2));     DHTMLAPI.moveTo(elem, DHTMLAPI.getInsideWindowWidth( ), y - fudgeFactor.top);     DHTMLAPI.show(elem);     intervalID = setInterval("glideToCenter('" + layerName + "')", 1); } // Move the object to the left by 5 pixels until it's centered function glideToCenter(layerName) {     var elem = DHTMLAPI.getRawObject(layerName);     DHTMLAPI.moveBy(elem,-5,0);     if (DHTMLAPI.getElementLeft(elem) <= stopPoint) {         clearInterval(intervalID);     } } function init( ) {     DHTMLAPI.init( );     startGlide("banner"); } window.onload = init; </script> </head> <body> <span >Congratulations!</span> </body> </html> 

The setup script in Example V-3 (the startGlide( ) function) borrows a great deal from the centerIt( ) function of Example V-2. One difference is that startGlide( ) establishes an end point along the x-axis at which the glide is to stop, given the current window size. The DHTMLAPI.moveTo( ) function positions the element just out of view to the right. Then the script invokes the glideToCenter( ) function, which performs the animation.

Repetitive motion is best controlled via the JavaScript setInterval( ) method, which continues to invoke a function (at a designated time interval in milliseconds) until a clearInterval( ) method stops the merry-go-round. The final script statement of startGlide( ) invokes the glideToCenter( ) function via setInterval( ). Each millisecond (or as quickly as the rendering engine allows), the browser invokes the glideToCenter( ) function and refreshes its display.

Each time glideToCenter( ) runs, it shifts the banner object to the left by five pixels without adjusting the vertical position. Then it checks whether the left edge of the banner has arrived at the position where the banner is centered on the screen. If it is at (or to the left of) that point, the internal timer associated with the interval ID stops and the browser ceases to invoke glideToCenter( ) anymore.

Unlike Example V-2, this one does not operate in IE 4 if you place the CSS rules in the head element, as shown in Example V-3. That's because IE 4 does not offer a way to read computed CSS values unless the rules are inserted within the element's tag as a style attribute. IE 5 and later provide the DHTMLAPI with the currentStyle object to retrieve the desired values.

If you want to move an element along a more complicated path, the strategy is similar, but you have to maintain one or more additional global variables to store loop counters or other values that change from point to point. To avoid mucking up your global space with extra variables, you can, instead, encapsulate the operation inside a custom JavaScript object. In fact, this is the avenue to follow if you want to have multiple moving objects. Example V-4 shows an object-based implementation for moving elements along a circular path. For each element you wish to send around the circle, create an instance of the circler object (passing the ID of the element to be moved), and invoke its startRoll( ) method. You can apply all sorts of motion formulas to this kind of DHTML controller and extend the parameters of a new instance to include values for initial position and diameter of the circle.

Example V-4. Rolling a banner in a circle

 <html> <head> <style type="text/css"> #banner {     position: absolute;     visibility: hidden;     left: 0;     top: 0;     background-color: yellow;     font-size: 36pt;     color: red; } </style> <script language="JavaScript" type="text/javascript" src="/books/2/570/1/html/2/DHTML3API.js"></script> <script language="JavaScript" type="text/javascript"> function circler(elemRef) {     var me = this;     this.intervalCount = 1;     this.intervalID;     this.fudgeFactor = {top:-1, left:-1};     this.elem = DHTMLAPI.getRawObject(elemRef);     this.start = function (layerName) {         // set fudgeFactor values only first time         if (this.fudgeFactor.top == -1) {             if ((typeof this.elem.offsetTop == "number") && this.elem.offsetTop > 0) {                 this.fudgeFactor.top = this.elem.offsetTop;                 this.fudgeFactor.left = this.elem.offsetLeft;             } else {                 this.fudgeFactor.top = 0;                 this.fudgeFactor.left = 0;             }             if (this.elem.offsetWidth && this.elem.scrollWidth) {                 if (this.elem.offsetWidth != this.elem.scrollWidth) {                     this.elem.style.width = this.elem.scrollWidth;                 }             }         }         this.x = Math.round((DHTMLAPI.getInsideWindowWidth( )/2) -             (DHTMLAPI.getElementWidth(this.elem)/2));         this.y = 50;         DHTMLAPI.moveTo(this.elem, this.x - this.fudgeFactor.left, this.y - this.fudgeFactor.top);         DHTMLAPI.show(this.elem);         this.intervalID = setInterval(function( ) {me.goAround( )}, 1);     }     this.goAround = function ( ) {         if (DHTMLAPI.getElementLeft(this.elem)) {             this.x = Math.round(DHTMLAPI.getElementLeft(this.elem) + Math.cos(this.intervalCount *                 (Math.PI/18)) * 10);             this.y = Math.round(DHTMLAPI.getElementTop(this.elem) + Math.sin(this.intervalCount *                 (Math.PI/18)) * 10);             DHTMLAPI.moveTo(this.elem, this.x - this.fudgeFactor.left, this.y -                 this.fudgeFactor.top);             if (this.intervalCount++ == 36) {                 clearInterval(this.intervalID);             }         } else {             clearInterval(this.intervalID);         }     } } function init( ) {     DHTMLAPI.init( );     var bannerCircle = new circler("banner");     bannerCircle.start( ); } window.onload = init; </script> </head> <body> <span >Congratulations!</span> </body> </html> 

Online Section VI has further examples of dynamic positioning of elements and examine how to make an object track the mouse pointer. That application requires knowledge of the partially conflicting event models built into Internet Explorer and the W3C DOM browsers.




Dynamic HTML. The Definitive Reference
Dynamic HTML: The Definitive Reference
ISBN: 0596527403
EAN: 2147483647
Year: 2004
Pages: 120
Authors: Danny Goodman

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net