Skip to content

Commit

Permalink
Compensate for positioned HTML element
Browse files Browse the repository at this point in the history
  • Loading branch information
Joel Steres committed Jun 12, 2017
1 parent 66baec7 commit 5bf6a63
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 26 deletions.
1 change: 1 addition & 0 deletions src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// useful private variables
var $document = $(document),
$window = $(window),
$html = $(document.documentElement),
$body = $('body');

// constants
Expand Down
65 changes: 39 additions & 26 deletions src/utility.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,38 +207,51 @@ function countFlags(value) {
}

/**
* Compute compensating position offsets if body element has non-standard position attribute.
* Compute compensating position offsets if body or html element has non-static position attribute.
* @private
* @param {number} windowWidth Window width in pixels.
* @param {number} windowHeight Window height in pixels.
* @return {Offsets} The top, left, right, bottom offset in pixels
*/
function computePositionCompensation(windowWidth, windowHeight) {
var bodyWidthWithMargin,
bodyHeightWithMargin,
offsets,
bodyPositionPx;

switch ($body.css('position')) {
case 'absolute':
case 'fixed':
case 'relative':
// jquery offset and position functions return top and left
// offset function computes position + margin
offsets = $body.offset();
bodyPositionPx = $body.position();
// because element might be positioned compute right margin using the different between
// outerWidth computations and add position offset
bodyWidthWithMargin = $body.outerWidth(true);
bodyHeightWithMargin = $body.outerHeight(true);
// right offset = right margin + body right position
offsets.right = (bodyWidthWithMargin - $body.outerWidth() - (offsets.left - bodyPositionPx.left)) + (windowWidth - bodyWidthWithMargin - bodyPositionPx.left);
// bottom offset = bottom margin + body bottom position
offsets.bottom = (bodyHeightWithMargin - $body.outerHeight() - offsets.top) + (windowHeight - bodyHeightWithMargin - bodyPositionPx.top);
break;
default:
// even though body may have offset, no compensation is required
offsets = { top: 0, bottom: 0, left: 0, right: 0 };
// Check if the element is "positioned". A "positioned" element has a CSS
// position value other than static. Whether the element is positioned is
// relevant because absolutely positioned elements are positioned relative
// to the first positioned ancestor rather than relative to the doc origin.
var isPositioned = function(el) {
return el.css('position') !== 'static';
};

var getElementOffsets = function(el) {
var elWidthWithMargin,
elHeightWithMargin,
elPositionPx,
offsets;
// jquery offset and position functions return top and left
// offset function computes position + margin
offsets = el.offset();
elPositionPx = el.position();

// Compute the far margins based off the outerWidth values.
elWidthWithMargin = el.outerWidth(true);
elHeightWithMargin = el.outerHeight(true);

// right offset = right margin + body right position
offsets.right = (elWidthWithMargin - el.outerWidth() - (offsets.left - elPositionPx.left)) + (windowWidth - elWidthWithMargin - elPositionPx.left);
// bottom offset = bottom margin + body bottom position
offsets.bottom = (elHeightWithMargin - el.outerHeight() - offsets.top) + (windowHeight - elHeightWithMargin - elPositionPx.top);
return offsets;
};

var offsets;

if (isPositioned($body)) {
offsets = getElementOffsets($body);
} else if (isPositioned($html)) {
offsets = getElementOffsets($html);
} else {
// even though body may have offset, no compensation is required
offsets = { top: 0, bottom: 0, left: 0, right: 0 };
}

return offsets;
Expand Down
78 changes: 78 additions & 0 deletions test/htmloffset-rel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="en-US" style="position:relative">
<head>
<meta charset="utf-8" />
<title>PowerTip Test Suite</title>

<!-- Library Resources -->
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>

<!-- PowerTip Core Resources -->
<script type="text/javascript" src="../src/core.js"></script>
<script type="text/javascript" src="../src/csscoordinates.js"></script>
<script type="text/javascript" src="../src/displaycontroller.js"></script>
<script type="text/javascript" src="../src/placementcalculator.js"></script>
<script type="text/javascript" src="../src/tooltipcontroller.js"></script>
<script type="text/javascript" src="../src/utility.js"></script>
<link rel="stylesheet" type="text/css" href="../css/jquery.powertip.css" />

<!-- Unit Test Scripts -->
<script type="text/javascript" src="tests-bodyoffset.js"></script>

<!-- Custom Styles For Test Cases -->
<style type="text/css">
header, section { margin-bottom: 20px; }
section { border: 1px solid #CCC; margin: 20px; padding: 20px; }
#powerTip { white-space: normal; }
#huge-text div, #huge-text-smart div { text-align: center; }
#huge-text input, #huge-text-smart input { margin: 10px; padding: 10px; }
#huge-text .east, #huge-text-smart .east { margin-left: 450px; }
#session { position: fixed; right: 10px; top: 10px; font-size: 10px; width: 160px; background-color: #fff; border: 1px solid #ccc; padding: 10px; overflow: hidden; }
#session pre { margin: 0; }
</style>
</head>
<body>
<section id="huge-text">
<h2>Huge Text</h2>
<p>The tooltips for the buttons below have a lot of text. The tooltip div is completely elastic for this demo. The tooltips should be properly placed when they render.</p>
<div>
<input type="button" class="north-west-alt" value="North West Alt" />
<input type="button" class="north-west" value="North West" />
<input type="button" class="north" value="North" />
<input type="button" class="north-east" value="North East" />
<input type="button" class="north-east-alt" value="North East Alt" /><br />
<input type="button" class="west" value="West" />
<input type="button" class="east" value="East" /><br />
<input type="button" class="south-west-alt" value="South West Alt" />
<input type="button" class="south-west" value="South West" />
<input type="button" class="south" value="South" />
<input type="button" class="south-east" value="South East" />
<input type="button" class="south-east-alt" value="South East Alt" />
</div>
</section>
<section id="huge-text-smart">
<h2>Huge Text with Smart Placement</h2>
<p>The tooltips for the buttons below have a lot of text. The tooltip div is completely elastic for this demo. The tooltips should be properly placed when they render.</p>
<div>
<input type="button" class="north-west-alt" value="North West Alt" />
<input type="button" class="north-west" value="North West" />
<input type="button" class="north" value="North" />
<input type="button" class="north-east" value="North East" />
<input type="button" class="north-east-alt" value="North East Alt" /><br />
<input type="button" class="west" value="West" />
<input type="button" class="east" value="East" /><br />
<input type="button" class="south-west-alt" value="South West Alt" />
<input type="button" class="south-west" value="South West" />
<input type="button" class="south" value="South" />
<input type="button" class="south-east" value="South East" />
<input type="button" class="south-east-alt" value="South East Alt" />
</div>
</section>
<section id="trapped-mousefollow" data-powertip="This is the tooltip text.&lt;br /&gt;It is tall so you can test the padding.">
<h2>Trapped mouse following tooltip</h2>
<p>This box has a mouse following tooltip.</p>
<p>Trap it in the bottom right corner of the viewport. It should flip out of the way. It should not flip if it only hits one edge.</p>
</section>
<div id="session"><pre /></div>
</body>
</html>

0 comments on commit 5bf6a63

Please sign in to comment.