Skip to content

Commit e965a0d

Browse files
authored
Merge pull request usablica#801 from usablica/fix-scroll-elements
Scroll parent element to targetElement; resolves usablica#210; resolves usablica#350; resolves usablica#373
2 parents 2a0e609 + b8cde24 commit e965a0d

File tree

3 files changed

+158
-30
lines changed

3 files changed

+158
-30
lines changed

example/hello-world/withScroll.html

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Basic usage</title>
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<meta name="description" content="Intro.js - Better introductions for websites and features with a step-by-step guide for your projects.">
8+
<meta name="author" content="Afshin Mehrabani (@afshinmeh) in usabli.ca group">
9+
10+
<!-- styles -->
11+
<link href="../assets/css/bootstrap.min.css" rel="stylesheet">
12+
<link href="../assets/css/demo.css" rel="stylesheet">
13+
14+
<!-- Add IntroJs styles -->
15+
<link href="../../introjs.css" rel="stylesheet">
16+
17+
<link href="../assets/css/bootstrap-responsive.min.css" rel="stylesheet">
18+
19+
<style>
20+
.marketing {
21+
height: 300px;
22+
height: 40vh;
23+
margin-top: 500px;
24+
margin-top: 40vh;
25+
overflow: auto;
26+
}
27+
</style>
28+
</head>
29+
30+
<body>
31+
32+
<div class="container-narrow">
33+
34+
<div class="masthead">
35+
<ul class="nav nav-pills pull-right">
36+
<li><a href="https://github.com/usablica/intro.js/tags"><i class='icon-black icon-download-alt'></i> Download</a></li>
37+
<li><a href="https://github.com/usablica/intro.js">Github</a></li>
38+
<li><a href="https://twitter.com/usablica">@usablica</a></li>
39+
</ul>
40+
<h3 class="muted">Intro.js</h3>
41+
</div>
42+
43+
<hr>
44+
45+
<div class="jumbotron">
46+
<h1 data-step="3" data-intro="This is a tooltip!">Works with a Scrollable Element</h1>
47+
<p class="lead">This is the basic usage of IntroJs, with <code>data-step</code> and <code>data-intro</code> attributes.</p>
48+
<a class="btn btn-large btn-success" href="javascript:void(0);" onclick="javascript:introJs().start();">Show me how</a>
49+
</div>
50+
51+
<hr>
52+
53+
<div class="row-fluid marketing">
54+
<h4 data-step="2" data-intro="Another step.">Section One</h4>
55+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
56+
57+
<h4>Section Two</h4>
58+
<div>
59+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
60+
<h5>testing</h5>
61+
</div>
62+
63+
<h4>Section Three</h4>
64+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
65+
66+
<h4>Section Four</h4>
67+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
68+
69+
<h4>Section Five</h4>
70+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
71+
72+
<h4 data-step="1" data-intro="A scrolling step.">Section Six</h4>
73+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
74+
75+
<h4>Section Seven</h4>
76+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
77+
78+
<h4>Section Eight</h4>
79+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
80+
81+
<h4>Section Nine</h4>
82+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci
83+
faucibus. Phasellus nec metus purus.</p>
84+
85+
<h4>Section Ten</h4>
86+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
87+
88+
<h4>Section Eleven</h4>
89+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
90+
91+
<h4>Section Twelve</h4>
92+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis augue a neque cursus ac blandit orci faucibus. Phasellus nec metus purus.</p>
93+
</div>
94+
95+
<hr>
96+
</div>
97+
<script type="text/javascript" src="../../intro.js"></script>
98+
</body>
99+
</html>

example/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ <h3 class="muted">Examples</h3>
2525
<li><a href="hello-world/withoutBullets.html" title='Basic usage with buttons'>Basic usage with buttons</a></li>
2626
<li><a href="hello-world/withoutButtons.html" title='Basic usage with bullets'>Basic usage with bullets</a></li>
2727
<li><a href="hello-world/withProgress.html" title='Basic usage with progress-bar'>Basic usage with progress-bar</a></li>
28+
<li><a href="hello-world/withScroll.html" title='Basic usage with a scrolling element'>Basic usage with a scrolling element</a></li>
2829
<li><a href="programmatic/index.html" title='Programmatic defining using JSON'>Programmatic defining using JSON</a></li>
2930
<li><a href="multi-page/index.html" title='Multi-Page introduction'>Multi-Page introduction</a></li>
3031
<li><a href="auto-position/index.html" title='Auto-positioning'>Auto-positioning</a></li>

intro.js

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,7 +1023,8 @@
10231023
highlightClass = 'introjs-helperLayer',
10241024
nextTooltipButton,
10251025
prevTooltipButton,
1026-
skipTooltipButton;
1026+
skipTooltipButton,
1027+
scrollParent;
10271028

10281029
//check for a current step highlight class
10291030
if (typeof (targetElement.highlightClass) === 'string') {
@@ -1058,7 +1059,15 @@
10581059
}
10591060
}
10601061

1061-
//set new position to helper layer
1062+
// scroll to element
1063+
scrollParent = _getScrollParent( targetElement.element );
1064+
1065+
if (scrollParent !== document.body) {
1066+
// target is within a scrollable element
1067+
_scrollParentToElement(scrollParent, targetElement.element);
1068+
}
1069+
1070+
// set new position to helper layer
10621071
_setHelperLayerPosition.call(self, oldHelperLayer);
10631072
_setHelperLayerPosition.call(self, oldReferenceLayer);
10641073

@@ -1126,6 +1135,14 @@
11261135
helperLayer.className = highlightClass;
11271136
referenceLayer.className = 'introjs-tooltipReferenceLayer';
11281137

1138+
// scroll to element
1139+
scrollParent = _getScrollParent( targetElement.element );
1140+
1141+
if (scrollParent !== document.body) {
1142+
// target is within a scrollable element
1143+
_scrollParentToElement(scrollParent, targetElement.element);
1144+
}
1145+
11291146
//set new position to helper layer
11301147
_setHelperLayerPosition.call(self, helperLayer);
11311148
_setHelperLayerPosition.call(self, referenceLayer);
@@ -2104,42 +2121,53 @@
21042121
* @returns Element's position info
21052122
*/
21062123
function _getOffset(element) {
2107-
var elementPosition = {};
2108-
21092124
var body = document.body;
21102125
var docEl = document.documentElement;
2111-
21122126
var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
21132127
var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
2128+
var x = element.getBoundingClientRect();
2129+
return {
2130+
top: x.top + scrollTop,
2131+
width: x.width,
2132+
height: x.height,
2133+
left: x.left + scrollLeft
2134+
};
2135+
}
21142136

2115-
if (element instanceof SVGElement) {
2116-
var x = element.getBoundingClientRect();
2117-
elementPosition.top = x.top + scrollTop;
2118-
elementPosition.width = x.width;
2119-
elementPosition.height = x.height;
2120-
elementPosition.left = x.left + scrollLeft;
2121-
} else {
2122-
//set width
2123-
elementPosition.width = element.offsetWidth;
2124-
2125-
//set height
2126-
elementPosition.height = element.offsetHeight;
2127-
2128-
//calculate element top and left
2129-
var _x = 0;
2130-
var _y = 0;
2131-
while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) {
2132-
_x += element.offsetLeft;
2133-
_y += element.offsetTop;
2134-
element = element.offsetParent;
2137+
/**
2138+
* Find the nearest scrollable parent
2139+
* copied from https://stackoverflow.com/questions/35939886/find-first-scrollable-parent
2140+
*
2141+
* @param Element element
2142+
* @return Element
2143+
*/
2144+
function _getScrollParent(element) {
2145+
var style = window.getComputedStyle(element);
2146+
var excludeStaticParent = (style.position === "absolute");
2147+
var overflowRegex = /(auto|scroll)/;
2148+
2149+
if (style.position === "fixed") return document.body;
2150+
2151+
for (var parent = element; (parent = parent.parentElement);) {
2152+
style = window.getComputedStyle(parent);
2153+
if (excludeStaticParent && style.position === "static") {
2154+
continue;
21352155
}
2136-
//set top
2137-
elementPosition.top = _y;
2138-
//set left
2139-
elementPosition.left = _x;
2156+
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) return parent;
21402157
}
21412158

2142-
return elementPosition;
2159+
return document.body;
2160+
}
2161+
2162+
/**
2163+
* scroll a scrollable element to a child element
2164+
*
2165+
* @param Element parent
2166+
* @param Element element
2167+
* @return Null
2168+
*/
2169+
function _scrollParentToElement (parent, element) {
2170+
parent.scrollTop = element.offsetTop - parent.offsetTop;
21432171
}
21442172

21452173
/**

0 commit comments

Comments
 (0)