Absolute Floating Menu
Many web pages don't fit on most users' screens. The visitors have to scroll to read the page contents. Such scrolling however hides the navigation menus (or a shopping cart contents) usually located at the top of the page.The javascript shown here allows to create dynamic menus which move along with scrolling. Such floating menu will be always visible on screen. The effect is achieved my moving an absolutely-positioned or relatively-positioned
DIV
box containing the menu markup.The floating
DIV
will visibly move towards the specified viewing area spot, slowing as it moves closer to it and finishing the move with a visible snap. Such animation will definitely draw user attention to the floating menu.Also available:
- Absolute Floating Menu packaged as a module for Joomla makes using Absolute floating menu much simpler in Joomla environment.
- Free-floating
DIV
box withposition:absolute
(see demo on this page) - Floating
DIV
box confined within specific area (demo):
- box with
position:relative
confined within a container area that doesn't have any other children nodes. - box with
position:absolute
confined within a container area havingposition:relative
.
- Complex containers:
- Experimental support for floating
DIV
confined withinIFRAME
container. - Experimental support for floating
DIV
confined withinDIV
container with scrollbar.
- Can work as a jQuery plugin.
Floating DIV demonstration
Look for the floating box somewhere on the page.Click the buttons to move the floating menu to different corners:
Using the javascript
To use the script perform the following steps:- Download version 1.7 of the script code and put it on your server as a separate file and reference it in HTML
HEAD
:
- <script type="text/javascript" src="specify script file URL here">
- </script>
- Create a DIV element with id of 'floatdiv' which contains the menu markup and add script start code.
- If you want your
DIV
box to move around the window freely, put it withinBODY
tag. - If you want to confine it to client area of specific container, place it within this container. Make sure that floating
DIV
box or container hasposition:relative
.
- <div id="floatdiv" style="
- position:absolute;
- width:200px;height:50px;top:10px;right:10px;
- padding:16px;background:#FFFFFF;
- border:2px solid #2266AA;
- z-index:100">
- This is a floating javascript menu.
- </div>
- <script type="text/javascript">
- floatingMenu.add('floatdiv',
- {
- // Represents distance from left or right browser window
- // border depending upon property used. Only one should be
- // specified.
- // targetLeft: 0,
- targetRight: 10,
- // Represents distance from top or bottom browser window
- // border depending upon property used. Only one should be
- // specified.
- targetTop: 10,
- // targetBottom: 0,
- // Uncomment one of those if you need centering on
- // X- or Y- axis.
- // centerX: true,
- // centerY: true,
- // Remove this one if you don't want snap effect
- snap: true
- });
- </script>
- Change the
targetTop
andtargetRight
variables. They represent the desired distance from the top and right window borders respectively.
- Alternatively, you can use
targetBottom
instead oftargetTop
ortargetLeft
instead oftargetRight
. - For centering use
centerX: true
orcenterY: true
. Values oftarget
for respective axis will be ignored. - To remove initial movement set DIV's position properties (
top
orbottom
,left
orright
) to the same values astarget*
variables.
- Make sure your page has
DOCTYPE
set. It doesn't matter if it isHTML 4.01
orXHTML
, but the script will fail to work if Internet Explorer is in quirks mode. - Be sure to verify page design with the Javascript turned off.
- If all else fails, use script support services.
See Also
- Script source code
- Absolute Floating Menu Close Link/Button, if you want to have a close link/button on your Absolute Floating menu.
- Fixed & Sticky Menu will pin the menu to the spot if the browser supports it, moving it if not.
Browser compatibility
The javascript snippet above was tested on the following user agents:Mozilla | Firefox 3.0.x | Ok. |
Microsoft | Internet Explorer 8.0 | Ok. |
KHTML | Google Chrome 7.0.517.44 | Ok. |
Safari 5.0.2 | Ok. | |
No Javascript or Javascript turned off | Any | Menu will be absolutely positioned on initial style left and top values and will not move. |
JS File Source
/* Script by: www.jtricks.com
* Version: 1.7 (20110408)
* Latest version: www.jtricks.com/javascript/navigation/floating.html
*/
var floatingMenu =
{
hasInner: typeof(window.innerWidth) == 'number',
hasElement: typeof(document.documentElement) == 'object'
&& typeof(document.documentElement.clientWidth) == 'number'
};
var floatingArray =
[
];
floatingMenu.add = function(obj, options)
{
var name;
var menu;
if (typeof(obj) === "string")
name = obj;
else
menu = obj;
if (options == undefined)
{
floatingArray.push(
{
id: name,
menu: menu,
targetLeft: 0,
targetTop: 0,
distance: .07,
snap: true
});
}
else
{
floatingArray.push(
{
id: name,
menu: menu,
targetLeft: options.targetLeft,
targetRight: options.targetRight,
targetTop: options.targetTop,
targetBottom: options.targetBottom,
centerX: options.centerX,
centerY: options.centerY,
prohibitXMovement: options.prohibitXMovement,
prohibitYMovement: options.prohibitYMovement,
distance: options.distance != undefined ? options.distance : .07,
snap: options.snap,
ignoreParentDimensions: options.ignoreParentDimensions,
scrollContainer: options.scrollContainer,
scrollContainerId: options.scrollContainerId
});
}
};
floatingMenu.findSingle = function(item)
{
if (item.id)
item.menu = document.getElementById(item.id);
if (item.scrollContainerId)
item.scrollContainer = document.getElementById(item.scrollContainerId);
};
floatingMenu.move = function (item)
{
if (!item.prohibitXMovement)
{
item.menu.style.left = item.nextX + 'px';
item.menu.style.right = '';
}
if (!item.prohibitYMovement)
{
item.menu.style.top = item.nextY + 'px';
item.menu.style.bottom = '';
}
};
floatingMenu.scrollLeft = function(item)
{
// If floating within scrollable container use it's scrollLeft
if (item.scrollContainer)
return item.scrollContainer.scrollLeft;
var w = window;
// Find top window scroll parameters if we're IFRAMEd
while (w != w.parent)
w = w.parent;
return this.hasInner
? w.pageXOffset
: this.hasElement
? w.document.documentElement.scrollLeft
: w.document.body.scrollLeft;
};
floatingMenu.scrollTop = function(item)
{
// If floating within scrollable container use it's scrollTop
if (item.scrollContainer)
return item.scrollContainer.scrollTop;
var w = window;
// Find top window scroll parameters if we're IFRAMEd
while (w != w.parent)
w = w.parent;
return this.hasInner
? w.pageYOffset
: this.hasElement
? w.document.documentElement.scrollTop
: w.document.body.scrollTop;
};
floatingMenu.windowWidth = function()
{
return this.hasElement
? document.documentElement.clientWidth
: document.body.clientWidth;
};
floatingMenu.windowHeight = function()
{
if (floatingMenu.hasElement && floatingMenu.hasInner)
{
// Handle Opera 8 problems
return document.documentElement.clientHeight > window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
}
else
{
return floatingMenu.hasElement
? document.documentElement.clientHeight
: document.body.clientHeight;
}
};
floatingMenu.documentHeight = function()
{
var innerHeight = this.hasInner
? window.innerHeight
: 0;
var body = document.body,
html = document.documentElement;
return Math.max(
body.scrollHeight,
body.offsetHeight,
html.clientHeight,
html.scrollHeight,
html.offsetHeight,
innerHeight);
};
floatingMenu.documentWidth = function()
{
var innerWidth = this.hasInner
? window.innerWidth
: 0;
var body = document.body,
html = document.documentElement;
return Math.max(
body.scrollWidth,
body.offsetWidth,
html.clientWidth,
html.scrollWidth,
html.offsetWidth,
innerWidth);
};
floatingMenu.calculateCornerX = function(item)
{
var offsetWidth = item.menu.offsetWidth;
if (item.centerX)
return this.scrollLeft(item) + (this.windowWidth() - offsetWidth)/2;
var result = this.scrollLeft(item) - item.parentLeft;
if (item.targetLeft == undefined)
{
result += this.windowWidth() - item.targetRight - offsetWidth;
}
else
{
result += item.targetLeft;
}
if (document.body != item.menu.parentNode
&& result + offsetWidth >= item.confinedWidthReserve)
{
result = item.confinedWidthReserve - offsetWidth;
}
if (result < 0)
result = 0;
return result;
};
floatingMenu.calculateCornerY = function(item)
{
var offsetHeight = item.menu.offsetHeight;
if (item.centerY)
return this.scrollTop(item) + (this.windowHeight() - offsetHeight)/2;
var result = this.scrollTop(item) - item.parentTop;
if (item.targetTop === undefined)
{
result += this.windowHeight() - item.targetBottom - offsetHeight;
}
else
{
result += item.targetTop;
}
if (document.body != item.menu.parentNode
&& result + offsetHeight >= item.confinedHeightReserve)
{
result = item.confinedHeightReserve - offsetHeight;
}
if (result < 0)
result = 0;
return result;
};
floatingMenu.computeParent = function(item)
{
if (item.ignoreParentDimensions)
{
item.confinedHeightReserve = this.documentHeight();
item.confinedWidthReserver = this.documentWidth();
item.parentLeft = 0;
item.parentTop = 0;
return;
}
var parentNode = item.menu.parentNode;
var parentOffsets = this.offsets(parentNode, item);
item.parentLeft = parentOffsets.left;
item.parentTop = parentOffsets.top;
item.confinedWidthReserve = parentNode.clientWidth;
// We could have absolutely-positioned DIV wrapped
// inside relatively-positioned. Then parent might not
// have any height. Try to find parent that has
// and try to find whats left of its height for us.
var obj = parentNode;
var objOffsets = this.offsets(obj, item);
while (obj.clientHeight + objOffsets.top
< item.menu.offsetHeight + parentOffsets.top)
{
obj = obj.parentNode;
objOffsets = this.offsets(obj, item);
}
item.confinedHeightReserve = obj.clientHeight
- (parentOffsets.top - objOffsets.top);
};
floatingMenu.offsets = function(obj, item)
{
var result =
{
left: 0,
top: 0
};
if (obj === item.scrollContainer)
return;
while (obj.offsetParent && obj.offsetParent != item.scrollContainer)
{
result.left += obj.offsetLeft;
result.top += obj.offsetTop;
obj = obj.offsetParent;
}
if (window == window.parent)
return result;
// we're IFRAMEd
var iframes = window.parent.document.body.getElementsByTagName("IFRAME");
for (var i = 0; i < iframes.length; i++)
{
if (iframes[i].contentWindow != window)
continue;
obj = iframes[i];
while (obj.offsetParent)
{
result.left += obj.offsetLeft;
result.top += obj.offsetTop;
obj = obj.offsetParent;
}
}
return result;
};
floatingMenu.doFloatSingle = function(item)
{
this.findSingle(item);
var stepX, stepY;
this.computeParent(item);
var cornerX = this.calculateCornerX(item);
var stepX = (cornerX - item.nextX) * item.distance;
if (Math.abs(stepX) < .5 && item.snap
|| Math.abs(cornerX - item.nextX) == 1)
{
stepX = cornerX - item.nextX;
}
var cornerY = this.calculateCornerY(item);
var stepY = (cornerY - item.nextY) * item.distance;
if (Math.abs(stepY) < .5 && item.snap
|| Math.abs(cornerY - item.nextY) == 1)
{
stepY = cornerY - item.nextY;
}
if (Math.abs(stepX) > 0 ||
Math.abs(stepY) > 0)
{
item.nextX += stepX;
item.nextY += stepY;
this.move(item);
}
};
floatingMenu.fixTargets = function()
{
};
floatingMenu.fixTarget = function(item)
{
};
floatingMenu.doFloat = function()
{
this.fixTargets();
for (var i=0; i < floatingArray.length; i++)
{
this.fixTarget(floatingArray[i]);
this.doFloatSingle(floatingArray[i]);
}
setTimeout('floatingMenu.doFloat()', 20);
};
floatingMenu.insertEvent = function(element, event, handler)
{
// W3C
if (element.addEventListener != undefined)
{
element.addEventListener(event, handler, false);
return;
}
var listener = 'on' + event;
// MS
if (element.attachEvent != undefined)
{
element.attachEvent(listener, handler);
return;
}
// Fallback
var oldHandler = element[listener];
element[listener] = function (e)
{
e = (e) ? e : window.event;
var result = handler(e);
return (oldHandler != undefined)
&& (oldHandler(e) == true)
&& (result == true);
};
};
floatingMenu.init = function()
{
floatingMenu.fixTargets();
for (var i=0; i < floatingArray.length; i++)
{
floatingMenu.initSingleMenu(floatingArray[i]);
}
setTimeout('floatingMenu.doFloat()', 100);
};
// Some browsers init scrollbars only after
// full document load.
floatingMenu.initSingleMenu = function(item)
{
this.findSingle(item);
this.computeParent(item);
this.fixTarget(item);
item.nextX = this.calculateCornerX(item);
item.nextY = this.calculateCornerY(item);
this.move(item);
};
floatingMenu.insertEvent(window, 'load', floatingMenu.init);
// Register ourselves as jQuery plugin if jQuery is present
if (typeof(jQuery) !== 'undefined')
{
(function ($)
{
$.fn.addFloating = function(options)
{
return this.each(function()
{
floatingMenu.add(this, options);
});
};
}) (jQuery);
}
Comments
Post a Comment