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.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 ac99dc8..c28f65a 100644 --- a/hamster.js +++ b/hamster.js @@ -1,327 +1,339 @@ /* - * Hamster.js v1.1.2 + * Hamster.js v1.1.4 * (c) 2013 Monospaced http://monospaced.com * License: MIT */ -(function(window, document){ -'use strict'; - -/** - * Hamster - * use this to create instances - * @returns {Hamster.Instance} - * @constructor + /* + MODIFIED FOR PASSIVE EVENT LISTENERS + BY KENSINGTON TECHNOLOGY ASSOCIATES */ -var Hamster = function(element) { - return new Hamster.Instance(element); -}; - -// 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){ - if (!Hamster.READY) { - // fix browser inconsistencies - Hamster.normalise.browser(); - - // Hamster is ready...! - Hamster.READY = true; - } - - this.element = element; - // store attached event handlers - this.handlers = []; - - // return instance - return this; -}; +(function(window, document){ +'use strict'; -/** - * 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; + } - // cross-browser addEventListener - hamster.element[Hamster.ADD_EVENT](Hamster.PREFIX + eventName, handler, useCapture || false); + Hamster.event.remove(this, Hamster.SUPPORT, handler, useCapture); - // 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; - } - }, - stopPropagation: function(){ - if (originalEvent.stopPropagation) { - originalEvent.stopPropagation(); - } else { - originalEvent.cancelBubble = 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; + } } - } - }; + }; - // 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 06ac503..e464b6a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "hamsterjs", - "version": "1.1.3", - "description": "A standalone javascript library for cross-browser mouse wheel support.", + "name": "@kensingtontech/hamsterjs", + "version": "1.1.6", + "description": "A standalone javascript library for cross-browser mouse wheel support", "keywords": [ "hamster", "hamsterjs", @@ -24,6 +24,8 @@ }, "files": [ "hamster.js", - "package.json" - ] + "package.json", + "hamster.d.ts" + ], + "types": "./hamster.d.ts" }