|
1 | 1 | /* ============================================================= |
2 | 2 |
|
3 | | - Smooth Scroll v4.5 |
| 3 | + Smooth Scroll v4.7.0 |
4 | 4 | Animate scrolling to anchor links, by Chris Ferdinandi. |
5 | 5 | http://gomakethings.com |
6 | 6 |
|
|
10 | 10 | Free to use under the MIT License. |
11 | 11 | http://gomakethings.com/mit/ |
12 | 12 |
|
| 13 | + @todo update readme |
| 14 | +
|
13 | 15 | * ============================================================= */ |
14 | 16 |
|
15 | | - (function (root, factory) { |
16 | | - if (typeof define === 'function' && define.amd) { |
| 17 | +(function (root, factory) { |
| 18 | + if ( typeof define === 'function' && define.amd ) { |
17 | 19 | define(factory); |
18 | | - } else if (typeof exports === 'object') { |
| 20 | + } else if ( typeof exports === 'object' ) { |
19 | 21 | module.exports = factory; |
20 | 22 | } else { |
21 | 23 | root.smoothScroll = factory(root); |
22 | 24 | } |
23 | | - })(this, function (root) { |
| 25 | +})(this, function (root) { |
24 | 26 |
|
25 | 27 | 'use strict'; |
26 | 28 |
|
27 | | - var exports = {}; |
28 | | - |
29 | | - var trim = function (str) { |
30 | | - return str.replace(/^\s+|\s+$/g, ''); |
31 | | - }; |
| 29 | + /* |
| 30 | + * Variables |
| 31 | + */ |
32 | 32 |
|
33 | | - var supports = function () { |
34 | | - return 'querySelector' in document && 'addEventListener' in root && Array.prototype.forEach; |
35 | | - }; |
| 33 | + var exports = {}; // Object for public APIs |
| 34 | + var supports = { html5: !!document.querySelector && !!root.addEventListener }; // Feature test |
36 | 35 |
|
37 | 36 | // Default settings |
38 | | - // Private {object} variable |
39 | 37 | var defaults = { |
40 | 38 | speed: 500, |
41 | 39 | easing: 'easeInOutCubic', |
|
45 | 43 | callbackAfter: function () {} |
46 | 44 | }; |
47 | 45 |
|
48 | | - // Merge default settings with user options |
49 | | - // Private method |
50 | | - // Returns an {object} |
51 | | - var extend = function ( target, source ) { |
52 | | - for (var key in source) { |
53 | | - if (Object.prototype.hasOwnProperty.call(source, key)) { |
54 | | - target[key] = source[key]; |
| 46 | + |
| 47 | + /* |
| 48 | + * Methods |
| 49 | + */ |
| 50 | + |
| 51 | + // Merge defaults with user options |
| 52 | + // @private |
| 53 | + // @param {Object} defaults Default settings |
| 54 | + // @param {Object} options User options |
| 55 | + // @returns {Object} Merged values of defaults and options |
| 56 | + var extend = function ( defaults, options ) { |
| 57 | + for ( var key in options ) { |
| 58 | + if (Object.prototype.hasOwnProperty.call(options, key)) { |
| 59 | + defaults[key] = options[key]; |
| 60 | + } |
| 61 | + } |
| 62 | + return defaults; |
| 63 | + }; |
| 64 | + |
| 65 | + // A simple forEach() implementation for Arrays, Objects and NodeLists |
| 66 | + // @private |
| 67 | + // @param {Array|Object|NodeList} collection Collection of items to iterate |
| 68 | + // @param {Function} callback Callback function for each iteration |
| 69 | + // @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`) |
| 70 | + var forEach = function (collection, callback, scope) { |
| 71 | + if (Object.prototype.toString.call(collection) === '[object Object]') { |
| 72 | + for (var prop in collection) { |
| 73 | + if (Object.prototype.hasOwnProperty.call(collection, prop)) { |
| 74 | + callback.call(scope, collection[prop], prop, collection); |
| 75 | + } |
| 76 | + } |
| 77 | + } else { |
| 78 | + for (var i = 0, len = collection.length; i < len; i++) { |
| 79 | + callback.call(scope, collection[i], i, collection); |
55 | 80 | } |
56 | 81 | } |
57 | | - return target; |
58 | 82 | }; |
59 | 83 |
|
60 | 84 | // Calculate the easing pattern |
61 | | - // Private method |
62 | | - // Returns a decimal number |
| 85 | + // @private |
| 86 | + // @param {String} type Easing pattern |
| 87 | + // @param {Number} time Time animation should take to complete |
| 88 | + // @returns {Number} |
63 | 89 | var easingPattern = function ( type, time ) { |
64 | 90 | var pattern; |
65 | 91 | if ( type === 'easeInQuad' ) pattern = time * time; // accelerating from zero velocity |
|
78 | 104 | }; |
79 | 105 |
|
80 | 106 | // Calculate how far to scroll |
81 | | - // Private method |
82 | | - // Returns an integer |
| 107 | + // @private |
| 108 | + // @param {Element} anchor The anchor element to scroll to |
| 109 | + // @param {Number} headerHeight Height of a fixed header, if any |
| 110 | + // @param {Number} offset Number of pixels by which to offset scroll |
| 111 | + // @returns {Number} |
83 | 112 | var getEndLocation = function ( anchor, headerHeight, offset ) { |
84 | 113 | var location = 0; |
85 | 114 | if (anchor.offsetParent) { |
|
93 | 122 | }; |
94 | 123 |
|
95 | 124 | // Determine the document's height |
96 | | - // Private method |
97 | | - // Returns an integer |
| 125 | + // @private |
| 126 | + // @returns {Number} |
98 | 127 | var getDocumentHeight = function () { |
99 | 128 | return Math.max( |
100 | 129 | document.body.scrollHeight, document.documentElement.scrollHeight, |
|
103 | 132 | ); |
104 | 133 | }; |
105 | 134 |
|
| 135 | + // Remove whitespace from a string |
| 136 | + // @private |
| 137 | + // @param {String} string |
| 138 | + // @returns {String} |
| 139 | + var trim = function ( string ) { |
| 140 | + return string.replace(/^\s+|\s+$/g, ''); |
| 141 | + }; |
| 142 | + |
106 | 143 | // Convert data-options attribute into an object of key/value pairs |
107 | | - // Private method |
108 | | - // Returns an {object} |
| 144 | + // @private |
| 145 | + // @param {String} options Link-specific options as a data attribute string |
| 146 | + // @returns {Object} |
109 | 147 | var getDataOptions = function ( options ) { |
110 | 148 | var settings = {}; |
111 | 149 | // Create a key/value pair for each setting |
|
123 | 161 | }; |
124 | 162 |
|
125 | 163 | // Update the URL |
126 | | - // Private method |
127 | | - // Runs functions |
| 164 | + // @private |
| 165 | + // @param {Element} anchor The element to scroll to |
| 166 | + // @param {Boolean} url Whether or not to update the URL history |
128 | 167 | var updateUrl = function ( anchor, url ) { |
129 | 168 | if ( history.pushState && (url || url === 'true') ) { |
130 | 169 | history.pushState( { |
|
134 | 173 | }; |
135 | 174 |
|
136 | 175 | // Start/stop the scrolling animation |
137 | | - // Public method |
138 | | - // Runs functions |
139 | | - exports.animateScroll = function ( toggle, anchor, options, event ) { |
| 176 | + // @public |
| 177 | + // @param {Element} toggle The element that toggled the scroll event |
| 178 | + // @param {Element} anchor The element to scroll to |
| 179 | + // @param {Object} settings |
| 180 | + // @param {Event} event |
| 181 | + exports.animateScroll = function ( toggle, anchor, settings, event ) { |
140 | 182 |
|
141 | 183 | // Options and overrides |
142 | | - options = extend( defaults, options || {} ); // Merge user options with defaults |
| 184 | + settings = extend( defaults, settings || {} ); // Merge user options with defaults |
143 | 185 | var overrides = getDataOptions( toggle ? toggle.getAttribute('data-options') : null ); |
144 | | - var speed = parseInt(overrides.speed || options.speed, 10); |
145 | | - var easing = overrides.easing || options.easing; |
146 | | - var offset = parseInt(overrides.offset || options.offset, 10); |
147 | | - var updateURL = overrides.updateURL || options.updateURL; |
| 186 | + var speed = parseInt(overrides.speed || settings.speed, 10); |
| 187 | + var easing = overrides.easing || settings.easing; |
| 188 | + var offset = parseInt(overrides.offset || settings.offset, 10); |
| 189 | + var updateURL = overrides.updateURL || settings.updateURL; |
148 | 190 |
|
149 | 191 | // Selectors and variables |
150 | 192 | var fixedHeader = document.querySelector('[data-scroll-header]'); // Get the fixed header |
|
166 | 208 | updateUrl(anchor, updateURL); |
167 | 209 |
|
168 | 210 | // Stop the scroll animation when it reaches its target (or the bottom/top of page) |
169 | | - // Private method |
170 | | - // Runs functions |
| 211 | + // @private |
| 212 | + // @param {Number} position Current position on the page |
| 213 | + // @param {Number} endLocation Scroll to location |
| 214 | + // @param {Number} animationInterval How much to scroll on this loop |
171 | 215 | var stopAnimateScroll = function (position, endLocation, animationInterval) { |
172 | 216 | var currentLocation = root.pageYOffset; |
173 | 217 | if ( position == endLocation || currentLocation == endLocation || ( (root.innerHeight + currentLocation) >= documentHeight ) ) { |
174 | 218 | clearInterval(animationInterval); |
175 | | - options.callbackAfter( toggle, anchor ); // Run callbacks after animation complete |
| 219 | + settings.callbackAfter( toggle, anchor ); // Run callbacks after animation complete |
176 | 220 | } |
177 | 221 | }; |
178 | 222 |
|
179 | 223 | // Loop scrolling animation |
180 | | - // Private method |
181 | | - // Runs functions |
| 224 | + // @private |
182 | 225 | var loopAnimateScroll = function () { |
183 | 226 | timeLapsed += 16; |
184 | 227 | percentage = ( timeLapsed / speed ); |
|
189 | 232 | }; |
190 | 233 |
|
191 | 234 | // Set interval timer |
192 | | - // Private method |
193 | | - // Runs functions |
| 235 | + // @private |
194 | 236 | var startAnimateScroll = function () { |
195 | | - options.callbackBefore( toggle, anchor ); // Run callbacks before animating scroll |
| 237 | + settings.callbackBefore( toggle, anchor ); // Run callbacks before animating scroll |
196 | 238 | animationInterval = setInterval(loopAnimateScroll, 16); |
197 | 239 | }; |
198 | 240 |
|
199 | 241 | // Reset position to fix weird iOS bug |
200 | | - // https://github.com/cferdinandi/smooth-scroll/issues/45 |
| 242 | + // @link https://github.com/cferdinandi/smooth-scroll/issues/45 |
201 | 243 | if ( root.pageYOffset === 0 ) { |
202 | 244 | root.scrollTo( 0, 0 ); |
203 | 245 | } |
|
208 | 250 | }; |
209 | 251 |
|
210 | 252 | // Initialize Smooth Scroll |
211 | | - // Public method |
212 | | - // Runs functions |
| 253 | + // @public |
| 254 | + // @param {Object} options User settings |
213 | 255 | exports.init = function ( options ) { |
214 | 256 |
|
215 | 257 | // feature test |
216 | | - if ( !supports() ) return; |
| 258 | + if ( !supports.html5 ) return; |
217 | 259 |
|
218 | 260 | // Selectors and variables |
219 | | - options = extend( defaults, options || {} ); // Merge user options with defaults |
| 261 | + var settings = extend( defaults, options || {} ); // Merge user options with defaults |
220 | 262 | var toggles = document.querySelectorAll('[data-scroll]'); // Get smooth scroll toggles |
221 | 263 |
|
222 | 264 | // When a toggle is clicked, run the click handler |
223 | | - Array.prototype.forEach.call(toggles, function (toggle, index) { |
224 | | - toggle.addEventListener('click', exports.animateScroll.bind( null, toggle, toggle.getAttribute('href'), options ), false); |
| 265 | + forEach(toggles, function (toggle) { |
| 266 | + toggle.addEventListener('click', exports.animateScroll.bind( null, toggle, toggle.getAttribute('href'), settings ), false); |
225 | 267 | }); |
226 | 268 |
|
227 | 269 | }; |
228 | 270 |
|
| 271 | + |
| 272 | + /* |
| 273 | + * Public APIs |
| 274 | + */ |
| 275 | + |
229 | 276 | return exports; |
230 | 277 |
|
231 | 278 | }); |
0 commit comments