|
Dynamic Floating Tool Tips
You are here: irt.org | Articles | Dynamic HTML (DHTML) | Dynamic Floating Tool Tips
Published on: Sunday 23rd January 2000 By: Martin Webb
Introduction
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>
|
show() and hide() functions
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>
|
A Relatively Positioned Layer
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
blah blah
An Absolutely Positioned 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
blah blah
<span> instead of <div>
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
blah blah
Layer before the Link
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
example 4
blah blah
showOffset() and hide() functions
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
example 5
blah blah
showOffset() and hideOffset() functions
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
example 6
blah blah
Layers in Table Cells
Where did it go?
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
example 7
blah blah
|
Anchoring an Absolute Layer to a Relative 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
example 8
blah blah
|
Centered Text
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
example 9
blah blah
Disinheriting the layer, and using the x and y coordinates of the link
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
example 10
blah blah
Centered Table Text
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
example 11
blah blah
|
Opaque Background Color
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
blah blah
example 12
blah blah
blah blah
|
Embedding the Table Cell in 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
blah blah
example 13
blah blah
blah blah
|
Capturing Events and the Mouse Pointer Location
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
blah blah
example 14
blah blah
blah blah
|
Detecting the Window Edges
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
For it to work correctly on MSIE4, it is necessary to provide
width and height style property values.
Working Example
You can thoroughly test out the above JavaScript code by using the
working example.
Feedback on 'Dynamic Floating Tool Tips'
View the profile on Martin Webb and the list of other Articles by Martin Webb.
|
|