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 19, 2018
1 parent 1d5032e commit 04ac385
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 21 deletions.
79 changes: 58 additions & 21 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,6 +1418,8 @@ Hopscotch = function(initOptions) {
adjustWindowScroll = function(cb) {
var bubble = getBubble(),

containerElementOffset = el.getBoundingClientRect(),

// Calculate the bubble element top and bottom position
bubbleEl = bubble.element,
bubbleTop = utils.getPixelValue(bubbleEl.style.top),
Expand All @@ -1398,9 +1432,9 @@ Hopscotch = function(initOptions) {
targetElBottom = targetBounds.bottom + utils.getScrollTop(),

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

// Calculate the current viewport top and bottom
windowTop = utils.getScrollTop(),
Expand All @@ -1414,9 +1448,14 @@ Hopscotch = function(initOptions) {
yuiEase,
direction,
scrollIncr,
scrollTimeout,
scrollTimeoutFn;

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

el.style.top = top + 'px';
el.style.left = left + 'px';

// Target and bubble are both visible in viewport
if (targetTop >= windowTop && (targetTop <= windowTop + getOption('scrollTopMargin') || targetBottom <= windowBottom)) {
if (cb) { cb(); } // HopscotchBubble.show
Expand Down Expand Up @@ -1763,7 +1802,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 @@ -2403,9 +2442,7 @@ Hopscotch = function(initOptions) {
* @returns {Object} The Hopscotch object (for chaining).
*/
this.setEscaper = function(esc){
if (typeof esc === 'function'){
customEscape = esc;
}
customEscape = esc;
return this;
};

Expand All @@ -2421,4 +2458,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 04ac385

Please sign in to comment.