How to do the impossible with Google Gears
Multi-dialogue forms on one page
Turning Tables Into Selection Lists
You are here: irt.org | Articles | Dynamic HTML (DHTML) | Dynamic Floating Tool Tips [ previous next ]
Published on: Sunday 23rd January 2000 By: Martin Webb
After a short absence I return with an article on DHTML. This particular article will attempt to solve one of the most annoying problems that developers come across when developing with DHTML - How to place a dynamic (visible/invisible) layer relative to a table cell, or, how to position an absolute layer relatively.
Why an absolute layer? So that we can hide an reveal the layer at will - perhaps as a tool tip.
In MSIE4 this problem is fairly easy, you just use a relative layer, if it happens to be within a table cell, then MSIE4 includes the layer within the table cell. NN4, however, does mot. The rest of this article will step by step build a solution that will work on both NN4 and MSIE4+, and result in a final example that shows intelligent tool tips that detect the window edges and adjust accordingly.
The following style sheet definitions are used throughout the examples used in this article:
<style type="text/css"><!-- .red { color:#ff0000; } .white { color:#ffffff; } .relative { position:relative; visibility:hidden; } .absolute { position:absolute; visibility:hidden; } .redtable { background-color:#ff0000; cell-spacing:0; } //--></style>
The following first few examples make use of the default show() and hide() functions for displaying and hiding a layer:
<script language="JavaScript"><!-- function show(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'visible'; else if (document.all) document.all[object].style.visibility = 'visible'; } function hide(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'hidden'; else if (document.all) document.all[object].style.visibility = 'hidden'; } //--></script>
The first example uses a relatively positioned layer which reserves room within the normal flow of the document - too much room:
The code:
<hr> <p> blah blah <a href="nextpage.htm" onMouseover="show('myLayer1')" onMouseout="hide('myLayer1')">example 1</a> <span id="myLayer1" class="relative"> <p class="red">Some text within a layer</p> </span> blah blah </p> <hr>
To see the effect, hover the mouse pointer over the text link::
blah blah
example 1
Some text within a layer
The alternative is to use an absolutely positioned layer which reserves no room at all, but when created using the DIV tags, cause line breaks before and after the layer, as in the second example:
The code:
<script language="JavaScript"><!-- function show(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'visible'; else if (document.all) document.all[object].style.visibility = 'visible'; } function hide(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'hidden'; else if (document.all) document.all[object].style.visibility = 'hidden'; } //--></script> <hr> <p> blah blah <a href="nextpage.htm" onMouseover="show('myLayer2')" onMouseout="hide('myLayer2')">example 2</a> <div id="myLayer2" class="absolute"> <p class="red">Some text within a layer</p> </div> blah blah </p> <hr>
The effect:
blah blah example 2
Some text within a layer
The third example uses the SPAN tags which avoid the line breaks, however, the layer appears following the link:
The code:
<script language="JavaScript"><!-- function show(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'visible'; else if (document.all) document.all[object].style.visibility = 'visible'; } function hide(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'hidden'; else if (document.all) document.all[object].style.visibility = 'hidden'; } //--></script> <hr> <p> blah blah <a href="nextpage.htm" onMouseover="show('myLayer3')" onMouseout="hide('myLayer3')">example 3</a> <span id="myLayer3" class="absolute"> <p class="red">Some text within a layer</p> </span> blah blah </p> <hr>
The effect:
blah blah
example 3
Some text within a layer
The fourth example positions the SPAN tags prior to the link, this causes the layer to float above the link - better, but not perfect as sometimes the layer flickers into and out of existence:
The code:
<script language="JavaScript"><!-- function show(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'visible'; else if (document.all) document.all[object].style.visibility = 'visible'; } function hide(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'hidden'; else if (document.all) document.all[object].style.visibility = 'hidden'; } //--></script> <hr> <p> blah blah <span id="myLayer4" class="absolute"> <p class="red">Some text within a layer</p> </span> <a href="nextpage.htm" onMouseover="show('myLayer4')" onMouseout="hide('myLayer4')">example 4</a> blah blah </p> <hr>
The effect:
blah blah
Some text within a layer
The fifth example adjusts the position of the layer by the use of a user controlled offset. Instead if using the default show() function we'll now use the new showOffset() function:
The code:
<script language="JavaScript"><!-- function showOffset(object,x,y) { if (document.layers && document.layers[object]) { document.layers[object].left += x; document.layers[object].top += y; document.layers[object].visibility = 'visible'; } else if (document.all) { document.all[object].style.posLeft = document.all[object].offsetLeft + x; document.all[object].style.posTop = document.all[object].offsetTop + y; document.all[object].style.visibility = 'visible'; } } function hide(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'hidden'; else if (document.all) document.all[object].style.visibility = 'hidden'; } //--></script> <hr> <p> blah blah <span id="myLayer5" class="absolute"> <p class="red">Some text within a layer</p> </span> <a href="nextpage.htm" onMouseover="showOffset('myLayer5',0,15)" onMouseout="hide('myLayer5')">example 5</a> blah blah </p> <hr>
The effect:
blah blah
Some text within a layer
However this offset is repeatedly added, causing the layer to move further away each time, the answer is to remove the offset when the layer is hidden:
The code:
<script language="JavaScript"><!-- function showOffset(object,x,y) { if (document.layers && document.layers[object]) { document.layers[object].left += x; document.layers[object].top += y; document.layers[object].visibility = 'visible'; } else if (document.all) { document.all[object].style.posLeft = document.all[object].offsetLeft + x; document.all[object].style.posTop = document.all[object].offsetTop + y; document.all[object].style.visibility = 'visible'; } } function hideOffset(object,x,y) { if (document.layers && document.layers[object]) { document.layers[object].visibility = 'hidden'; document.layers[object].left -= x; document.layers[object].top -= y; } else if (document.all) { document.all[object].style.visibility = 'hidden'; document.all[object].style.posLeft -= x; document.all[object].style.posTop -= y; } } //--></script> <hr> <p> blah blah <span id="myLayer6" class="absolute"> <p class="red">Some text within a layer</p> </span> <a href="nextpage.htm" onMouseover="showOffset('myLayer6',0,15)" onMouseout="hideOffset('myLayer6',0,15)">example 6</a> blah blah </p> <hr>
The effect:
blah blah
Some text within a layer
So we've managed to show a layer correctly in the normal flow of text, how about within a table? This next example, shows how in NN4+ an absolute layer within a table seems to totally break free of the table - it can usually be seen at the top left hand corner of the document:
The code:
<hr> <table width="100%" border="1"><tr><td width="50%"> </td><td width="50%"> <p> blah blah <span id="myLayer7" class="absolute"> <p class="red">Some text within a layer</p> </span> <a href="nextpage.htm" onMouseover="showOffset('myLayer7',0,15)" onMouseout="hideOffset('myLayer7',0,15)">example 7</a> blah blah </p> </td></tr></table> <hr>
The effect:
blah blah
Some text within a layer |
The solution is to use a relatively positioned layer in NN4 which can be used as an anchor to allow us to reposition the absolute layer based on its location within the document using the showByShadow() function.
In MSIE4+ there is a tendency for the layer to jump an extra offset amount. Caused by a double triggering of the show function for one hide function. The answer is to introduce a user defined property (myFlag) to the layer to indicate that is has already been repositioned, and doesn't need to be repositioned ever again:
The code:
<script language="JavaScript"><!-- function showByShadow(object,shadow,x,y) { if (document.layers && document.layers[object]) { document.layers[object].left = document.layers[shadow].pageX + x; document.layers[object].top = document.layers[shadow].pageY + y; document.layers[object].visibility = 'visible'; } else if (document.all) { document.all[object].style.visibility = 'visible'; if (document.all[object].myFlag == null) { document.all[object].style.posLeft = document.all[object].offsetLeft + x; document.all[object].style.posTop = document.all[object].offsetTop + y; document.all[object].myFlag = true; } } } function hide(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'hidden'; else if (document.all) document.all[object].style.visibility = 'hidden'; } //--></script> <hr> <table width="100%" border="1"><tr><td width="50%"> </td><td width="50%"> <p> blah blah <span id="myLayer8" class="absolute"> <p class="red">Some text within a layer</p> </span> <span id="myShadow8" class="relative"> </span> <a href="nextpage.htm" onMouseover="showByShadow('myLayer8','myShadow8',0,15)" onMouseout="hide('myLayer8')">example 8</a> blah blah </p> </td></tr></table> <hr>
The effect:
blah blah
Some text within a layer |
So we've solved absolute layers in tables, now how about centered text?
In MSIE4+ a layer reserves the whole width of the current document for itself. Normally you don't tend to see this. However when a layer inherits a text alignment from the containing layer, the centering of the text, as shown in this example, shows up the effect of the layer reserving the whole document width.
In NN4+ the perfect alignment we have so far achieved has been lost:
The code:
<hr> <center> <p> blah blah <span id="myLayer9" class="absolute"> <p class="red">Some text within a layer</p> </span> <span id="myShadow9" class="relative"> </span> <a href="nextpage.htm" onMouseover="showByShadow('myLayer9','myShadow9',0,15)" onMouseout="hide('myLayer9')">example 9</a> blah blah </p> </center> <hr>
The effect:
blah blah
Some text within a layer
The solution for MSIE4+ is to disinherit the alignment of the containing layer, by defining our own text alignment style.
In NN4, we can use the x and y property co-ordinates of the actual link an an anchor to reposition the layer rather then the relative layer we have used in the previous examples:
The code:
<script language="JavaScript"><!-- function showByLink(object,link,x,y) { if (document.layers && document.layers[object]) { document.layers[object].left = link.x + x; document.layers[object].top = link.y + y; document.layers[object].visibility = 'visible'; } else if (document.all) { document.all[object].style.visibility = 'visible'; if (document.all[object].myFlag == null) { document.all[object].style.posLeft = document.all[object].offsetLeft + x; document.all[object].style.posTop = document.all[object].offsetTop + y; } document.all[object].myFlag = true; } } function hide(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'hidden'; else if (document.all) document.all[object].style.visibility = 'hidden'; } //--></script> <hr> <center> <p> blah blah <span id="myLayer10" class="absolute" style="text-align:left;"> <p class="red">Some text within a layer</p> </span> <a href="nextpage.htm" onMouseover="showByLink('myLayer10',this,0,15)" onMouseout="hide('myLayer10')">example 10</a> blah blah </p> </center> <hr>
The effect:
blah blah
Some text within a layer
So how about centered text in a table? As you can see from this next example, there appears to be some reserved space following the inline text. Apart from that everything appears to be working:
The code:
<hr> <table width="100%" border="1"><tr><td width="50%"> </td><td width="50%"> <center> <p> blah blah <span id="myLayer11" class="absolute" style="text-align:left;"> <p class="red">Some text within a layer</p> </span> <a href="nextpage.htm" onMouseover="showByLink('myLayer11',this,0,15)" onMouseout="hide('myLayer11')">example 11</a> blah blah </p> </center> </td></tr></table> <hr>
The effect:
blah blah
Some text within a layer |
Generally speaking, we wouldn't just use plain text within a toggled layer, we would tend to use a table so as to block out the background. So let's try a toggled table layer:
The code:
<hr> <table width="100%" border="1"><tr><td width="50%"> </td><td width="50%"> <center> <p> blah blah <br> blah blah <span id="myLayer12" class="absolute" style="width:150;"> <table class="redtable" cellspacing="0"><tr><td><p class="white">Some text within a layer</p></td></tr></table> </span> <a href="nextpage.htm" onMouseover="showByLink('myLayer12',this,0,15)" onMouseout="hide('myLayer12')">example 12</a> blah blah <br> blah blah </p> </center> </td></tr></table> <hr>
The effect:
blah blah
Some text within a layer |
The previous example Works perfectly in MSIE4+, but in NN4 the layer is never shown. Don't ask me why, but placing the contents of the table cell within a DIV tag appears to solve this problem:
The code:
<hr> <table width="100%" border="1"><tr><td width="50%"> </td><td width="50%"> <center> <p> blah blah <br> blah blah <span id="myLayer13" class="absolute" style="width:150;"> <table class="redtable"><tr><td><div class="color:#ffffff">Some text within a layer</div></td></tr></table> </span> <a href="nextpage.htm" onMouseover="showByLink('myLayer13',this,0,15)" onMouseout="hide('myLayer13')">example 13</a> blah blah <br> blah blah </p> </center> </td></tr></table> <hr>
The effect:
blah blah
|
For some reason the previous two examples do not work correctly on NN4, the layers within the tables do not appear within the tables, but following them.
The following example overcomes this problem, by placing the layer after the table, and then using the x and y property co-ordinates of the current event (the mouseover event in this case) to position the layer relative to the mouse pointer:
The code:
<script language="JavaScript"><!-- function showByEvent(object,x,y,e) { if (document.layers && document.layers[object]) { document.layers[object].left = e.x + x; document.layers[object].top = e.y + y; document.layers[object].visibility = 'visible'; } else if (document.all) { document.all[object].style.posLeft = window.event.x + x; document.all[object].style.posTop = window.event.y + y; document.all[object].style.visibility = 'visible'; } } function hide(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'hidden'; else if (document.all) document.all[object].style.visibility = 'hidden'; } //--></script> <hr> <table width="100%" border="1"><tr><td width="50%"> </td><td width="50%"> <center> <p> blah blah <br> blah blah <a href="nextpage.htm" onMouseover="showByEvent('myLayer14',0,15,event)" onMouseout="hide('myLayer14')">example 14</a> blah blah <br> blah blah </p> </center> </td></tr></table> <span id="myLayer14" class="absolute"> <table class="redtable"><tr><td><div class="color:#ffffff">Some text within a layer</div></td></tr></table> </span> <hr>
The effect:
blah blah
|
Some text within a layer |
This is all okay, so long as the hypertext link is towards the top left of the window, if its towards the bottom right of the window then its possible for the layer to extend out of the side or the bottom of the window - making the effectiveness of the layer fairly useless.
This next example attempts to test for the closeness of the window edges and adjust the position of the layer accordingly:
The code:
<script language="JavaScript"><!-- function showByPosition(object,x,y,e) { if (document.layers && document.layers[object]) { if ((e.x + x + 50 + document.layers[object].clip.width) > (window.pageXOffset + window.innerWidth)) x = x - document.layers[object].clip.width; if ((e.y + y + 50 + document.layers[object].clip.height) > (window.pageYOffset + window.innerHeight)) y *= -4; document.layers[object].left = e.x + x; document.layers[object].top = e.y + y; document.layers[object].visibility = 'visible'; } else if (document.all) { e = window.event; if ((e.x + x + document.all[object].clientWidth) > (document.body.clientWidth + document.body.scrollLeft)) x = (document.body.clientWidth + document.body.scrollLeft) - document.all[object].clientWidth; else x = e.x + x; if ((e.y + y + document.all[object].clientHeight) > (document.body.clientHeight + document.body.scrollTop)) y = e.y - (y * 4); else y = e.y + y; document.all[object].style.posLeft = x; document.all[object].style.posTop = y; document.all[object].style.visibility = 'visible'; } } function hide(object) { if (document.layers && document.layers[object]) document.layers[object].visibility = 'hidden'; else if (document.all) document.all[object].style.visibility = 'hidden'; } //--></script> <hr> <p align="right"> blah blah blah blah <a href="nextpage.htm" onMouseover="showByPosition('myLayer15',0,15,event)" onMouseout="hide('myLayer15')">example 15</a></p> <span id="myLayer15" class="absolute" style="width:100; height:100"> <table class="redtable"><tr><td><div class="color:#ffffff">Some text within a layer</div></td></tr></table> </span> <hr>
The effect:
blah blah blah blah example 15
Some text within a layer |
For it to work correctly on MSIE4, it is necessary to provide width and height style property values.
You can thoroughly test out the above JavaScript code by using the working example.
How to do the impossible with Google Gears
Multi-dialogue forms on one page
Turning Tables Into Selection Lists
Drag and Drop with Microsoft Internet Explorer 5