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

Added container tour option, instead of always using document.body #313

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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