[jQuery] Simplifying this X/Y box display adjustment

[jQuery] Simplifying this X/Y box display adjustment


I wrote a tip plugin for our needs and spent much of the design time
getting the cross-browser correct placement of the tip box within a
BODY container (viewport) or a user-defined viewport (a specific <DIV>
id passed to the plugin settings). I think I finally got it all
worked out.
The requirements is that there is no clipping of the tip box and the
preferred placement order is:
- At the end of the tag (right side) and the box centered at
current
mouse Y position
- Above or below the current tag if it doesn't fit on the right
Currentlly I don't have a need to place the tipbox on the left side of
the current tag, but I plan to add that logic in the future.
I would like to see if I can reduce the _onPlacement() function below
as I believe I had some redundancy and maybe I am not using already
available dimension properties. The distribution of the code is
compressed so I am not concern with the comments and clean layout of
the code.
I think basically that I "over coded" as I worked out the logic to
taken into account scroll offsets of the view port window and/or the
body or both.
So if you have any tips to reduce the calculation, I would be grateful
to see them.
The onPlacement() function is called lke so:
function doShow()
{
// This is required logic flow: show it, then make it hidden
// so that the browser can calculate its dimensions. This
// allows the chance to call onPlacement to adjust the XY
// position. After the adjustment, make it visible.
if (typeof cfg.showHow == "string") {
$tip[ cfg.showHow || "show"](cfg.showSpeed);
} else {
$tip.show();
}
$tip.get(0).style.visibility="hidden";
try {
var tipOffset = { x: cfg.offsetX, y: cfg.offsetY };
cfg.onPlacement.apply(
tag,
[evt,
$tip.get(0),
cfg.viewPort?$(cfg.viewPort).get(0):window,
tipOffset
]);
} catch(e) {
alert(e.message);
}
$tip.get(0).style.visibility="visible";
}
The parameters passed to the onPlacement() call are:
tag <--- the current <A> tag (becomes this in function)
evt <--- the mouse event adjusted with .pageX/PageY properties
box <--- a hidden tip <DIV> with the ajax loaded HTML
view <--- the viewport of the browser window. Default "body"
boxOffset <-- X/Y margin of tip box
//-----------------------------------------
// default placement function
//-----------------------------------------
function _onPlacement(evt, box, view, boxOffset) {
// mouse position
var mouseX = evt.pageX;
var mouseY = evt.pageY;
var left = mouseX;
var top = mouseY;
// viewport position and dimensions
var vp = $(view).offset();
vp.width = view.offsetWidth;
vp.height = view.offsetHeight;
if (view.tagName.toUpperCase() == "BODY") { // 1.0.3b
vp.width = view.clientWidth;
vp.height = view.clientHeight;
}
// scroll difference
var ydiff = (evt.pageY - evt.clientY) - vp.top;
var xdiff = (evt.pageX - evt.clientX) - vp.left;
if (ydiff < 0) ydiff = 0;
if (xdiff < 0) xdiff = 0;
// tipbox dimensions
var boxWidth = box.offsetWidth;
var boxHeight = box.offsetHeight;
var halfY = Math.ceil(boxHeight/2);
var halfX = Math.ceil(boxWidth/2);
var marginX = boxOffset? boxOffset.x:16;
var marginY = boxOffset? boxOffset.y:20;
// current tag position and size
var tagXY = $(this).offset();
var tagWidth = this.offsetWidth;
var tagHeight = this.offsetHeight;
var endTag = tagXY.left+tagWidth-mouseX;
//--------------------------------------------------
// Initial position
// Check to see if the box can be display
// to the right at mid box section:
left += endTag + marginX; // right side of tag end
top -= halfY; // box center at mouse Y position
//------------------------------------------
// adjust X
//------------------------------------------
// Can it fit on the right?
var widthAdjusted = false;
if (((left-vp.left) + boxWidth) > vp.width) {
// width adjusted
left = vp.width - boxWidth + vp.left;
widthAdjusted = true;
}
//------------------------------------------
// adjust Y
//------------------------------------------
function moveHalfWayAcross(x)
{
left = (x?x:mouseX) - halfX;
if (left < vp.left) left = vp.left + marginX;
if (left < vp.left) left = mouseX;
}
function moveAboveOrBelow()
{
if (((tagXY.top+tagHeight-vp.top) + boxHeight) > vp.height) {
top = mouseY - boxHeight - tagHeight;
} else {
top = mouseY + marginY;
}
}
// If the top above the view port, and the
// left is still on the right, then
// adjust the top to match the viewport top.
if (((top-ydiff) < vp.top) && (mouseX < left)) {
// place top of box at viewport top position
top = vp.top + ydiff;
// if the width was adjusted, then check to see
// if we need to move the box above or below.
if (widthAdjusted) {
moveHalfWayAcross();
moveAboveOrBelow();
}
}
else
if ((top-ydiff-vp.top + boxHeight) > vp.height) {
// place above mouse
top = vp.top + ydiff + vp.height - boxHeight;
if (widthAdjusted ) {
if ((left-mouseX-endTag-marginX) < 0) {
top = mouseY - boxHeight - marginY;
moveHalfWayAcross(left);
}
}
if (top < 0) top = 0;
}
else {
//==========================
// Keep box at initial spot, however,
// if width adjusted, move above or below
// and move left half box from mouse.
//==========================
if (widthAdjusted) {
moveHalfWayAcross();
moveAboveOrBelow();
}
}
// set position of box
box.style.left = left;
box.style.top = top;
return;
}
thanks
--
HLS