Skip to content

Commit d70e601

Browse files
author
Joel Steres
committed
Compensate for positioned HTML element
1 parent 4a1abf3 commit d70e601

File tree

3 files changed

+119
-27
lines changed

3 files changed

+119
-27
lines changed

src/core.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// useful private variables
1111
var $document = $(document),
1212
$window = $(window),
13+
$html = $(document.documentElement),
1314
$body = $('body');
1415

1516
// constants

src/utility.js

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ function getViewportCollisions(coords, elementWidth, elementHeight) {
170170
// adjusting viewport even though it might be negative because coords
171171
// comparing with are relative to compensated position
172172
var viewportTop = session.scrollTop - session.positionCompensation.top,
173-
viewportLeft = session.scrollLeft - session.positionCompensation.left,
173+
viewportLeft = session.scrollLeft - session.positionCompensation.left,
174174
viewportBottom = viewportTop + session.windowHeight,
175175
viewportRight = viewportLeft + session.windowWidth,
176176
collisions = Collision.none;
@@ -206,38 +206,51 @@ function countFlags(value) {
206206
}
207207

208208
/**
209-
* Compute compensating position offsets if body element has non-standard position attribute.
209+
* Compute compensating position offsets if body or html element has non-static position attribute.
210210
* @private
211211
* @param {number} windowWidth Window width in pixels.
212212
* @param {number} windowHeight Window height in pixels.
213213
* @return {Offsets} The top, left, right, bottom offset in pixels
214214
*/
215215
function computePositionCompensation(windowWidth, windowHeight) {
216-
var bodyWidthWithMargin,
217-
bodyHeightWithMargin,
218-
offsets,
219-
bodyPositionPx;
220-
221-
switch ($body.css('position')) {
222-
case 'absolute':
223-
case 'fixed':
224-
case 'relative':
225-
// jquery offset and position functions return top and left
226-
// offset function computes position + margin
227-
offsets = $body.offset();
228-
bodyPositionPx = $body.position();
229-
// because element might be positioned compute right margin using the different between
230-
// outerWidth computations and add position offset
231-
bodyWidthWithMargin = $body.outerWidth(true);
232-
bodyHeightWithMargin = $body.outerHeight(true);
233-
// right offset = right margin + body right position
234-
offsets.right = (bodyWidthWithMargin - $body.outerWidth() - (offsets.left - bodyPositionPx.left)) + (windowWidth - bodyWidthWithMargin - bodyPositionPx.left);
235-
// bottom offset = bottom margin + body bottom position
236-
offsets.bottom = (bodyHeightWithMargin - $body.outerHeight() - offsets.top) + (windowHeight - bodyHeightWithMargin - bodyPositionPx.top);
237-
break;
238-
default:
239-
// even though body may have offset, no compensation is required
240-
offsets = { top: 0, bottom: 0, left: 0, right: 0 };
216+
// Check if the element is "positioned". A "positioned" element has a CSS
217+
// position value other than static. Whether the element is positioned is
218+
// relevant because absolutely positioned elements are positioned relative
219+
// to the first positioned ancestor rather than relative to the doc origin.
220+
var isPositioned = function(el) {
221+
return el.css('position') !== 'static';
222+
};
223+
224+
var getViewportToElementOffset = function(el) {
225+
var elWidthWithMargin,
226+
elHeightWithMargin,
227+
elPositionPx,
228+
offsets;
229+
// jquery offset and position functions return top and left
230+
// offset function computes position + margin
231+
offsets = el.offset();
232+
elPositionPx = el.position();
233+
234+
// Compute the far margins based off the outerWidth values.
235+
elWidthWithMargin = el.outerWidth(true);
236+
elHeightWithMargin = el.outerHeight(true);
237+
238+
// right offset = right margin + body right position
239+
offsets.right = (elWidthWithMargin - el.outerWidth() - (offsets.left - elPositionPx.left)) + (windowWidth - elWidthWithMargin - elPositionPx.left);
240+
// bottom offset = bottom margin + body bottom position
241+
offsets.bottom = (elHeightWithMargin - el.outerHeight() - offsets.top) + (windowHeight - elHeightWithMargin - elPositionPx.top);
242+
return offsets;
243+
};
244+
245+
var offsets;
246+
247+
if (isPositioned($body)) {
248+
offsets = getViewportToElementOffset($body);
249+
} else if (isPositioned($html)) {
250+
offsets = getViewportToElementOffset($html);
251+
} else {
252+
// even though body may have offset, no compensation is required
253+
offsets = { top: 0, bottom: 0, left: 0, right: 0 };
241254
}
242255

243256
return offsets;

test/htmloffset-rel.html

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<!DOCTYPE html>
2+
<html lang="en-US" style="position:relative">
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>PowerTip Test Suite</title>
6+
7+
<!-- Library Resources -->
8+
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
9+
10+
<!-- PowerTip Core Resources -->
11+
<script type="text/javascript" src="../src/core.js"></script>
12+
<script type="text/javascript" src="../src/csscoordinates.js"></script>
13+
<script type="text/javascript" src="../src/displaycontroller.js"></script>
14+
<script type="text/javascript" src="../src/placementcalculator.js"></script>
15+
<script type="text/javascript" src="../src/tooltipcontroller.js"></script>
16+
<script type="text/javascript" src="../src/utility.js"></script>
17+
<link rel="stylesheet" type="text/css" href="../css/jquery.powertip.css" />
18+
19+
<!-- Unit Test Scripts -->
20+
<script type="text/javascript" src="tests-bodyoffset.js"></script>
21+
22+
<!-- Custom Styles For Test Cases -->
23+
<style type="text/css">
24+
header, section { margin-bottom: 20px; }
25+
section { border: 1px solid #CCC; margin: 20px; padding: 20px; }
26+
#powerTip { white-space: normal; }
27+
#huge-text div, #huge-text-smart div { text-align: center; }
28+
#huge-text input, #huge-text-smart input { margin: 10px; padding: 10px; }
29+
#huge-text .east, #huge-text-smart .east { margin-left: 450px; }
30+
#session { position: fixed; right: 10px; top: 10px; font-size: 10px; width: 160px; background-color: #fff; border: 1px solid #ccc; padding: 10px; overflow: hidden; }
31+
#session pre { margin: 0; }
32+
</style>
33+
</head>
34+
<body>
35+
<section id="huge-text">
36+
<h2>Huge Text</h2>
37+
<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>
38+
<div>
39+
<input type="button" class="north-west-alt" value="North West Alt" />
40+
<input type="button" class="north-west" value="North West" />
41+
<input type="button" class="north" value="North" />
42+
<input type="button" class="north-east" value="North East" />
43+
<input type="button" class="north-east-alt" value="North East Alt" /><br />
44+
<input type="button" class="west" value="West" />
45+
<input type="button" class="east" value="East" /><br />
46+
<input type="button" class="south-west-alt" value="South West Alt" />
47+
<input type="button" class="south-west" value="South West" />
48+
<input type="button" class="south" value="South" />
49+
<input type="button" class="south-east" value="South East" />
50+
<input type="button" class="south-east-alt" value="South East Alt" />
51+
</div>
52+
</section>
53+
<section id="huge-text-smart">
54+
<h2>Huge Text with Smart Placement</h2>
55+
<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>
56+
<div>
57+
<input type="button" class="north-west-alt" value="North West Alt" />
58+
<input type="button" class="north-west" value="North West" />
59+
<input type="button" class="north" value="North" />
60+
<input type="button" class="north-east" value="North East" />
61+
<input type="button" class="north-east-alt" value="North East Alt" /><br />
62+
<input type="button" class="west" value="West" />
63+
<input type="button" class="east" value="East" /><br />
64+
<input type="button" class="south-west-alt" value="South West Alt" />
65+
<input type="button" class="south-west" value="South West" />
66+
<input type="button" class="south" value="South" />
67+
<input type="button" class="south-east" value="South East" />
68+
<input type="button" class="south-east-alt" value="South East Alt" />
69+
</div>
70+
</section>
71+
<section id="trapped-mousefollow" data-powertip="This is the tooltip text.&lt;br /&gt;It is tall so you can test the padding.">
72+
<h2>Trapped mouse following tooltip</h2>
73+
<p>This box has a mouse following tooltip.</p>
74+
<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>
75+
</section>
76+
<div id="session"><pre /></div>
77+
</body>
78+
</html>

0 commit comments

Comments
 (0)