views:

1348

answers:

9

The mouse hovers over an element and a tip appears. The tip overflows the page, triggering a scrollbar, which changes the layout just enough so that the underlying element that triggered the tip is no longer under the mouse pointer, so the tip goes away.

The tip goes away, so the scrollbar goes away, and now the mouse is again over the element.

Wash, rinse, repeat.

If I could make sure that tip isn't too big so as to trigger scrollbars, that would solve my problem.

EDIT: After reading comments, some things to clarify: The div contains text which can vary. If I can, I want to show all the text. The div's location needs to be near the element the mouse's tip is over. So the key is, I need to know whether to truncate the text.

I did find this link:
http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
which contains this piece of the puzzle, figuring out how big the browser window is:

function alertSize() {
  var myWidth = 0, myHeight = 0;
  if( typeof( window.innerWidth ) == 'number' ) {
  //Non-IE
  myWidth = window.innerWidth;
  myHeight = window.innerHeight;
 } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE 6+ in 'standards compliant mode'
    myWidth = document.documentElement.clientWidth;
    myHeight = document.documentElement.clientHeight;
 }  else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    //IE 4 compatible
    myWidth = document.body.clientWidth;
    myHeight = document.body.clientHeight;
 }
 window.alert( 'Width = ' + myWidth );
 window.alert( 'Height = ' + myHeight );
}
+1  A: 

edit: in response to the comments, it sounds like you're trying to have the tooltip appear, without affecting the positioning of existing elements (and thus causing the scrollbar on the main window).

if that's the case, you want to define your tooltip's position as absolute, as this will remove it from the flow of elements (so when it appears it won't push the rest of the page down).

for example, you could start it hidden:

#tooltip {
  position: absolute;
  height: 100px;
  width: 200px;
  border: 1px solid #444444;
  background-color: #EEEEEE;
  display: none;
}

then, on your mouseover event (or whatever it's called on), set the top and left css of the #tooltip to where ever you want it, and switch the display to block. as it's positioned absolutely, it won't cause the flicker.

Owen
+1  A: 

CSS : specify the tooltip's width and height, add overflow: hidden or overflow: scroll to it.

position: absolute works fine too, but of course, then you'll have to specify the top and left position of the tooltip.

andyk
Maybe I'm asking, how do I get the boundary of screen, so that I can set the width and height. (is that called the "viewport"?)
Corey Trager
umm.. I'm not sure I understand that. But then if the tooltip is at the bottom of the screen wouldn't it get less height than it should be ?You only objective is to avoid the 'flickering'-like condition caused by the appearance of the tooltip, right ?
andyk
My only objective is to avoid the flickering, which in this case means not triggering a page without scrollbars to suddenly have scrollbars.
Corey Trager
carefully positioning the tooltip might accomplish that. Have you tried position: fixed too ? That way the tooltip will be completely inside the viewport given the correct setting. If that still doesn't help you, please post further details and the url of the page you mentioned.
andyk
it may help to edit your post with some code of where you're at right now as well
Owen
A: 

Seems to me that what you need is cursor position within the client browser window. Then you can do your calculations to place the tooltip so it doesn't cross the border.

What I found on the web is a short article discussing this in diffrent browsers: Mouse Cursor Position. Maybe this could help you fix your problem?

And some more info about browser size can be found here.

Hope it helps.

kender
+1  A: 

you can use a hidden DIV positioned at 0,0 with width and height set to 100% as a 'yardstick' to measure the client area of the screen

if you know the size of your tooltip window, you can clip it to the client window, or change the display position to shift it so that it stays within the boundaries

some code below (untested, ripped from another project and renamed inline)

var toolTipDiv; //this is your tooltip div element
//call AdjustToolTipPosition(window.event);
function AdjustToolTipPosition(e)
{
    var cpos = getPosition(e);
    mouseX = cpos.x;
    mouseY = cpos.y;

    //Depending on IE/Firefox, find out what 
    //object to use to find mouse position

    toolTipDiv.style.visibility = "visible";

    //backdrop 'yardstick' for client area measurement
    var backdropDiv = document.getElementById("divBackdrop");

    //make sure floating box doesn't leave the screen
    //we know box is 200x200 plus margins, say 215x215
    if ((cpos.y + 215) > backdropDiv.offsetHeight)
    {
        cpos.y = backdropDiv.offsetHeight - 215;
    }
    if ((cpos.x + 215) > backdropDiv.offsetWidth)
    {
        cpos.x = backdropDiv.offsetWidth - 215;
    }
    toolTipDiv.style.left = cpos.x + "px";
    toolTipDiv.style.top = cpos.y + "px";
}
//this function courtesy of 
//http://hartshorne.ca/2006/01/23/javascript_cursor_position/
function getPosition(e) 
{
    e = e || window.event;
    var cursor = {x:0, y:0};
    if (e.pageX || e.pageY) 
    {
        cursor.x = e.pageX;
        cursor.y = e.pageY;
    }
    else 
    {
        var de = document.documentElement;
        var b = document.body;
        cursor.x = e.clientX + 
            (de.scrollLeft || b.scrollLeft) - (de.clientLeft || 0);
        cursor.y = e.clientY + 
            (de.scrollTop || b.scrollTop) - (de.clientTop || 0);
    }
    return cursor;
}
Steven A. Lowe
A: 

It could be possible to setup a ghost transparent DIV exactly of you whole page/viewport size. Then you can 'stick' a tooltip DIV within it, providing CSS float:right attribute. That would give you correct top/left tooltip's corner measures for a final tooltip rendering.

Edit: this should be done only for the case of 'edge situations'.

Thevs
A: 

You could try determining where the pointer is, and if it is in the right 1/4 (or whatever area you determine) of the viewport, put the tool tip on the left of the pointer, otherwise put it to the right.

You mentioned that the text can vary, but is it possible it will grow very large? Could it take up an entire screen itself? Most likely, there is a maximum size it will be, so take that into account when deciding what threshold to use to decide if the tip should be on the right or the left.

Then, absolutely position your tip div, and to be safe, give it a max-height and max-width attribute. If the text does grow larger than that, give it overflow: scroll in the CSS.

pkaeding
A: 

I had this same problem earlier this year. The way I fixed it:

  1. I assumed vertical scrolling is ok, but horizonal scrolling is not. (There was always enough room so that the vertical scrollbar didn't affect my layout)
  2. I fixed the relative vertical position of the tooltip with regards to the target. (The top of the tooltip was always 5px below the bottom of the anchor)
  3. The left side of the tooltip was set with regard to the size of the screen. If the whole tooltip could fit on one line, cool. Otherwise, I constrained the max width and made it wrap.

One thing that helped me implement it this was was Quirksmode's Find Position article.

My solution might not be exactly what you're looking for, but at least have a look at the Quirksmode link, its good.

Hope that helps!

Zachary Yates
A: 

A better idea may be to place the tooltip to the left or to the right of the element depending on which side of the page is closer. I have width of my tooltip fixed, fill it with content and make it visible when needed, and then position it depending on mouse position. Here's the key part of onmousemove event handler when tooltip is enabled:

if (!e) var e = window.event;
if(e) {
    var posx = 0;
    var posy = 0;

    if (e.pageX || e.pageY) {
     posx = e.pageX;
     posy = e.pageY;
    }
    else if (e.clientX || e.clientY) {
     posx = e.clientX + document.body.scrollLeft
      + document.documentElement.scrollLeft;
     posy = e.clientY + document.body.scrollTop
      + document.documentElement.scrollTop;
    }

    var overflowX = (document.body.clientWidth + document.body.scrollLeft + document.documentElement.scrollLeft) - (posx + 25+ tooltip.clientWidth);
    if(overflowX < 0) posx -= 25+ (tooltip.clientWidth);

    var overflowY = (document.body.clientHeight + document.body.scrollTop + document.documentElement.scrollTop) - (posy + 15+ tooltip.clientHeight);
    if(overflowY < 0) posy += overflowY;

    tooltip.style.left=(10+posx);
    tooltip.style.top=(10+posy);
}
buti-oxa
+1  A: 

Here is the code that I ended up using, and it seems to be working.

function display_popup(s)
{ 

 var popup = document.getElementById("popup");
 popup.innerHTML = s

 //viewport_height = $(document).height()  doesn't work
 viewport_height = get_viewport_size()[1] // does this factor in scrollbar?

 mytop = $(current_element).offset().top + $(current_element).height() + 4
 scroll_offset_y = $(document).scrollTop()
 y_in_viewport = mytop - scroll_offset_y

 if (y_in_viewport < viewport_height) // are we even visible?
 {
  // Display the popup, but truncate it if it overflows 
  // to prevent scrollbar, which shifts element under mouse
  // which leads to flicker...

  popup.style.height= ""
  popup.style.display = "block";

  if (y_in_viewport + popup.offsetHeight > viewport_height)
  {
   overflow = (y_in_viewport + popup.offsetHeight) - viewport_height

   newh = popup.offsetHeight -  overflow
   newh -= 10 // not sure why i need the margin..

   if (newh > 0)
   {
    popup.style.height = newh 
   }
   else
   {
    popup.style.display = "none";
   }
  }
  popup.style.left = $(current_element).offset().left + 40
  popup.style.top = mytop
 }
}


function get_viewport_size()
{
  var myWidth = 0, myHeight = 0;

  if( typeof( window.innerWidth ) == 'number' )
  {
 //Non-IE
 myWidth = window.innerWidth;
 myHeight = window.innerHeight;
  }
  else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) )
  {
 //IE 6+ in 'standards compliant mode'
 myWidth = document.documentElement.clientWidth;
 myHeight = document.documentElement.clientHeight;
  }
  else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) )
  {
 //IE 4 compatible
 myWidth = document.body.clientWidth;
 myHeight = document.body.clientHeight;
  }

  return [myWidth, myHeight];
}
Corey Trager