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

Commit

Permalink
Added container tour option, instead of always using document.body
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin-Dobell committed Feb 20, 2018
1 parent 1d5032e commit e9c9f7f
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 23 deletions.
79 changes: 56 additions & 23 deletions src/js/hopscotch.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ utils = {
},

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

//Backwards compatibility: assume the string is an id
Expand Down Expand Up @@ -370,6 +370,23 @@ utils = {
return null;
},

/**
* Returns the container DOM element where bubble elements will be added
* as children. The container element can be specified by tourOpt.container
* as either a string identifier (ID/selector) or directly as a JavaScript
* DOM element. By default, or if the specified string identifier does not
* match an element, the document's body is used.
*
* @private
*/
getContainer: function(tourOpt) {
if (tourOpt.container) {
return typeof tourOpt.container === 'string' ? utils.getElementByIdentifier(tourOpt.container) || document.body : tourOpt.container;
}

return document.body;
},

/**
* Given a step, returns the target DOM element associated with it. It is
* recommended to only assign one target per step. However, there are
Expand All @@ -389,7 +406,7 @@ utils = {

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

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

if (queriedTarget) {
return queriedTarget;
Expand Down Expand Up @@ -596,6 +613,7 @@ HopscotchBubble.prototype = {
left,
arrowOffset,
verticalLeftPosition,
containerElementOffset,
targetEl = utils.getStepTarget(step),
el = this.element,
arrowEl = this.arrowEl,
Expand All @@ -610,7 +628,6 @@ HopscotchBubble.prototype = {

// SET POSITION
boundingRect = targetEl.getBoundingClientRect();

verticalLeftPosition = step.isRtl ? boundingRect.right - bubbleBoundingWidth : boundingRect.left;

if (step.placement === 'top') {
Expand Down Expand Up @@ -672,6 +689,7 @@ HopscotchBubble.prototype = {
else {
left += utils.getPixelValue(step.xOffset);
}

// VERTICAL OFFSET
if (step.yOffset === 'center') {
top = (boundingRect.top + targetEl.offsetHeight/2) - (bubbleBoundingHeight / 2);
Expand All @@ -686,6 +704,20 @@ HopscotchBubble.prototype = {
left += utils.getScrollLeft();
}

// CONVERT TO CONTAINER COORDINATES
el.style.top = '0';
el.style.left = '0';

containerElementOffset = el.getBoundingClientRect();

top -= containerElementOffset.top;
left -= containerElementOffset.left;

if (!this.opt.fixedContainer) {
top -= utils.getScrollTop();
left -= utils.getScrollLeft();
}

// ACCOUNT FOR FIXED POSITION ELEMENTS
el.style.position = (step.fixedElement ? 'fixed' : 'absolute');

Expand Down Expand Up @@ -1048,7 +1080,7 @@ HopscotchBubble.prototype = {
self = this,
resizeCooldown = false, // for updating after window resize
onWinResize,
appendToBody,
appendToContainer,
children,
numChildren,
node,
Expand Down Expand Up @@ -1117,18 +1149,18 @@ HopscotchBubble.prototype = {
//Hide the bubble by default
this.hide();

//Finally, append our new bubble to body once the DOM is ready.
//Finally, append our new bubble to the container once the DOM is ready.

if (utils.documentIsReady()) {
document.body.appendChild(el);
utils.getContainer(opt).appendChild(el);
}
else {
// Moz, webkit, Opera
if (document.addEventListener) {
appendToBody = function() {
document.removeEventListener('DOMContentLoaded', appendToBody);
window.removeEventListener('load', appendToBody);

document.body.appendChild(el);
appendToContainer = function() {
document.removeEventListener('DOMContentLoaded', appendToContainer);
window.removeEventListener('load', appendToContainer);
utils.getContainer(opt).appendChild(el);
};

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

// Calculate the current viewport top and bottom
windowTop = utils.getScrollTop(),
windowBottom = windowTop + utils.getWindowHeight(),

containerTop = utils.getContainer(opt).getBoundingClientRect().top + windowTop,

// Calculate the bubble element top and bottom position
bubbleEl = bubble.element,
bubbleTop = utils.getPixelValue(bubbleEl.style.top),
bubbleTop = utils.getPixelValue(bubbleEl.style.top) + containerTop,
bubbleBottom = bubbleTop + utils.getPixelValue(bubbleEl.offsetHeight),

// Calculate the target element top and bottom position
targetEl = utils.getStepTarget(getCurrStep()),
targetBounds = targetEl.getBoundingClientRect(),
targetElTop = targetBounds.top + utils.getScrollTop(),
targetElBottom = targetBounds.bottom + utils.getScrollTop(),
targetElTop = targetBounds.top + windowTop,
targetElBottom = targetBounds.bottom + windowTop,

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

// Calculate the current viewport top and bottom
windowTop = utils.getScrollTop(),
windowBottom = windowTop + utils.getWindowHeight(),

// This is our final target scroll value.
scrollToVal = targetTop - getOption('scrollTopMargin'),

Expand All @@ -1414,7 +1448,6 @@ Hopscotch = function(initOptions) {
yuiEase,
direction,
scrollIncr,
scrollTimeout,
scrollTimeoutFn;

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

bubble.render(step, stepNum, function(adjustScroll) {
// when done adjusting window scroll, call showBubble helper fn
if (adjustScroll) {
if (adjustScroll && !getOption('fixedContainer')) {
adjustWindowScroll(showBubble);
}
else {
Expand Down Expand Up @@ -2421,4 +2454,4 @@ winHopscotch = new Hopscotch();
// @@include('../../tmp/js/hopscotch_templates.js') //
}.call(winHopscotch));

export default winHopscotch;
export default winHopscotch;
56 changes: 56 additions & 0 deletions test/js/test.hopscotch.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,62 @@ describe('Hopscotch', function() {
hopscotch.endTour();
});

it('should create a div for the HopscotchBubble and append to the specified container element', function() {
var containerEl = document.createElement('div');
document.body.appendChild(containerEl);
hopscotch.startTour({
id: 'hopscotch-test-tour',
steps: [
{
target: 'shopping-list',
placement: 'left',
title: 'Shopping List',
content: 'It\'s a shopping list'
}
],
container: containerEl
});
expect(document.querySelector('.hopscotch-bubble').parentElement).toEqual(containerEl);
hopscotch.endTour();
});

it('should create a div for the HopscotchBubble and append to the container element specified by ID', function() {
var containerEl = document.createElement('div');
containerEl.id = 'container-element';
document.body.appendChild(containerEl);
hopscotch.startTour({
id: 'hopscotch-test-tour',
steps: [
{
target: 'shopping-list',
placement: 'left',
title: 'Shopping List',
content: 'It\'s a shopping list'
}
],
container: 'container-element'
});
expect(document.querySelector('.hopscotch-bubble').parentElement).toEqual(containerEl);
hopscotch.endTour();
});

it('should create a div for the HopscotchBubble and append to body, as the specified container element does not exist', function() {
hopscotch.startTour({
id: 'hopscotch-test-tour',
steps: [
{
target: 'shopping-list',
placement: 'left',
title: 'Shopping List',
content: 'It\'s a shopping list'
}
],
container: 'no-element'
});
expect(document.querySelector('.hopscotch-bubble').parentElement).toEqual(document.body);
hopscotch.endTour();
});

it('should start the tour at the specified step when a step number is supplied as an argument', function() {
hopscotch.startTour({
id: 'hopscotch-test-tour',
Expand Down

0 comments on commit e9c9f7f

Please sign in to comment.