From 442c9b1c55b57bc44dfd45e2ae7df5722e837263 Mon Sep 17 00:00:00 2001 From: Tim Underhay <15734900+citizentim@users.noreply.github.com> Date: Wed, 9 May 2018 11:57:17 -0600 Subject: [PATCH 1/4] Added support for passive event listeners with Hamster(el, true) Updated readme as appropriate --- README.md | 8 ++++++-- bower.json | 2 +- hamster.js | 22 +++++++++++++++++----- package.json | 2 +- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 81d1904..046baf8 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Usage The event callback receives 3 extra arguments which are the normalized “deltas” of the mouse wheel. ```js -var hamster = Hamster(el); +var hamster = Hamster(el, false); // boolean is whether passive = true | false hamster.wheel(function(event, delta, deltaX, deltaY){ console.log(delta, deltaX, deltaY); @@ -24,13 +24,16 @@ hamster.unwheel(); Support ------- -No jQuery or other libraries required, but an adapter for AngularJS is available. +The second parameter to Hamster() is optional, and indicates whether to mark the event listener as passive, for performance. Please do your research about passive event listeners before turning this on. + +No jQuery or other libraries are required, but an adapter for AngularJS is available. Tested in these [core browsers](http://monospaced.github.io/obs). Install ------- + npm install hamsterjs bower install hamsterjs Demo @@ -43,3 +46,4 @@ Reference * [jquery-mousewheel](https://github.com/brandonaaron/jquery-mousewheel) * [wheel event on MDN](https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/wheel) +* [Passive Event Listeners by Google](https://developers.google.com/web/updates/2016/06/passive-event-listeners) diff --git a/bower.json b/bower.json index 09e5fa8..a9526f2 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "hamsterjs", - "version": "1.1.2", + "version": "1.1.4", "main": "hamster.js", "ignore": [ "**/.*", diff --git a/hamster.js b/hamster.js index ac99dc8..589cc26 100644 --- a/hamster.js +++ b/hamster.js @@ -1,9 +1,14 @@ /* - * Hamster.js v1.1.2 + * Hamster.js v1.1.4 * (c) 2013 Monospaced http://monospaced.com * License: MIT */ + /* + MODIFIED FOR PASSIVE EVENT LISTENERS + BY KENSINGTON TECHNOLOGY ASSOCIATES + */ + (function(window, document){ 'use strict'; @@ -13,8 +18,8 @@ * @returns {Hamster.Instance} * @constructor */ -var Hamster = function(element) { - return new Hamster.Instance(element); +var Hamster = function(element, passive) { + return new Hamster.Instance(element, passive); }; // default event name @@ -28,7 +33,7 @@ Hamster.PREFIX = ''; // until browser inconsistencies have been fixed... Hamster.READY = false; -Hamster.Instance = function(element){ +Hamster.Instance = function(element, passive){ if (!Hamster.READY) { // fix browser inconsistencies Hamster.normalise.browser(); @@ -39,6 +44,8 @@ Hamster.Instance = function(element){ this.element = element; + this.passive = passive || false; + // store attached event handlers this.handlers = []; @@ -125,7 +132,12 @@ Hamster.event = { }; // cross-browser addEventListener - hamster.element[Hamster.ADD_EVENT](Hamster.PREFIX + eventName, handler, useCapture || false); + if (!hamster.passive) { + hamster.element[Hamster.ADD_EVENT](Hamster.PREFIX + eventName, handler, useCapture || false); + } + else { + hamster.element[Hamster.ADD_EVENT](Hamster.PREFIX + eventName, handler, { passive: true } ); + } // store original and normalised handlers on the instance hamster.handlers.push({ diff --git a/package.json b/package.json index 06ac503..becde98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hamsterjs", - "version": "1.1.3", + "version": "1.1.4", "description": "A standalone javascript library for cross-browser mouse wheel support.", "keywords": [ "hamster", From f1868ba8c587e839dbea829d872bb388956e2471 Mon Sep 17 00:00:00 2001 From: Tim Underhay <15734900+citizentim@users.noreply.github.com> Date: Thu, 10 May 2018 11:58:26 -0600 Subject: [PATCH 2/4] Typescript definitions --- hamster.d.ts | 9 + hamster.js | 564 +++++++++++++++++++++++++-------------------------- package.json | 7 +- 3 files changed, 295 insertions(+), 285 deletions(-) create mode 100644 hamster.d.ts diff --git a/hamster.d.ts b/hamster.d.ts new file mode 100644 index 0000000..060bc63 --- /dev/null +++ b/hamster.d.ts @@ -0,0 +1,9 @@ +// Type definitions for Hamster.js 1.1.4 +// Definitions by: Kensington Technology associates <[~https://knowledgekta.com~]> + +declare function Hamster(el: HTMLElement, passive?: boolean): Hamster; + +interface Hamster { + wheel( handler, useCapture?: boolean ); + unwheel(); +} diff --git a/hamster.js b/hamster.js index 589cc26..c28f65a 100644 --- a/hamster.js +++ b/hamster.js @@ -12,328 +12,328 @@ (function(window, document){ 'use strict'; -/** - * Hamster - * use this to create instances - * @returns {Hamster.Instance} - * @constructor - */ -var Hamster = function(element, passive) { - return new Hamster.Instance(element, passive); -}; - -// default event name -Hamster.SUPPORT = 'wheel'; - -// default DOM methods -Hamster.ADD_EVENT = 'addEventListener'; -Hamster.REMOVE_EVENT = 'removeEventListener'; -Hamster.PREFIX = ''; - -// until browser inconsistencies have been fixed... -Hamster.READY = false; - -Hamster.Instance = function(element, passive){ - if (!Hamster.READY) { - // fix browser inconsistencies - Hamster.normalise.browser(); - - // Hamster is ready...! - Hamster.READY = true; - } - - this.element = element; - - this.passive = passive || false; - - // store attached event handlers - this.handlers = []; - - // return instance - return this; -}; - -/** - * create new hamster instance - * all methods should return the instance itself, so it is chainable. - * @param {HTMLElement} element - * @returns {Hamster.Instance} - * @constructor - */ -Hamster.Instance.prototype = { /** - * bind events to the instance - * @param {Function} handler - * @param {Boolean} useCapture + * Hamster + * use this to create instances * @returns {Hamster.Instance} + * @constructor */ - wheel: function onEvent(handler, useCapture){ - Hamster.event.add(this, Hamster.SUPPORT, handler, useCapture); + var Hamster = function(element, passive) { + return new Hamster.Instance(element, passive); + }; - // handle MozMousePixelScroll in older Firefox - if (Hamster.SUPPORT === 'DOMMouseScroll') { - Hamster.event.add(this, 'MozMousePixelScroll', handler, useCapture); - } + // default event name + Hamster.SUPPORT = 'wheel'; - return this; - }, + // default DOM methods + Hamster.ADD_EVENT = 'addEventListener'; + Hamster.REMOVE_EVENT = 'removeEventListener'; + Hamster.PREFIX = ''; - /** - * unbind events to the instance - * @param {Function} handler - * @param {Boolean} useCapture - * @returns {Hamster.Instance} - */ - unwheel: function offEvent(handler, useCapture){ - // if no handler argument, - // unbind the last bound handler (if exists) - if (handler === undefined && (handler = this.handlers.slice(-1)[0])) { - handler = handler.original; - } + // until browser inconsistencies have been fixed... + Hamster.READY = false; - Hamster.event.remove(this, Hamster.SUPPORT, handler, useCapture); + Hamster.Instance = function(element, passive){ + if (!Hamster.READY) { + // fix browser inconsistencies + Hamster.normalise.browser(); - // handle MozMousePixelScroll in older Firefox - if (Hamster.SUPPORT === 'DOMMouseScroll') { - Hamster.event.remove(this, 'MozMousePixelScroll', handler, useCapture); + // Hamster is ready...! + Hamster.READY = true; } + this.element = element; + + this.passive = passive || false; + + // store attached event handlers + this.handlers = []; + + // return instance return this; - } -}; + }; -Hamster.event = { /** - * cross-browser 'addWheelListener' - * @param {Instance} hamster - * @param {String} eventName - * @param {Function} handler - * @param {Boolean} useCapture + * create new hamster instance + * all methods should return the instance itself, so it is chainable. + * @param {HTMLElement} element + * @returns {Hamster.Instance} + * @constructor */ - add: function add(hamster, eventName, handler, useCapture){ - // store the original handler - var originalHandler = handler; - - // redefine the handler - handler = function(originalEvent){ - - if (!originalEvent) { - originalEvent = window.event; + Hamster.Instance.prototype = { + /** + * bind events to the instance + * @param {Function} handler + * @param {Boolean} useCapture + * @returns {Hamster.Instance} + */ + wheel: function onEvent(handler, useCapture){ + Hamster.event.add(this, Hamster.SUPPORT, handler, useCapture); + + // handle MozMousePixelScroll in older Firefox + if (Hamster.SUPPORT === 'DOMMouseScroll') { + Hamster.event.add(this, 'MozMousePixelScroll', handler, useCapture); } - // create a normalised event object, - // and normalise "deltas" of the mouse wheel - var event = Hamster.normalise.event(originalEvent), - delta = Hamster.normalise.delta(originalEvent); - - // fire the original handler with normalised arguments - return originalHandler(event, delta[0], delta[1], delta[2]); + return this; + }, + + /** + * unbind events to the instance + * @param {Function} handler + * @param {Boolean} useCapture + * @returns {Hamster.Instance} + */ + unwheel: function offEvent(handler, useCapture){ + // if no handler argument, + // unbind the last bound handler (if exists) + if (handler === undefined && (handler = this.handlers.slice(-1)[0])) { + handler = handler.original; + } - }; + Hamster.event.remove(this, Hamster.SUPPORT, handler, useCapture); - // cross-browser addEventListener - if (!hamster.passive) { - hamster.element[Hamster.ADD_EVENT](Hamster.PREFIX + eventName, handler, useCapture || false); - } - else { - hamster.element[Hamster.ADD_EVENT](Hamster.PREFIX + eventName, handler, { passive: true } ); - } - - // store original and normalised handlers on the instance - hamster.handlers.push({ - original: originalHandler, - normalised: handler - }); - }, + // handle MozMousePixelScroll in older Firefox + if (Hamster.SUPPORT === 'DOMMouseScroll') { + Hamster.event.remove(this, 'MozMousePixelScroll', handler, useCapture); + } - /** - * removeWheelListener - * @param {Instance} hamster - * @param {String} eventName - * @param {Function} handler - * @param {Boolean} useCapture - */ - remove: function remove(hamster, eventName, handler, useCapture){ - // find the normalised handler on the instance - var originalHandler = handler, - lookup = {}, - handlers; - for (var i = 0, len = hamster.handlers.length; i < len; ++i) { - lookup[hamster.handlers[i].original] = hamster.handlers[i]; + return this; } - handlers = lookup[originalHandler]; - handler = handlers.normalised; - - // cross-browser removeEventListener - hamster.element[Hamster.REMOVE_EVENT](Hamster.PREFIX + eventName, handler, useCapture || false); + }; + + Hamster.event = { + /** + * cross-browser 'addWheelListener' + * @param {Instance} hamster + * @param {String} eventName + * @param {Function} handler + * @param {Boolean} useCapture + */ + add: function add(hamster, eventName, handler, useCapture){ + // store the original handler + var originalHandler = handler; + + // redefine the handler + handler = function(originalEvent){ + + if (!originalEvent) { + originalEvent = window.event; + } + + // create a normalised event object, + // and normalise "deltas" of the mouse wheel + var event = Hamster.normalise.event(originalEvent), + delta = Hamster.normalise.delta(originalEvent); + + // fire the original handler with normalised arguments + return originalHandler(event, delta[0], delta[1], delta[2]); + + }; + + // cross-browser addEventListener + if (!hamster.passive) { + hamster.element[Hamster.ADD_EVENT](Hamster.PREFIX + eventName, handler, useCapture || false); + } + else { + hamster.element[Hamster.ADD_EVENT](Hamster.PREFIX + eventName, handler, { passive: true } ); + } - // remove original and normalised handlers from the instance - for (var h in hamster.handlers) { - if (hamster.handlers[h] == handlers) { - hamster.handlers.splice(h, 1); - break; + // store original and normalised handlers on the instance + hamster.handlers.push({ + original: originalHandler, + normalised: handler + }); + }, + + /** + * removeWheelListener + * @param {Instance} hamster + * @param {String} eventName + * @param {Function} handler + * @param {Boolean} useCapture + */ + remove: function remove(hamster, eventName, handler, useCapture){ + // find the normalised handler on the instance + var originalHandler = handler, + lookup = {}, + handlers; + for (var i = 0, len = hamster.handlers.length; i < len; ++i) { + lookup[hamster.handlers[i].original] = hamster.handlers[i]; + } + handlers = lookup[originalHandler]; + handler = handlers.normalised; + + // cross-browser removeEventListener + hamster.element[Hamster.REMOVE_EVENT](Hamster.PREFIX + eventName, handler, useCapture || false); + + // remove original and normalised handlers from the instance + for (var h in hamster.handlers) { + if (hamster.handlers[h] == handlers) { + hamster.handlers.splice(h, 1); + break; + } } } - } -}; - -/** - * these hold the lowest deltas, - * used to normalise the delta values - * @type {Number} - */ -var lowestDelta, - lowestDeltaXY; + }; -Hamster.normalise = { /** - * fix browser inconsistencies + * these hold the lowest deltas, + * used to normalise the delta values + * @type {Number} */ - browser: function normaliseBrowser(){ - // detect deprecated wheel events - if (!('onwheel' in document || document.documentMode >= 9)) { - Hamster.SUPPORT = document.onmousewheel !== undefined ? - 'mousewheel' : // webkit and IE < 9 support at least "mousewheel" - 'DOMMouseScroll'; // assume remaining browsers are older Firefox - } - - // detect deprecated event model - if (!window.addEventListener) { - // assume IE < 9 - Hamster.ADD_EVENT = 'attachEvent'; - Hamster.REMOVE_EVENT = 'detachEvent'; - Hamster.PREFIX = 'on'; - } + var lowestDelta, + lowestDeltaXY; + + Hamster.normalise = { + /** + * fix browser inconsistencies + */ + browser: function normaliseBrowser(){ + // detect deprecated wheel events + if (!('onwheel' in document || document.documentMode >= 9)) { + Hamster.SUPPORT = document.onmousewheel !== undefined ? + 'mousewheel' : // webkit and IE < 9 support at least "mousewheel" + 'DOMMouseScroll'; // assume remaining browsers are older Firefox + } - }, + // detect deprecated event model + if (!window.addEventListener) { + // assume IE < 9 + Hamster.ADD_EVENT = 'attachEvent'; + Hamster.REMOVE_EVENT = 'detachEvent'; + Hamster.PREFIX = 'on'; + } - /** - * create a normalised event object - * @param {Function} originalEvent - * @returns {Object} event - */ - event: function normaliseEvent(originalEvent){ - var event = { - // keep a reference to the original event object - originalEvent: originalEvent, - target: originalEvent.target || originalEvent.srcElement, - type: 'wheel', - deltaMode: originalEvent.type === 'MozMousePixelScroll' ? 0 : 1, - deltaX: 0, - deltaZ: 0, - preventDefault: function(){ - if (originalEvent.preventDefault) { - originalEvent.preventDefault(); - } else { - originalEvent.returnValue = false; + }, + + /** + * create a normalised event object + * @param {Function} originalEvent + * @returns {Object} event + */ + event: function normaliseEvent(originalEvent){ + var event = { + // keep a reference to the original event object + originalEvent: originalEvent, + target: originalEvent.target || originalEvent.srcElement, + type: 'wheel', + deltaMode: originalEvent.type === 'MozMousePixelScroll' ? 0 : 1, + deltaX: 0, + deltaZ: 0, + preventDefault: function(){ + if (originalEvent.preventDefault) { + originalEvent.preventDefault(); + } else { + originalEvent.returnValue = false; + } + }, + stopPropagation: function(){ + if (originalEvent.stopPropagation) { + originalEvent.stopPropagation(); + } else { + originalEvent.cancelBubble = false; + } } - }, - stopPropagation: function(){ - if (originalEvent.stopPropagation) { - originalEvent.stopPropagation(); - } else { - originalEvent.cancelBubble = false; - } - } - }; + }; - // calculate deltaY (and deltaX) according to the event + // calculate deltaY (and deltaX) according to the event - // 'mousewheel' - if (originalEvent.wheelDelta) { - event.deltaY = - 1/40 * originalEvent.wheelDelta; - } - // webkit - if (originalEvent.wheelDeltaX) { - event.deltaX = - 1/40 * originalEvent.wheelDeltaX; - } + // 'mousewheel' + if (originalEvent.wheelDelta) { + event.deltaY = - 1/40 * originalEvent.wheelDelta; + } + // webkit + if (originalEvent.wheelDeltaX) { + event.deltaX = - 1/40 * originalEvent.wheelDeltaX; + } - // 'DomMouseScroll' - if (originalEvent.detail) { - event.deltaY = originalEvent.detail; - } + // 'DomMouseScroll' + if (originalEvent.detail) { + event.deltaY = originalEvent.detail; + } - return event; - }, + return event; + }, + + /** + * normalise 'deltas' of the mouse wheel + * @param {Function} originalEvent + * @returns {Array} deltas + */ + delta: function normaliseDelta(originalEvent){ + var delta = 0, + deltaX = 0, + deltaY = 0, + absDelta = 0, + absDeltaXY = 0, + fn; + + // normalise deltas according to the event + + // 'wheel' event + if (originalEvent.deltaY) { + deltaY = originalEvent.deltaY * -1; + delta = deltaY; + } + if (originalEvent.deltaX) { + deltaX = originalEvent.deltaX; + delta = deltaX * -1; + } - /** - * normalise 'deltas' of the mouse wheel - * @param {Function} originalEvent - * @returns {Array} deltas - */ - delta: function normaliseDelta(originalEvent){ - var delta = 0, - deltaX = 0, - deltaY = 0, - absDelta = 0, - absDeltaXY = 0, - fn; - - // normalise deltas according to the event - - // 'wheel' event - if (originalEvent.deltaY) { - deltaY = originalEvent.deltaY * -1; - delta = deltaY; - } - if (originalEvent.deltaX) { - deltaX = originalEvent.deltaX; - delta = deltaX * -1; - } + // 'mousewheel' event + if (originalEvent.wheelDelta) { + delta = originalEvent.wheelDelta; + } + // webkit + if (originalEvent.wheelDeltaY) { + deltaY = originalEvent.wheelDeltaY; + } + if (originalEvent.wheelDeltaX) { + deltaX = originalEvent.wheelDeltaX * -1; + } - // 'mousewheel' event - if (originalEvent.wheelDelta) { - delta = originalEvent.wheelDelta; - } - // webkit - if (originalEvent.wheelDeltaY) { - deltaY = originalEvent.wheelDeltaY; - } - if (originalEvent.wheelDeltaX) { - deltaX = originalEvent.wheelDeltaX * -1; - } + // 'DomMouseScroll' event + if (originalEvent.detail) { + delta = originalEvent.detail * -1; + } - // 'DomMouseScroll' event - if (originalEvent.detail) { - delta = originalEvent.detail * -1; - } + // Don't return NaN + if (delta === 0) { + return [0, 0, 0]; + } - // Don't return NaN - if (delta === 0) { - return [0, 0, 0]; - } + // look for lowest delta to normalize the delta values + absDelta = Math.abs(delta); + if (!lowestDelta || absDelta < lowestDelta) { + lowestDelta = absDelta; + } + absDeltaXY = Math.max(Math.abs(deltaY), Math.abs(deltaX)); + if (!lowestDeltaXY || absDeltaXY < lowestDeltaXY) { + lowestDeltaXY = absDeltaXY; + } - // look for lowest delta to normalize the delta values - absDelta = Math.abs(delta); - if (!lowestDelta || absDelta < lowestDelta) { - lowestDelta = absDelta; - } - absDeltaXY = Math.max(Math.abs(deltaY), Math.abs(deltaX)); - if (!lowestDeltaXY || absDeltaXY < lowestDeltaXY) { - lowestDeltaXY = absDeltaXY; - } + // convert deltas to whole numbers + fn = delta > 0 ? 'floor' : 'ceil'; + delta = Math[fn](delta / lowestDelta); + deltaX = Math[fn](deltaX / lowestDeltaXY); + deltaY = Math[fn](deltaY / lowestDeltaXY); - // convert deltas to whole numbers - fn = delta > 0 ? 'floor' : 'ceil'; - delta = Math[fn](delta / lowestDelta); - deltaX = Math[fn](deltaX / lowestDeltaXY); - deltaY = Math[fn](deltaY / lowestDeltaXY); + return [delta, deltaX, deltaY]; + } + }; - return [delta, deltaX, deltaY]; + if (typeof window.define === 'function' && window.define.amd) { + // AMD + window.define('hamster', [], function(){ + return Hamster; + }); + } else if (typeof exports === 'object') { + // CommonJS + module.exports = Hamster; + } else { + // Browser global + window.Hamster = Hamster; } -}; - -if (typeof window.define === 'function' && window.define.amd) { - // AMD - window.define('hamster', [], function(){ - return Hamster; - }); -} else if (typeof exports === 'object') { - // CommonJS - module.exports = Hamster; -} else { - // Browser global - window.Hamster = Hamster; -} })(window, window.document); diff --git a/package.json b/package.json index becde98..3638084 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "hamsterjs", + "name": "@kensingtontech/hamsterjs", "version": "1.1.4", - "description": "A standalone javascript library for cross-browser mouse wheel support.", + "description": "A standalone javascript library for cross-browser mouse wheel support", "keywords": [ "hamster", "hamsterjs", @@ -25,5 +25,6 @@ "files": [ "hamster.js", "package.json" - ] + ], + "types": "./hamster.d.ts" } From 49d8b81b210009807ab04522c10e6fb8017d55c6 Mon Sep 17 00:00:00 2001 From: Tim Underhay <15734900+citizentim@users.noreply.github.com> Date: Thu, 10 May 2018 12:07:21 -0600 Subject: [PATCH 3/4] Bumped version to 1.1.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3638084..5956863 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kensingtontech/hamsterjs", - "version": "1.1.4", + "version": "1.1.5", "description": "A standalone javascript library for cross-browser mouse wheel support", "keywords": [ "hamster", From 34c94941c7021072c6b546dc6808fadcbef182d1 Mon Sep 17 00:00:00 2001 From: Tim Underhay <15734900+citizentim@users.noreply.github.com> Date: Thu, 10 May 2018 12:14:49 -0600 Subject: [PATCH 4/4] hamster.d.ts wasn't included in npm package --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5956863..e464b6a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kensingtontech/hamsterjs", - "version": "1.1.5", + "version": "1.1.6", "description": "A standalone javascript library for cross-browser mouse wheel support", "keywords": [ "hamster", @@ -24,7 +24,8 @@ }, "files": [ "hamster.js", - "package.json" + "package.json", + "hamster.d.ts" ], "types": "./hamster.d.ts" }