diff --git a/README.md b/README.md index cdbfbb4..2075734 100644 --- a/README.md +++ b/README.md @@ -15,53 +15,115 @@ This library lets you highlight DOMs on the page, and explain what their purpose - Recalculates positions after window resizes. ## Things To Do Still - - Update the library to instantiate programmatically to allow load-time configuration - Update the location calculations to work better on Mobile/Small Screens - Add more configuration options for styling - - Put the CSS in the JS to control/change the class prefixes - Allow overriding the tour's functions (next/prev/exit) ## How To Use -There are currently 3 parts to this: - - CSS - - HTML - - JS - -I'd recommend minifying the CSS and JS before using in production. The files aren't large, but + +#### Prerequisites + + - jQuery 3.x + - A modern browser that has CSS transitions and animations. + - (optional) Bootstrap 3.x/4.x + +**Note:** By default, there are Bootstrap 3.x/4.x classes used for the control links in the tour. +If you want to use them, you'll need to include the Bootstrap style sheet in your HTML. +Otherwise, you'll probably want to add some style classes for the links and set them +in the instantiation options. + +#### Instantiation and Options + +I'd recommend minifying the JS before using in production. The file isn't large, but everything adds up. -Obviously, include the CSS and JavaScript files in your HTML page. I put the CSS in the head -and put the JS script tag as one of the last items in the HTML Body. +Obviously, include the JavaScript file in your HTML page. It's best practice to + put the JS script tag as one of the last items in the HTML Body. + + +###### Basic Instantiation + +The Page Tour JS file should be included _after_ the jQuery library. + +**Note:** When the tour initializes, it appends a style tag and a number of CSS rules to the document body. + If the names of these rules conflict with your application's CSS, you can change the prefix used for the library. + +After the JS file has been included on the page, the library can be instantiated by calling the page tour function: + ```JavaScript +// Instantiate the actual page tour object. +var PageTour; +try { + PageTour = $.fn.PageTour(); +} catch (e) { + console.error('Cannot start page tour: ', e); +} + ``` + +###### Options + +You can instantiate the Page Tour with your own options: + +```JavaScript +var PageTour = $.fn.PageTour({ /* options */ }); +``` + +The following options are exposed: + - `prefix` - [string default: 'tour'] Use this to change the full prefix of the CSS and HTML data attributes + - `horizontalPadding` - [integer default: 20] - Used to add left/right padding to tour elements + - `verticalPadding` - [integer default: 5] - Used to add top/bottom padding to tour elements + - `generalPadding` - [integer default: 5] - Used to add padding to target and shadow elements + - `nextText` - [string default: 'Next'] - The text shown on the "next" link. + - `prevText` - [string default: 'Previous'] - The text shown on the "previous" link. + - `exitText` - [string default: 'Exit'] - The text shown on the "exit" link. + - `nextClass` - [string default: 'btn btn-primary'] - The class for the "next" link. + - `prevClass` - [string default: 'btn btn-default'] - The class for the "previous" link. + - `exitClass` - [string default: 'btn btn-danger'] - The class for the "exit" link. + - `defaultIndex` - [integer default: '9999'] - The default index of items without a manually set index. + #### HTML Attributes In the HTML, for each DOM you want to add to the tour, you must add at least one attribute to the -DOM: - - "data-tour-title" : This attribute should be assigned the title of the DOM object tour stop. +DOM: + - `data--title`: This attribute should be assigned the title of the DOM object tour stop. - example: `
` - - "data-tour-description" : This attribute should be assigned the description of what the tour item is. + - `data--description`: This attribute should be assigned the description of what the tour item is. - example: `
` + +**Note:** In our examples, the prefix is the default "tour" prefix. Both of these attributes can (and really should) be used together. However, only one is necessary. #### Ordering Tour Stops -To enforce an order of which tour items are stopped at, you can add an index attribute: +To enforce an order of which tour items are stopped at, you can add the `data--index` attribute: - example: `
` The tour items are ordered by the index in a general integer sort. So you can skip numbers if that makes -things easy. The order will start at 0. I haven't tested it, but it's possible you can go into -negative numbers. +things easy. You can also use negative numbers. -Items without an index are actually defaulted to an index of `9999`. +Items without an index are actually defaulted to an index of `9999`, though that can be changed via the +`defaultIndex` option. #### Programmatically Controlling the Tour -The tour assigns a window-scoped dict object as `PageTour` at initialization. The `PageTour` object has 4 publicly accessible methods: +The tour assigns a window-scoped dict object as `PageTour` at initialization. The `PageTour` object has 4 publicly +accessible methods: - `.open()` - Opens the tour. If the tour has previously been opened, it will open from where it was left off. - `.next()` - Moves the tour on to the next tour item. - `.prev()` - Moves the tour back to the previous tour item. - `.exit()` - Closes the tour. + - `.rediscover()` - [deprecated] Rediscover any new DOMs on the page. This is not really necessary at all. Each time the tour is + opened, the DOMs are rediscovered. The only method completely necessary for tour operation is the `.open()` method. This starts the tour. However, controls to proceed through the tour and exit the tour are displayed in the tour. + +Example: +```JavaScript +// Instantiate the tour: +var PageTour = $.fn.PageTour(); + +// Open the tour: +PageTour.open(); +``` \ No newline at end of file diff --git a/css/tour.css b/css/tour.css deleted file mode 100644 index 4255cc8..0000000 --- a/css/tour.css +++ /dev/null @@ -1,123 +0,0 @@ -#tour_tour, -#tour_overlay { - height: 100vh; - width: 100%; -} - -#tour_tour { - position: absolute; -} - -.tour_title { - top: 0; - left: 0; - text-shadow: 0 0 10px #aaa; - color: white; - font-size: 2em; - position: absolute; - font-weight: bold !important; - width: 20rem; -} - -.tour_description { - top: 0; - left: 0; - box-shadow: 0 0 7px #666; - border-radius: 7px; - padding: 11px; - color: white; - font-size: 20px; - position: absolute; - width: 35rem; - font-weight: normal !important; -} - -.tour_controls { - top: 0; - left: 0; - margin: 8px 0; - text-align: right; - padding: 11px; - color: white; - font-size: 20px; - position: absolute; - z-index: 10000; - transition: all 0.75s; - transition-timing-function: ease-in-out; -} - -.tour_controls .btn { - margin-right: 10px; - box-shadow: 0 0 3px white; -} - -#tour_targets { - top: 0; - left: 0; - border-radius: 50%; - border: 1px solid #333; - position: absolute; - opacity: 0.3; -} - -.tour_target { - border-radius: 50%; - position: relative; - height: 80%; - width: 80%; - top: 9%; - padding: 0; - margin: 0 auto; -} - -.tour_target_small { - border: 2px solid #aaa; -} - -.tour_target_medium { - border: 2px solid #666; -} - -.tour_element_transition { - transition: all 0.75s; - transition-timing-function: ease-in-out; -} - -.tour_box_transition { - transition-property: top, left; - transition-duration: 0.75s; - transition-timing-function: ease-in-out; -} - -@keyframes targetGlow { - from { - box-shadow: 0 0 1px rgba(64, 64, 64, 0.78); - } - to { - box-shadow: 0 0 8px white; - } -} - -.tour_glow { - animation-duration: 1s; - animation-name: targetGlow; - animation-iteration-count: infinite; - animation-direction: alternate; -} - -.tour_animation_delay1 { - animation-delay: 0.3s; -} - -.tour_animation_delay2 { - animation-delay: 0.6s; -} - -/** shadows **/ -.tour_shade { - overflow: hidden; - transition: all 0.75s; - transition-timing-function: ease-in-out; - position: absolute; - border-radius: 5px; -} \ No newline at end of file diff --git a/index.html b/index.html index c671eea..ed9d100 100644 --- a/index.html +++ b/index.html @@ -2,6 +2,7 @@ + Page Tour Demo - - -

Responsive Web Design Demo

+ +

Responsive Web Design Demo

+

Reference to the original responsive design demo + here.

London

@@ -31,11 +33,12 @@

London

Boom!
-
+ +

Paris

Paris is the capital of France.

The Paris area is one of the largest population centers in Europe, with more than 12 million inhabitants.

- Boomer! + Boomer!
@@ -51,22 +54,43 @@

New York

New York is an important center for international diplomacy and has been described as the cultural and financial capital of the world.

- Boomieist! + Boomieist!
-
- -
- +
+ +   +
+ + + + + + + + + + + + - + + \ No newline at end of file diff --git a/js/tour.js b/js/tour.js index 5f1ead5..b508e21 100644 --- a/js/tour.js +++ b/js/tour.js @@ -1,68 +1,71 @@ -(function (opts) { - - var _o = $.extend({ - prefix : 'tour', - horizontalPadding : 20, - verticalPadding : 5, - generalPadding : 5, - nextText : 'Next', - prevText : 'Previous', - exitText : 'Exit', - thisControl : 'PageTour' - }, opts); - - var dims = {}; - var doms = []; - var elId = 0; - var resizeTimer; - var elements = { - body : $('body'), - overlay : $('
', { id : _o.prefix + '_overlay' }), - tour : $('
', { id : _o.prefix + '_tour' }), - title : $('
', { - class : _o.prefix + '_title ' + _o.prefix + '_box_transition', - }), - description : $('
', { - class : _o.prefix + '_description ' + _o.prefix + '_box_transition', - }), - descriptionText : $(''), - controls : $('
', { - class : _o.prefix + '_controls' - }), - exit : $('', { - class : 'btn btn-danger', - html : _o.exitText, - - }), - targets : $('
', { - id : _o.prefix + '_targets', - class : _o.prefix + '_element_transition ' + _o.prefix + '_glow ' + _o.prefix + '_animation_delay2' - }), - nextBtn : $('', { - html : _o.nextText, - class : 'btn btn-primary' - }), - prevBtn : $('', { - html : _o.prevText, - class : 'btn btn-default' - }), - shadow : $('
', { class : _o.prefix + '_shade' }) - }; +$.fn.PageTour = function (opts) { + var + _o = $.extend({ + prefix : 'tour', + horizontalPadding : 20, + verticalPadding : 5, + generalPadding : 5, + nextText : 'Next', + prevText : 'Previous', + exitText : 'Exit', + nextClass : 'btn btn-primary', + prevClass : 'btn btn-default', + exitClass : 'btn btn-danger', + defaultIndex : 9999 + }, opts), + dims = {}, + doms = [], + elId = 0, + resizeTimer, + elements = { + body : $('body'), + style : $(''), + overlay : $('
', { id : _o.prefix + '_overlay' }), + tour : $('
', { id : _o.prefix + '_tour' }), + title : $('
', { + class : _o.prefix + '_title ' + _o.prefix + '_box_transition' + }), + description : $('
', { + class : _o.prefix + '_description ' + _o.prefix + '_box_transition' + }), + descriptionText : $(''), + controls : $('
', { + class : _o.prefix + '_controls' + }), + targets : $('
', { + id : _o.prefix + '_targets', + class : _o.prefix + '_element_transition ' + _o.prefix + '_glow ' + _o.prefix + '_animation_delay2' + }), + nextBtn : $('', { + html : _o.nextText, + class : _o.nextClass + }), + prevBtn : $('', { + html : _o.prevText, + class : _o.prevClass + }), + exit : $('', { + html : _o.exitText, + class : _o.exitClass + }), + shadow : $('
', { class : _o.prefix + '_shade' }) + }; function __init() { - var medTarg = $('
', { - class : _o.prefix + '_target ' + - _o.prefix + '_target_medium ' + - _o.prefix + '_element_transition ' + - _o.prefix + '_glow ' + - _o.prefix + '_animation_delay1' - }); - var smTarg = $('
', { - class : _o.prefix + '_target ' + - _o.prefix + '_target_small ' + - _o.prefix + '_element_transition ' + - _o.prefix + '_glow' - }); + var + medTarg = $('
', { + class : _o.prefix + '_target ' + + _o.prefix + '_target_medium ' + + _o.prefix + '_element_transition ' + + _o.prefix + '_glow ' + + _o.prefix + '_animation_delay1' + }), + smTarg = $('
', { + class : _o.prefix + '_target ' + + _o.prefix + '_target_small ' + + _o.prefix + '_element_transition ' + + _o.prefix + '_glow' + }); medTarg.append(smTarg); elements.targets.append(medTarg); @@ -70,27 +73,32 @@ elements.prevBtn.on('click', prev); elements.nextBtn.on('click', next); elements.exit.on('click', exit); - elements.description.append(elements.descriptionText); - elements.controls.append(elements.prevBtn, elements.nextBtn, elements.exit); + + elements.description + .append(elements.descriptionText); + + elements.controls + .append( + elements.prevBtn, + elements.nextBtn, + elements.exit + ); + elements.tour + .css({ display : 'none', top : 0, left : 0 }) .append( elements.overlay, elements.title, elements.description, elements.controls, elements.targets - ).css({ 'display' : 'none', 'top' : 0, 'left' : 0 }); - elements.overlay.css({ 'position' : 'absolute', 'top' : 0, 'left' : 0 }) + ); + + elements.overlay + .css({ position : 'absolute', top : 0, left : 0 }) .append(elements.shadow); - elements.body.prepend(elements.tour); - window[ _o.thisControl ] = { - rediscover : discoverDoms, - next : next, - prev : prev, - open : open, - exit : exit - }; + elements.body.prepend(elements.tour); $(window).resize(function () { clearTimeout(resizeTimer); @@ -100,6 +108,17 @@ }, 100); } }); + + setupStyle(); + elements.body.append(elements.style); + + return { + rediscover : discoverDoms, + next : next, + prev : prev, + open : open, + exit : exit + }; } function open() { @@ -157,8 +176,10 @@ }); doms.sort(function (d1, d2) { - var d1i = parseInt(d1.attr('data-' + _o.prefix + '-index') || 9999); - var d2i = parseInt(d2.attr('data-' + _o.prefix + '-index') || 9999); + var + d1i = parseInt(d1.attr('data-' + _o.prefix + '-index') || _o.defaultIndex), + d2i = parseInt(d2.attr('data-' + _o.prefix + '-index') || _o.defaultIndex); + return d1i - d2i; }); } @@ -176,11 +197,12 @@ if (sqSize < 100) sqSize = 100; // Find the X/Y to place the target box - var myX = dims[ 'elCenterX' ] - (sqSize / 2); - var myY = dims[ 'elCenterY' ] - (sqSize / 2); + var + myX = dims[ 'elCenterX' ] - (sqSize / 2), + myY = dims[ 'elCenterY' ] - (sqSize / 2); // Animate the targets to the correct size and coordinates. - $('#tour_targets').css({ + $('#' + _o.prefix + '_targets').css({ width : sqSize, height : sqSize, left : myX - (_o.horizontalPadding / 2) + _o.generalPadding, @@ -205,8 +227,9 @@ 'top' : dims[ 'desc_y' ] + dims[ 'descHeight' ] - _o.verticalPadding }); - var offset = doms[ elId ].offset().top - 120; - var title = dims[ 'title_y' ] - 10; + var + offset = doms[ elId ].offset().top - 120, + title = dims[ 'title_y' ] - 10; $('html, body').animate({ scrollTop : offset > title ? offset : title @@ -216,24 +239,26 @@ function updateTourTitle(title) { var div = elements.title; // Get the title object. - if (title === '') div.hide(); - else div.show(); + if (title === '') + div.hide(); + else + div.show(); // Update the title of the tour. div.html(title); // Set the title. - var newWidth = div.textWidth(); // Figure out the width of the title based on CSS font size and the text hit box size. - div.width(newWidth * 2.5); // Set the width of the title for reflow calculation. + div.width(textWidth.apply(div) * 2.5); // Set the width of the title for reflow calculation. } function updateTourDesc(text) { var div = elements.description; // Get the parent description object. - if (text === '') div.hide(); - else div.show(); + if (text === '') + div.hide(); + else + div.show(); // Update the description of the tour element. var desc = div.children('span').first(); // Get the description span object. - //var controls = div.children('.tour_controls'); // in the future there may be a reason to update the controls. desc.html(text); // Set the description. } @@ -245,11 +270,13 @@ function doTourCalculations() { // Get all the height/width and X/Y coordinates of all the elements involved with the tour // including the window viewport dimensions. - var titleIs; - var titleHeight = parseInt(elements.title.outerHeight()); - var titleWidth = parseInt(elements.title.outerWidth()); - var descHeight = parseInt(elements.description.outerHeight()); - var descWidth = parseInt(elements.description.outerWidth()); + var + titleIs, + titleHeight = parseInt(elements.title.outerHeight()), + titleWidth = parseInt(elements.title.outerWidth()), + descHeight = parseInt(elements.description.outerHeight()), + descWidth = parseInt(elements.description.outerWidth()); + dims[ 'descHeight' ] = descHeight; dims[ 'winWidth' ] = parseInt($(window).outerWidth()); dims[ 'winHeight' ] = parseInt($(window).outerHeight()); @@ -323,7 +350,29 @@ } } - $.fn.textWidth = function () { + function setupStyle() { + elements.style.html( + '#' + _o.prefix + '_tour, #' + _o.prefix + '_overlay { height: 100vh; width: 100%; }\n' + + '#' + _o.prefix + '_tour { position: absolute; }\n' + + '.' + _o.prefix + '_title { top: 0; left: 0; text-shadow: 0 0 10px #aaa; color: white; font-size: 2em; position: absolute; font-weight: bold !important; width: 20rem; }\n' + + '.' + _o.prefix + '_description { top: 0; left: 0; box-shadow: 0 0 7px #666; border-radius: 7px; padding: 11px; color: white; font-size: 20px; position: absolute; width: 35rem; font-weight: normal !important; }\n' + + '.' + _o.prefix + '_controls { top: 0; left: 0; margin: 8px 0; text-align: right; padding: 11px; color: white; font-size: 20px; position: absolute; z-index: 10000; transition: all 0.75s; transition-timing-function: ease-in-out; }\n' + + '.' + _o.prefix + '_controls .btn { margin-right: 10px; box-shadow: 0 0 3px white; }\n' + + '#' + _o.prefix + '_targets { top: 0; left: 0; border-radius: 50%; border: 1px solid #333; position: absolute; opacity: 0.3; }\n' + + '.' + _o.prefix + '_target { border-radius: 50%; position: relative; height: 80%; width: 80%; top: 9%; padding: 0; margin: 0 auto; }\n' + + '.' + _o.prefix + '_target_small { border: 2px solid #aaa; }\n' + + '.' + _o.prefix + '_target_medium { border: 2px solid #666; }\n' + + '.' + _o.prefix + '_element_transition { transition: all 0.75s; transition-timing-function: ease-in-out; }\n' + + '.' + _o.prefix + '_box_transition { transition-property: top, left; transition-duration: 0.75s; transition-timing-function: ease-in-out; }\n' + + '@keyframes targetGlow { from { box-shadow: 0 0 1px rgba(64, 64, 64, 0.78); } to { box-shadow: 0 0 8px white; } }\n' + + '.' + _o.prefix + '_glow { animation-duration: 1s; animation-name: targetGlow; animation-iteration-count: infinite; animation-direction: alternate; }\n' + + '.' + _o.prefix + '_animation_delay1 { animation-delay: 0.3s; }\n' + + '.' + _o.prefix + '_animation_delay2 { animation-delay: 0.6s; }\n' + + '.' + _o.prefix + '_shade { overflow: hidden; transition: all 0.75s; transition-timing-function: ease-in-out; position: absolute; border-radius: 5px; }' + ); + } + + function textWidth() { var org = $(this); var html = $('' + org.html() + ''); html.css({ @@ -337,7 +386,7 @@ var width = html.outerWidth(); html.remove(); return width; - }; + } - __init(); -})({}); + return __init(); +}; \ No newline at end of file