I'm trying to create a div 'floater' which acts similar to a fixed div (stuck in a specific position regardless of scrolling), but when it hits the boundaries of the div it is within, it stops being fixed.
When the scrollbar is at the top, it should have the div placed at 0 within the containing div (positioned say 100 pixels from the top) and when the scrollbar reaches the bottom, it should prevent the floater from going outside the container. The height of the floater would be static, but the container height would be dynamic.
I've seen this type of this all over the place, but can't figure out how to find a good example for it.
I'd like to avoid jQuery if possible, as I imagine it should only require some simple JavaScript to determine the current position relative to the div it is within.
Thank you.
Okay folks, here is a complete solution. I've only tested this in Firefox and IE, but it should work across the board (I think). This is straight JavaScript and jQuery is not used. The first JS function makes sure the height returned works in various browsers.
Edit - I improved on this, see below.
<html>
<head>
<style type="text/css">
* {margin:0;padding:0;}
#header {height:300px;width:100%;background:#888;}
#main {height:800px;width:70%;background:#eee;float:left;}
#side {width:30%;height:auto;background:#ddd;float:left;position:relative;}
#floater {height:400px;width:90%;background:#dcd;top:0px;position:absolute;}
#footer {height:300px;width:100%;background:#888;clear:both;}
</style>
<script>
function getPageY() {
var height = 0;
if(typeof(window.pageYOffset) == 'number') {
height = window.pageYOffset;
}
else if(document.body && document.body.scrollTop) {
height = document.body.scrollTop;
}
else if(document.documentElement && document.documentElement.scrollTop) {
height = document.documentElement.scrollTop;
}
return height;
}
onload=function() {
window.onscroll = scroll;
function scroll() {
ybox.value = "this: "+getPageY();
var f = document.getElementById("floater");
var y = getPageY()-300; // minus header height
var divh = document.getElementById("main").offsetHeight;
if (divh > 400) { // greater than floater height
divh -= 400; // minus floater height
if (y < 0) y = 0;
else if (y > divh) y = divh;
f.style.top = y+"px";
}
}
}
</script>
</head>
<body>
<div id="header"></div>
<div id="main"></div>
<div id="side"><div id="floater">Float Content<br />
<input name="ybox" id="ybox"></div></div>
<div id="footer"></div>
</body>
</html>
This works, but with images it is extremely jumpy. I modified it to use a fixed position when it should be stuck in a position. Replace the three matching lines with this for a smoother result:
if (y < 0) {y = 0;f.style.position = "absolute";}
else if (y > divh) {y = divh;f.style.position = "absolute";f.style.top = divh+"px";}
else {f.style.position = "fixed";f.style.top = 0;}
I've implemented this and its pretty good. http://net.tutsplus.com/tutorials/html-css-techniques/creating-a-floating-html-menu-using-jquery-and-css/. But this is done using jquery only. Ill let you know if icome across any links with just plain javascript.
Related
Banging my head trying to sort out the correct logic for adding simple parallax behavior.
I would like to have a number of elements on a page which start out with their top offset a certain distance (e.g. 300px). Then as you scroll down the page, once the top of the element is revealed it will slowly shift upwards (tied to scroll) until the top of element reaches middle of viewport at which time it's top offset is 0 and it remains in place.
I tried using third party script (Scroll Magic, Stellar, etc), but when I couldn't get it right now I'm trying custom code:
https://jsfiddle.net/louiswalch/5bxz8fku/1/
var $Window = $(window);
var offset_amount = 400;
var window_height = $Window.height();
var window_half = (window_height/2);
var sections = $('SECTION.reveal');
sections.each(function() {
var element = $(this);
// Make sure we always start with the right offset
element.css({top: offset_amount});
$Window.bind('scroll', function() {
var viewport_top = $Window.scrollTop();
var viewport_middle = viewport_top + (window_height/2)
var viewport_bottom = viewport_top + window_height;
var element_top = element.offset().top;
if (element_top > viewport_top && element_top <= viewport_bottom) {
var distance_to_middle = (element_top - viewport_middle);
var amount_to_middle = (distance_to_middle / window_half);
console.log(amount_to_middle);
if (amount_to_middle >= 0) {
element.css({top: (offset_amount * amount_to_middle)+ 'px'});
} else {
// ? Lock to end position ?
}
}
});
});
jsBin demo 1. (margin space effect on both enter and exit)
jsBin demo 2. (preserve 0 margin once touched)
Instead of targeting the section elements, (create and) target their first child elements,
otherwise you'll create a concurrency mess trying to get the top position but simultaneously modifying it.
Also, you cannot rely on fixed 300px margin (i.e: if window height is less than 500px, you're already missing 100px). That space can vary when the screen height is really small, so you also need to find the idealMarg value.
var $win = $(window),
$rev = $('.reveal'),
winH2 = 0,
winSt = 0;
function reveal() {
winSt = $win.scrollTop();
winH2 = $win.height()/2;
$rev.each(function(i, el){
var y = el.getBoundingClientRect().top,
toMiddleMax = Math.max(0, y-winH2),
idealMarg = Math.min(300, toMiddleMax),
margMin = Math.min(idealMarg, idealMarg * (toMiddleMax/winH2));
$(">div", this).css({transform: "translateY("+ margMin +"px)"});
});
}
$win.on({"load resize scroll" : reveal});
*{box-sizing:border-box; -webkit-box-sizing:border-box;}
html, body{height:100%; margin:0;}
section > div{
padding: 40px;
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section>
<div style="background-color:red">1</div>
</section>
<section class="reveal">
<div style="background-color: yellow">2</div>
</section>
<section class="reveal">
<div style="background-color: orange">3</div>
</section>
<section class="reveal">
<div style="background-color: pink">4</div>
</section>
I've used in HTML just a <div> logically, that has to be the one and only first child of a section parent.
You're welcome to tweak the above code to make it more performant.
Hey so here is my go at an awnser.
http://jsbin.com/wibiferili/edit?html,js,output
The jist of it is as follows.
JS
var $Window = $(window),
parallaxFactor = 2;
$('.parallaxblock').each(function(a,b){
var element = $(b);
element.css("top",element.data("pOffset") + "px");
$Window.bind('scroll', function() {
var pos =
// Base Offset
element.data("pOffset")
// parallaxFactor
- ($Window.scrollTop() / parallaxFactor);
pos = pos < 0 ? 0 : pos;
element.animate({"top": pos + "px"},10);
return;
});
});
Styles
body{
height: 4000px;
}
.parallaxblock{
position:fixed;
background:#999;
opacity:.5;
}
Example Usage
<div class="parallaxblock" data-p-offset=100>Im A Block</div>
<div class="parallaxblock" data-p-offset=200>Im Also Block</div>
<div class="parallaxblock" data-p-offset=1500>Im Another Block</div>
So by checking the offest its never lower then 0 we can lock it at the top of the screen once it reaches it.
I get the offset amount of the data tag on the div.
If you wanted to change the rate of scroll in different posistions you could change the parallax factor at a certain percentage of screen height.
Hope this helps.
I am trying to figure out if the following scenario is possible with simple jQuery or javascript, without the use of a plugin.
Here is the base example code:
<div id="section1">
<!-- content here -->
</div>
<div id="section2">
<!-- content here -->
</div>
<div id="section3">
<!-- content here -->
</div>
Each section will fill the viewport in both width and height. What I would like to achieve, is when the viewer scrolls down or up, regardless of the length of the scroll, to simply transition to the next section. I don't want there to be typical scrolling, only transitions to the next or previous section.
I know that I can to this with buttons really easily, and would like to extend this to scrolling. I was checking out a great example of this the other day, but I cannot for the life of me find that page.
I had thought perhaps using a variable to store which section the viewer is currently in, and cycling depending on the direction, but I'm not sure if that is the best approach or even how to get started with that.
Please let me know if I have explained the concept clearly. I would greatly appreciate any thoughts on this.
Thanks!
Using Simple JS? No. Because this type of interaction requires viewport functionality for checking which elements exist in viewport and which ones are partially in view, it would be better off to use a simple plugin that takes into account all user interaction, edge cases and options. Many plugins have been thoroughly debugged by the community for this purpose.
If you'd like to start somewhere for knowledge purposes on this concept, i've highlighted below the steps in a jsfiddle to get you started:
JSFiddle
http://jsfiddle.net/3nb0mwoe/13/
HTML
<section id="section1">
</section>
<section id="section2">
</section>
<section id="section3">
</section>
CSS
section {
height: 100%;
}
#section1 {
background-color: red;
}
#section2 {
background-color: blue;
}
JS
/* helpers */
// http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
function isElementInViewport (el) {
//special bonus for those using jQuery
if (typeof jQuery === "function" && el instanceof jQuery) {
el = el[0];
}
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom - 25 <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
function anyOfElementInViewport(el) {
var top = el.offsetTop;
var left = el.offsetLeft;
var width = el.offsetWidth;
var height = el.offsetHeight;
while(el.offsetParent) {
el = el.offsetParent;
top += el.offsetTop;
left += el.offsetLeft;
}
return (
top < (window.pageYOffset + window.innerHeight) &&
left < (window.pageXOffset + window.innerWidth) &&
(top + height) > window.pageYOffset &&
(left + width) > window.pageXOffset
);
}
/* START */
// step 1: get all sections and set viewport height for consistency
var $sections = $('section');
$sections.css('height', window.innerHeight);
// step 2: get current section
var $currSection = $.grep($sections, function($s) {
return isElementInViewport($s);
});
if($currSection.length > 0) {
$(window).scroll(function(){
// step 3. check for any section that is 'partially in viewport'
var $nextAndCurrSection = $.grep($sections, function($s) {
return anyOfElementInViewport($s);
});
if($nextAndCurrSection.length > 0) {
// remove currSection from list
var $nextSection = $($nextAndCurrSection).not($($currSection));
if($nextSection.length > 0) {
alert('test');
}
}
});
}
Update
Based on your requirements, I would currently recommend this plugin: alvarotrigo.com/fullPage
I have created a parallax scroll, which seem to be working fine in firefox however in the chrome browser there's a slight jump on the body text when scrolling. click here scroll to the about section. I am not sure if t this is a css or JS issue.. below is a snippet i have incorporated into my parallax function
Does anyone know how i an fix this issue?
$(document).ready(function(){
// Cache the Window object
$window = $(window);
// Cache the Y offset and the speed of each sprite
$('[data-type]').each(function() {
$(this).data('offsetY', parseInt($(this).attr('data-offsetY')));
$(this).data('Xposition', $(this).attr('data-Xposition'));
$(this).data('speed', $(this).attr('data-speed'));
});
// For each element that has a data-type attribute
$('[data-type="background"]').each(function(){
// Store some variables based on where we are
var $self = $(this),
offsetCoords = $self.offset(),
topOffset = offsetCoords.top;
// When the window is scrolled...
$(window).scroll(function() {
// If this section is in view
if ( ($window.scrollTop() + $window.height()) > (topOffset) &&
( (topOffset + $self.height()) > $window.scrollTop() ) ) {
// Scroll the background at var speed
// the yPos is a negative value because we're scrolling it UP!
var yPos = -($window.scrollTop() / $self.data('speed'));
// If this element has a Y offset then add it on
if ($self.data('offsetY')) {
yPos += $self.data('offsetY');
}
// Put together our final background position
var coords = '50% '+ yPos + 'px';
// Move the background
$self.css({ backgroundPosition: coords });
$('[data-type="scroll-text"]', $self).each(function() {
var $text= $(this);
var pos = ($window.scrollTop()/10) * $text.data('speed');
var curP = $text.css('margin-top');
var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
if(is_chrome) {
$text.animate({
paddingTop: pos,
}, 200, 'linear', function() {
// Animation complete.
});
} else {
$text.css('padding-top', pos);
}
});
}; // in view
}); // window scroll
}); // each data-type
}); // document ready
Some suggestions:
1.) Use position: fixed to avoid any jitter, as you'll be taking the element out of the document flow. You can then position it using z-index.
2.) Cache as much as you can to ease processing time.
3.) Math.round may not be necessary, but try adding this CSS to your moving areas: -webkit-transform: translate3d(0,0,0); This will force hardware acceleration in Chrome, which may ease some of the jittering. (It looked smoother on my screen when I added this with Inspector, but it didn't get rid of the jumpiness with the scroll wheel.) Note: Don't do this on your entire document (e.g. body tag), as it might cause some issues with your current layout. (Your navigation bar didn't stick to the top of the window, for instance.)
4.) If you have any animations running as part of your parallax logic (tweening the margin into place or something along those lines), remove it - that would probably cause the jump you see.
Hope this helps. Best of luck.
I see the same jittering in FireFox and Chrome (Mac). Looking at your containers, one thing that's glaring at me is the pixel position that's being calculated/used.
Chrome: <div id="about-title" style="margin-top: 1562.3999999999999px;">
FireFox: <div id="about-title" style="margin-top: 1562.4px;">
Browsers aren't going to allow content to sit at 1/2 pixel, let alone 0.3999999 of a pixel. I think it's moving it, and trying to calculate whether to round up or round down. It jitters because it's calculating with every click of your mouse wheel.
Thus, I'd try adding Math.round() to your positions so that the containers are never being left in limbo.
Take a look at the code here: http://webdesigntutsplus.s3.amazonaws.com/tuts/338_parallax/src/index.html
Firebug some of the elements, and you'll see that their only fraction of a pixel is '0.5'. Most of them (the bulk) go to round number values.
You are going to have to change the way that the scrolling works (i.e. change how the spacing is computed), but this can be fixed by adding the position:fixed CSS element to the page elements that are scrolling. The problem is coming from the time that it takes for the JavaScript to process and then render.
For example, on your page you would set each of the <div> tags containing text to have a fixed position and then use the JavaScript/JQuery function to update the top: CSS element. This should make the page scroll smoothly.
Have you tried adding the preventdefault inside the scroll function?
$(window).scroll(function(e) {
e.preventDefault();
// rest of your code
}
In a previous question I created a fairly good parallax scrolling implementation. Jquery Parallax Scrolling effect - Multi directional You might find it useful.
Here's the JSFiddle http://jsfiddle.net/9R4hZ/40/ use the up/down arrows or scroll wheel.
Using padding and margin for the positioning are probably why you're experiencing rendering issues. While my code uses scroll or keyboard input for the effect you can loop the relavent portion and check the $moving variable until you reach the desired element on screen.
function parallaxScroll(scroll) {
// current moving object
var ml = $moving.position().left;
var mt = $moving.position().top;
var mw = $moving.width();
var mh = $moving.height();
// calc velocity
var fromTop = false;
var fromBottom = false;
var fromLeft = false;
var fromRight = false;
var vLeft = 0;
var vTop = 0;
if($moving.hasClass('from-top')) {
vTop = scroll;
fromTop = true;
} else if($moving.hasClass('from-bottom')) {
vTop = -scroll;
fromBottom = true;
} else if($moving.hasClass('from-left')) {
vLeft = scroll;
fromLeft = true;
} else if($moving.hasClass('from-right')) {
vLeft = -scroll;
fromRight = true;
}
// calc new position
var newLeft = ml + vLeft;
var newTop = mt + vTop;
// check bounds
var finished = false;
if(fromTop && (newTop > t || newTop + mh < t)) {
finished = true;
newTop = (scroll > 0 ? t : t - mh);
} else if(fromBottom && (newTop < t || newTop > h)) {
finished = true;
newTop = (scroll > 0 ? t : t + h);
} else if(fromLeft && (newLeft > l || newLeft + mw < l)) {
finished = true;
newLeft = (scroll > 0 ? l : l - mw);
} else if(fromRight && (newLeft < l || newLeft > w)) {
finished = true;
newLeft = (scroll > 0 ? l : l + w);
}
// set new position
$moving.css('left', newLeft);
$moving.css('top', newTop);
// if finished change moving object
if(finished) {
// get the next moving
if(scroll > 0) {
$moving = $moving.next('.parallax');
if($moving.length == 0)
$moving = $view.find('.parallax:last');
} else {
$moving = $moving.prev('.parallax');
if($moving.length == 0)
$moving = $view.find('.parallax:first');
}
}
// for debug
$('#direction').text(scroll + " " + l + "/" + t + " " + ml + "/" + mt + " " + finished + " " + $moving.text());
}
May not be related to your specifics, but I had a jumpy parallax scrolling problem, I was able to solve it adding the following CSS for the fixed portions of the page:
#supports (background-attachment: fixed)
{
.fixed-background
{
background-attachment: fixed;
}
}
Not sure of all the specifics, but found at Alternate Fixed & Scroll Backgrounds
All,
A total newbie here but could someone please review this code and let me know where I am going wrong?
What effect I am trying to achieve: I have about 9 divs on the webpage, consisting of images and text, the text comes into focus when the user hovers over the image inside the div. I want these divs to 'appear' to be floating around the page.
So what I did: I absolutely positioned the divs using CSS on the webpage. Now I am using JS to move the divs from their position to set positions on the page (will do this for each div) and try to give the effect of random movement to the divs. Here is the code:
<script language="javascript">
var x = document.getElementById("cr001").style.left;
var y = document.getElementById("cr001").style.top;
var d_x = 75;
var d_y = 100;
var interval = 2; //move by only 2px...
function moveImage() {
if(x < d_x) x = x + interval;
if(y < d_y) y = y + interval - 1; //move y by only 1px
document.getElementById("cr001").style.left = x+'px';
document.getElementById("cr001").style.top = y+'px';
if ((x + interval < d_x) && (y + interval < d_y)) {
window.setTimeout('moveImage()',100);
}
}
</script>
</head>
<body onload="moveImage()">
<div id="blackhole"><img src="img/bimg.png" alt="blackhole" /></div>
<div id="container">
<div id="cr001" class="twinkles">
<a href="#">
<img src="img/cr.png" alt="Co is about your freedom" />
<span>Co is about your freedom</span></a>
</div>
</body>
Could someone please explain where I am going wrong?
Cheers,
Based on the refined post, I now see that you are trying to access body content in the head with var x = document.getElementById("cr001").style.left;
This won't work because when the head is loaded, the body is not ready. You should make an init() function that looks like:
function init(){
window.x = document.getElementById("cr001").style.left;
window.y = document.getElementById("cr001").style.top;
moveImage();
}
and then attach that to the body onload.
EDIT: ok, here is some modified code that does what you want. You can stick this in a file named index.html and launch it in Firefox. I broke it down for you:
<html>
<head>
<script language="javascript">
var d_x = 75;
var d_y = 100;
var interval = 2;
//init is used for getting things up and running once the page is loaded
function init(){
//optimization: only get the element once
var el = document.getElementById("cr001")
x = parseInt(el.style.left);
if(isNaN(x)){
//parseInt("") == NaN
x = 0;
}
y = parseInt(el.style.top);
if(isNaN(y)){
//ditto
y = 0;
}
//call the nuts of this script
moveImage();
}
//this is mostly unchanged
function moveImage() {
if(x < d_x){
x = x + interval;
}else{
//lets keep looping just for fun!
x = 0;
}
if(y < d_y){
y = y + interval - 1; //move y by only 1px
}else{
y = 0;
}
//optimization: only get the element once
var el = document.getElementById("cr001")
el.style.left = x + 'px'; //dont forget to add 'px' back in
el.style.top = y + 'px';
//loop, don't use strings in setTimeout since this is basically eval...eval is evil
setTimeout(moveImage, 100);
}
</script>
</head>
<body onload="init()">
<div id="container">
<!-- IMPORTANT: POSITION IS ABSOLUTE -->
<div id="cr001" style="position:absolute; width:10px; height:10px; background-color:black"></div>
</div>
</body>
The problem is here:
document.getElementById("cr001").style.left
This actually returns the css value, which is a string for example '100px' or '10%' etc. Yet, later on your code, you use this value as if it was an integer.
You have an unclosed div. You open <div id="container"> and <div id="cr001" class="twinkles"> but only close one of them
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 );
}
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.
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.
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;
}
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];
}
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.
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'.
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.
I had this same problem earlier this year. The way I fixed it:
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)
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)
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!
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);
}