Skip to content
This repository was archived by the owner on Feb 17, 2021. It is now read-only.

Commit 2f81e4f

Browse files
Added container tour option, instead of always using document.body
1 parent 1d5032e commit 2f81e4f

File tree

2 files changed

+113
-26
lines changed

2 files changed

+113
-26
lines changed

src/js/hopscotch.js

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ utils = {
328328
},
329329

330330
/**
331-
* Helper function to get a single target DOM element. We will try to
331+
* Helper function to find a DOM element with an identifier. We will try to
332332
* locate the DOM element through several ways, in the following order:
333333
*
334334
* 1) Passing the string into document.querySelector
@@ -341,7 +341,7 @@ utils = {
341341
*
342342
* @private
343343
*/
344-
getStepTargetHelper: function(target){
344+
getElementByIdentifier: function(target) {
345345
var result = document.getElementById(target);
346346

347347
//Backwards compatibility: assume the string is an id
@@ -370,6 +370,23 @@ utils = {
370370
return null;
371371
},
372372

373+
/**
374+
* Returns the container DOM element where bubble elements will be added
375+
* as children. The container element can be specified by tourOpt.container
376+
* as either a string identifier (ID/selector) or directly as a JavaScript
377+
* DOM element. By default, or if the specified string identifier does not
378+
* match an element, the document's body is used.
379+
*
380+
* @private
381+
*/
382+
getContainer: function(tourOpt) {
383+
if (tourOpt.container) {
384+
return typeof tourOpt.container === 'string' ? utils.getElementByIdentifier(tourOpt.container) || document.body : tourOpt.container;
385+
}
386+
387+
return document.body;
388+
},
389+
373390
/**
374391
* Given a step, returns the target DOM element associated with it. It is
375392
* recommended to only assign one target per step. However, there are
@@ -389,7 +406,7 @@ utils = {
389406

390407
if (typeof step.target === 'string') {
391408
//Just one target to test. Check and return its results.
392-
return utils.getStepTargetHelper(step.target);
409+
return utils.getElementByIdentifier(step.target);
393410
}
394411
else if (Array.isArray(step.target)) {
395412
// Multiple items to check. Check each and return the first success.
@@ -399,7 +416,7 @@ utils = {
399416

400417
for (i = 0, len = step.target.length; i < len; i++){
401418
if (typeof step.target[i] === 'string') {
402-
queriedTarget = utils.getStepTargetHelper(step.target[i]);
419+
queriedTarget = utils.getElementByIdentifier(step.target[i]);
403420

404421
if (queriedTarget) {
405422
return queriedTarget;
@@ -596,6 +613,7 @@ HopscotchBubble.prototype = {
596613
left,
597614
arrowOffset,
598615
verticalLeftPosition,
616+
containerElementOffset,
599617
targetEl = utils.getStepTarget(step),
600618
el = this.element,
601619
arrowEl = this.arrowEl,
@@ -610,7 +628,6 @@ HopscotchBubble.prototype = {
610628

611629
// SET POSITION
612630
boundingRect = targetEl.getBoundingClientRect();
613-
614631
verticalLeftPosition = step.isRtl ? boundingRect.right - bubbleBoundingWidth : boundingRect.left;
615632

616633
if (step.placement === 'top') {
@@ -672,6 +689,7 @@ HopscotchBubble.prototype = {
672689
else {
673690
left += utils.getPixelValue(step.xOffset);
674691
}
692+
675693
// VERTICAL OFFSET
676694
if (step.yOffset === 'center') {
677695
top = (boundingRect.top + targetEl.offsetHeight/2) - (bubbleBoundingHeight / 2);
@@ -686,6 +704,20 @@ HopscotchBubble.prototype = {
686704
left += utils.getScrollLeft();
687705
}
688706

707+
// CONVERT TO CONTAINER COORDINATES
708+
el.style.top = '0';
709+
el.style.left = '0';
710+
711+
containerElementOffset = el.getBoundingClientRect();
712+
713+
top -= containerElementOffset.top;
714+
left -= containerElementOffset.left;
715+
716+
if (!this.opt.fixedContainer) {
717+
top -= utils.getScrollTop();
718+
left -= utils.getScrollLeft();
719+
}
720+
689721
// ACCOUNT FOR FIXED POSITION ELEMENTS
690722
el.style.position = (step.fixedElement ? 'fixed' : 'absolute');
691723

@@ -1048,7 +1080,7 @@ HopscotchBubble.prototype = {
10481080
self = this,
10491081
resizeCooldown = false, // for updating after window resize
10501082
onWinResize,
1051-
appendToBody,
1083+
appendToContainer,
10521084
children,
10531085
numChildren,
10541086
node,
@@ -1117,18 +1149,18 @@ HopscotchBubble.prototype = {
11171149
//Hide the bubble by default
11181150
this.hide();
11191151

1120-
//Finally, append our new bubble to body once the DOM is ready.
1152+
//Finally, append our new bubble to the container once the DOM is ready.
1153+
11211154
if (utils.documentIsReady()) {
1122-
document.body.appendChild(el);
1155+
utils.getContainer(opt).appendChild(el);
11231156
}
11241157
else {
11251158
// Moz, webkit, Opera
11261159
if (document.addEventListener) {
1127-
appendToBody = function() {
1128-
document.removeEventListener('DOMContentLoaded', appendToBody);
1129-
window.removeEventListener('load', appendToBody);
1130-
1131-
document.body.appendChild(el);
1160+
appendToContainer = function() {
1161+
document.removeEventListener('DOMContentLoaded', appendToContainer);
1162+
window.removeEventListener('load', appendToContainer);
1163+
utils.getContainer(opt).appendChild(el);
11321164
};
11331165

11341166
document.addEventListener('DOMContentLoaded', appendToBody, false);
@@ -1386,26 +1418,28 @@ Hopscotch = function(initOptions) {
13861418
adjustWindowScroll = function(cb) {
13871419
var bubble = getBubble(),
13881420

1421+
// Calculate the current viewport top and bottom
1422+
windowTop = utils.getScrollTop(),
1423+
windowBottom = windowTop + utils.getWindowHeight(),
1424+
1425+
containerTop = utils.getContainer(opt).getBoundingClientRect().top + windowTop,
1426+
13891427
// Calculate the bubble element top and bottom position
13901428
bubbleEl = bubble.element,
1391-
bubbleTop = utils.getPixelValue(bubbleEl.style.top),
1429+
bubbleTop = utils.getPixelValue(bubbleEl.style.top) + containerTop,
13921430
bubbleBottom = bubbleTop + utils.getPixelValue(bubbleEl.offsetHeight),
13931431

13941432
// Calculate the target element top and bottom position
13951433
targetEl = utils.getStepTarget(getCurrStep()),
13961434
targetBounds = targetEl.getBoundingClientRect(),
1397-
targetElTop = targetBounds.top + utils.getScrollTop(),
1398-
targetElBottom = targetBounds.bottom + utils.getScrollTop(),
1435+
targetElTop = targetBounds.top + windowTop,
1436+
targetElBottom = targetBounds.bottom + windowTop,
13991437

14001438
// The higher of the two: bubble or target
14011439
targetTop = (bubbleTop < targetElTop) ? bubbleTop : targetElTop,
14021440
// The lower of the two: bubble or target
14031441
targetBottom = (bubbleBottom > targetElBottom) ? bubbleBottom : targetElBottom,
14041442

1405-
// Calculate the current viewport top and bottom
1406-
windowTop = utils.getScrollTop(),
1407-
windowBottom = windowTop + utils.getWindowHeight(),
1408-
14091443
// This is our final target scroll value.
14101444
scrollToVal = targetTop - getOption('scrollTopMargin'),
14111445

@@ -1414,7 +1448,6 @@ Hopscotch = function(initOptions) {
14141448
yuiEase,
14151449
direction,
14161450
scrollIncr,
1417-
scrollTimeout,
14181451
scrollTimeoutFn;
14191452

14201453
// Target and bubble are both visible in viewport
@@ -1763,7 +1796,7 @@ Hopscotch = function(initOptions) {
17631796

17641797
bubble.render(step, stepNum, function(adjustScroll) {
17651798
// when done adjusting window scroll, call showBubble helper fn
1766-
if (adjustScroll) {
1799+
if (adjustScroll && !getOption('fixedContainer')) {
17671800
adjustWindowScroll(showBubble);
17681801
}
17691802
else {
@@ -2403,9 +2436,7 @@ Hopscotch = function(initOptions) {
24032436
* @returns {Object} The Hopscotch object (for chaining).
24042437
*/
24052438
this.setEscaper = function(esc){
2406-
if (typeof esc === 'function'){
2407-
customEscape = esc;
2408-
}
2439+
customEscape = esc;
24092440
return this;
24102441
};
24112442

@@ -2421,4 +2452,4 @@ winHopscotch = new Hopscotch();
24212452
// @@include('../../tmp/js/hopscotch_templates.js') //
24222453
}.call(winHopscotch));
24232454

2424-
export default winHopscotch;
2455+
export default winHopscotch;

test/js/test.hopscotch.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,62 @@ describe('Hopscotch', function() {
138138
hopscotch.endTour();
139139
});
140140

141+
it('should create a div for the HopscotchBubble and append to the specified container element', function() {
142+
var containerEl = document.createElement('div');
143+
document.body.appendChild(containerEl);
144+
hopscotch.startTour({
145+
id: 'hopscotch-test-tour',
146+
steps: [
147+
{
148+
target: 'shopping-list',
149+
placement: 'left',
150+
title: 'Shopping List',
151+
content: 'It\'s a shopping list'
152+
}
153+
],
154+
container: containerEl
155+
});
156+
expect(document.querySelector('.hopscotch-bubble').parentElement).toEqual(containerEl);
157+
hopscotch.endTour();
158+
});
159+
160+
it('should create a div for the HopscotchBubble and append to the container element specified by ID', function() {
161+
var containerEl = document.createElement('div');
162+
containerEl.id = 'container-element';
163+
document.body.appendChild(containerEl);
164+
hopscotch.startTour({
165+
id: 'hopscotch-test-tour',
166+
steps: [
167+
{
168+
target: 'shopping-list',
169+
placement: 'left',
170+
title: 'Shopping List',
171+
content: 'It\'s a shopping list'
172+
}
173+
],
174+
container: 'container-element'
175+
});
176+
expect(document.querySelector('.hopscotch-bubble').parentElement).toEqual(containerEl);
177+
hopscotch.endTour();
178+
});
179+
180+
it('should create a div for the HopscotchBubble and append to body, as the specified container element does not exist', function() {
181+
hopscotch.startTour({
182+
id: 'hopscotch-test-tour',
183+
steps: [
184+
{
185+
target: 'shopping-list',
186+
placement: 'left',
187+
title: 'Shopping List',
188+
content: 'It\'s a shopping list'
189+
}
190+
],
191+
container: 'no-element'
192+
});
193+
expect(document.querySelector('.hopscotch-bubble').parentElement).toEqual(document.body);
194+
hopscotch.endTour();
195+
});
196+
141197
it('should start the tour at the specified step when a step number is supplied as an argument', function() {
142198
hopscotch.startTour({
143199
id: 'hopscotch-test-tour',

0 commit comments

Comments
 (0)