Using mouse wheel to scroll in Sencha Touch application on desktop - javascript

While looking for how to enable scrolling with the mouse wheel in Sencha Touch, I came across this answer. However, I am relatively new to Sencha Touch and the codebase I was given to maintain that uses it.
The answer says to put it in the initialization block of my application: as far as I can tell, that would be my app.js file that is generated by Sencha Cmd (which has a launch function). However, I'm lost after this. Would I add the first part of the above answer in the launch block? Outside of it? How would I make sure that it is automatically called on every page?
Edit: Here is my app.js file, in case it helps.
Ext.application({
name: 'App',
requires: [
'Ext.MessageBox',
'Ext.direct.*'
],
models:[
"..."
],
controllers: [
'...',
'...',
'...'
],
icon: {
'57': 'resources/icons/Icon.png',
'72': 'resources/icons/Icon~ipad.png',
'114': 'resources/icons/Icon#2x.png',
'144': 'resources/icons/Icon~ipad#2x.png'
},
isIconPrecomposed: true,
startupImage: {
'320x460': 'resources/startup/320x460.jpg',
'640x920': 'resources/startup/640x920.png',
'768x1004': 'resources/startup/768x1004.png',
'748x1024': 'resources/startup/748x1024.png',
'1536x2008': 'resources/startup/1536x2008.png',
'1496x2048': 'resources/startup/1496x2048.png'
},
profiles: ['Tablet', 'Phone'],
launch: function() {
...
}
....
});
Edit 2: I am using Sencha Touch 2.3.

The provided code in the other answer is pure Javascript and not ExtJs code, it runs in a global scope so you can add this above Ext.application (outside of ExtJs code, so make it your first bit of JS code that gets run). You could even wrap it inside an Ext.onReady call to make sure ExtJs is also fully loaded before you add it, if needed.
This should work, it might be worth looking over the Sencha forums or even on here for a more elegant and updated solution though.

The OP's answer above works, however it throws errors if trying to scroll over elements that do not have indexOf on their className (like SVG elements). Here is the updated code that first checks for the existence of indexOf.
I've also extended this method to support horizontal mouse scrolling if the browser supports wheelDeltaX and wheelDeltaY. Otherwise it defaults to using the more widely available wheelDelta and only scrolls in the Y direction.
Note that you can embed this code in a function and simply call it during the launch of your app. No need to put it at the top of the app.js file.
var mouseWheelHandler = function (e) {
var e = window.event || e,
el = e.target,
cmp,
offset,
scroller,
deltaY,
deltaX,
_results = [];
e.preventDefault(); // prevent scrolling when in iframe
while (el !== document.body) {
if (el && el.className && el.className.indexOf && el.className.indexOf('x-container') >= 0) {
cmp = Ext.getCmp(el.id);
if (cmp && typeof cmp.getScrollable == 'function' && cmp.getScrollable()) {
scroller = cmp.getScrollable().getScroller();
if (scroller) {
deltaY = e.detail ? e.detail * (-120) : e.hasOwnProperty('wheelDeltaY') ? e.wheelDeltaY : e.wheelDelta;
deltaX = e.detail ? e.detail * (-120) : e.hasOwnProperty('wheelDeltaX') ? e.wheelDeltaX : 0;
offset = {x: -deltaX * 0.5, y: -deltaY * 0.5};
scroller.fireEvent('scrollstart', scroller, scroller.position.x, scroller.position.y, e);
scroller.scrollBy(offset.x, offset.y);
scroller.snapToBoundary();
scroller.fireEvent('scrollend', scroller, scroller.position.x + offset.x, scroller.position.y - offset.y);
break;
}
}
}
_results.push(el = el.parentNode);
}
return _results;
};
if (document.addEventListener) {
// IE9, Chrome, Safari, Opera
document.addEventListener('mousewheel', mouseWheelHandler, false);
// Firefox
document.addEventListener('DOMMouseScroll', mouseWheelHandler, false);
}
else {
// IE 6/7/8
document.attachEvent('onmousewheel', mouseWheelHandler);
}
}

Thanks user991710 and Scriptable for your answer. In my case i added the entire code within the Ext.onReady event because it didn't work in the app.js.
Below is how i have incorporated the code in the Ext.onReady in the default.js
onReady: function() {
if (this.getAutoRender()) {
this.render();
}
if (Ext.browser.name == 'ChromeiOS') {
this.setHeight('-webkit-calc(100% - ' + ((window.outerHeight - window.innerHeight) / 2) + 'px)');
}
/* code ten behoeve van mousescroll in Chrome situatie */
var mouseWheelHandler = function (e) {
var e = window.event || e,
el = e.target,
cmp,
offset,
scroller,
delta,
_results = [];
e.preventDefault(); // prevent scrolling when in iframe
while (el !== document.body) {
if (el && el.className && el.className.indexOf('x-container') >= 0) {
cmp = Ext.getCmp(el.id);
if (cmp && typeof cmp.getScrollable == 'function' && cmp.getScrollable()) {
scroller = cmp.getScrollable().getScroller();
if (scroller) {
delta = e.detail ? e.detail * (-120) : e.wheelDelta;
offset = { x: 0, y: -delta * 0.5 };
scroller.fireEvent('scrollstart', scroller, scroller.position.x, scroller.position.y, e);
scroller.scrollBy(offset.x, offset.y);
scroller.snapToBoundary();
scroller.fireEvent('scrollend', scroller, scroller.position.x, scroller.position.y - offset.y);
break;
}
}
}
_results.push(el = el.parentNode);
}
return _results;
};
if (document.addEventListener) {
// IE9, Chrome, Safari, Opera
document.addEventListener('mousewheel', mouseWheelHandler, false);
// Firefox
document.addEventListener('DOMMouseScroll', mouseWheelHandler, false);
}
else {
// IE 6/7/8
document.attachEvent('onmousewheel', mouseWheelHandler);
}
/*einde code ten behoeve van muisscroll in Chrome modus */
},

Solution by OP.
In my app.js file (the one generated by Sencha Cmd), I added the following code at the very top of the file, before my Ext.application definition:
var mouseWheelHandler = function (e) {
var e = window.event || e,
el = e.target,
cmp,
offset,
scroller,
delta,
_results = [];
e.preventDefault(); // prevent scrolling when in iframe
while (el !== document.body) {
if (el && el.className && el.className.indexOf('x-container') >= 0) {
cmp = Ext.getCmp(el.id);
if (cmp && typeof cmp.getScrollable == 'function' && cmp.getScrollable()) {
scroller = cmp.getScrollable().getScroller();
if (scroller) {
delta = e.detail ? e.detail*(-120) : e.wheelDelta;
offset = { x:0, y: -delta*0.5 };
scroller.fireEvent('scrollstart', scroller, scroller.position.x, scroller.position.y, e);
scroller.scrollBy(offset.x, offset.y);
scroller.snapToBoundary();
scroller.fireEvent('scrollend', scroller, scroller.position.x, scroller.position.y-offset.y);
break;
}
}
}
_results.push(el = el.parentNode);
}
return _results;
};
if (document.addEventListener) {
// IE9, Chrome, Safari, Opera
document.addEventListener('mousewheel', mouseWheelHandler, false);
// Firefox
document.addEventListener('DOMMouseScroll', mouseWheelHandler, false);
}
Credit to the above code goes to user m.dostal on the Sencha Touch forums. If you happen across this solution, please upvote user Scriptable below as he helped me find the correct solution.

Related

Touch move fire only once when implementing "less" like scrolling on mobile

I'm trying to to implement touch scroll in less extension to jQuery Terminal. It work similar to less unix command.
I have this code:
self.touch_scroll(function(event) {
// how much difference changed since last touch move
var delta = event.current.clientY - event.previous.clientY;
var ret;
var interpreter = interpreters.top();
if (is_function(interpreter.touchscroll)) {
ret = interpreter.touchscroll(event, delta, self);
} else if (is_function(settings.touchscroll)) {
ret = settings.touchscroll(event, delta, self);
}
if (ret === true) {
return;
}
return false;
});
// make_callback_plugin is helper that use $.Callbacks and make sure that there is only
// one handler on the element
$.fn.touch_scroll = make_callback_plugin({
name: 'touch',
init: function(handler) {
var origin;
var previous;
$(this).on('touchstart.scroll', function(e) {
e = e.originalEvent;
if (e.touches.length === 1) {
previous = origin = e.touches[0];
}
}).on('touchmove.scroll', function(e) {
e = e.originalEvent;
console.log(!!origin + ' && ' + (e.touches.length) + ' === 1');
if (origin && e.touches.length === 1) {
var current = e.touches[0];
var ret = handler({
origin: origin,
previous: previous,
current: current
});
if (ret === false) {
// this don't change anything
e.preventDefault();
}
previous = current;
}
}).on('touchend.scroll', function() {
if (origin || previous) {
origin = previous = null;
}
});
},
destroy: function() {
$(this).off('touchstart.scroll touchmove.scroll touchend.scroll');
}
});
and inside less I have:
function scroll(delta, scroll_by) {
if (delta > 0) {
pos -= scroll_by;
if (pos < 0) {
pos = 0;
}
} else {
pos += scroll_by;
if (pos - 1 > lines.length - rows) {
pos = lines.length - rows + 1;
}
}
print();
return true;
}
term.push($.noop, {
onResize: refresh_view,
touchscroll: function(event, delta) {
console.log({delta});
var offset = Math.abs(delta);
// 14 is default height of single line in pixels
scroll(delta, Math.ceil(offset / 14));
return false;
},
mousewheel: function(event, delta) {
return scroll(delta, scroll_by);
},
I also have this css:
.terminal-less {
touch-action: none;
overscroll-behavior-y: contain;
}
on Mousewheel scrolling works good it scroll with the same amount of scroll_by which is by default 3 (seems about right). (pos is lines offset so if I use pos++ it move/scroll by one line, delta in touchscroll is positive or negative from about -20 to 20 pixels.
The problem I have and the question is, how can I make it scroll with the finger? it don't feel right. Also it scroll only once it don't move with the finger. touchmove fire only once, shoudn't it fire while I move the finger while touching the phone?
Anyone have experience with this type of touch scroll behavior?
I was searching for similar problem and didn't found solution. Do you know why touchmove could fire once? The only thing I can think of was textarea that is used as clipboard (on mobile it's also used to enable virtual keyboard), but I've set background to red and it don't move on Android. I was testing other code from this drawing demo:
https://zipso.net/a-simple-touchscreen-sketchpad-using-javascript-and-html5/
and it works fine, touch move keeps firing while you move the finger.
Any ideas? It will be hard to replicate but if somone is interested in investigation I can put all my code on github in jQuery Terminal repo (in some branch).
What's weird is that touchend don't fire after touchmove, it fire once only when I click on the terminal to enable keyboard.
I've tried to monkey patch jQuery on and log each time it fire but I didn't have any other event (that may prevent default behavior) also according to docs mouse events fire after touchend and those don't fire.
What you need is simple .terminal-wrapper { pointer-events: none; } (based on the devel branch). But with this rule you can't select the text, that's why you need to use it only for mobile devices.
I'm not sure if this will block the selection of text on mobile, but if so, you can try to add this on touchstart (or even on touchmove as the first instruction) and remove it on touchend.
Also, I had to add some JS code, because without it interpreter.touchScroll is undefined. But this is not the main cause of the problem.
interpreters = new Stack($.extend({}, settings.extra, {
name: settings.name,
prompt: prompt,
keypress: settings.keypress,
keydown: settings.keydown,
resize: settings.onResize,
greetings: settings.greetings,
mousewheel: settings.mousewheel,
touchScroll: settings.touchScroll, // NEW LINE
history: settings.history,
keymap: new_keymap
}, interpreter));
self.touch_scroll(function(event) {
var delta = event.current.clientY - event.previous.clientY;
var ret;
var interpreter = interpreters.top(); // NEW LINE
if (is_function(interpreter.touchScroll)) {
ret = interpreter.touchScroll(event, delta, self);
} else if (is_function(settings.touchScroll)) {
ret = settings.touchScroll(event, delta, self);
}
if (ret === true) {
return;
}
});
Without pointer-events: none;
With pointer-events: none;

Script not running in Firefox, but ran in Chrome

I made a script to register the thumb scroll wheel event (MX Master 2S if you're wondering). However, this script ran perfectly fine in Chrome, but not in Firefox (Quantum). Why is that so?
var expression = /[-a-zA-Z0-9#:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9#:%_\+.~#?&//=]*)?/gi;
var regex = new RegExp(expression);
var elements = document.getElementsByClassName('pagination'); // get the elements
var search = (elements[0].innerHTML.match(regex));
//alert(search);
if(document.addEventListener){
document.addEventListener("mousewheel", MouseWheelHandler, false);
document.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
} else {
document.attachEvent("onmousewheel", MouseWheelHandler);
}
function MouseWheelHandler(e) {
var e = window.event || e;
var ret = true;
if (e.wheelDelta) {
// Tilt to the left
if (e.wheelDeltaX < 0) {
str = window.location.toString();
strsplit = str.split('/');
preloc=Number(strsplit[4])+1;
if (preloc > 0) {
window.location.replace("https://somepage.com/page/"+preloc);}
prelocstr=preloc.toString();
if (prelocstr == "NaN") {
window.location.replace(search[0]); }
ret = false;
}
// Tilt to the right
if (e.wheelDeltaX > 0) {
str = window.location.toString();
strsplit = str.split('/');
preloc=Number(strsplit[4])-1;
if (preloc > 0) {
window.location.replace("https://somepage.com/page/"+preloc);}
ret = false;
}
}
event.returnValue = ret;
}
This script is made within Tampermonkey. Could anyone point me the mistake? Thanks in advance!
There's a newer standard for handling mouse wheel event that's standard across browsers:
https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
https://developer.mozilla.org/en-US/docs/Web/Events/wheel
To use this event, do:
document.addEventListener("wheel", MouseWheelHandler);
And there's no need for:
e = window.event || e
The event will be there.
Only DOMMouseScroll works with Firefox, but it uses a different API. So, you have to write a separate handler for Firefox, instead of using the MouseWheelHandler, or adjust the MouseWheelHandler to support both.
As kshetline pointed out, there is now a new standard, which works with all modern browsers: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent.
The other two options don't work in Firefox, as stated here:
This feature is non-standard and is not on a standards track. Do not
use it on production sites facing the Web: it will not work for every
user. There may also be large incompatibilities between
implementations and the behavior may change in the future.
Source: https://developer.mozilla.org/en-US/docs/Web/Events/mousewheel

Detect mouse on touch screen device

I use the following code to detect whether the device is a touch device or not:
var isTouchDevice = 'ontouchstart' in window || navigator.msMaxTouchPoints;
if(isTouchDevice)
{
$('body').addClass('yes-touch');
}
else
{
$('body').addClass('no-touch');
}
I use this to only show :hover states when it is NOT a touch device (as most touch devices interpret a tap as a hover).
.no-touch .element:hover {
color: red;
}
The problem is, one of our PCs in the office is an all-on-one touch screen PC, which means that when using a mouse the hover states don't occur.
Is there a way to work out whether a mouse is being used on a touch screen device? In other words, it should have the no-touch class applied when the mouse is being used and the yes-touch class applied when the touch screen is being used.
As of today, there is no foolproof ironclad way of doing it. The modernizr folks, pretty much the experts in feature detection, recently had this to say about it:
https://github.com/Modernizr/Modernizr/issues/869#issuecomment-57891034
The end result of all of this is that you cannot detect a mouse use in
a way that would conform to the level of reliability that Modernizr is
credited with. For our intents and purposes, it is a undetectable.
If you, future traveler, wish to attempt to detect a mouse user, then
the following is the best guide I can offer.
Don't. Seriously. Just because a user has a "mouse" doesn't mean that
they don't have multiple other forms of input. You should try really
hard to avoid making any kind of UI/UX decision that changes based
upon the idea of a mouse user being diametrically opposed to a
touchscreen user (or any other kind, for that matter). Make things
universal.
If you have to, and only care about IE 10 and 11, then IE's
PointerEvent would be worth checking out. Support is abysmal, outside
of those two (and presumably future IE versions).
You can attach a
listener for a 'hover' event on the body, and if it is true, then the
user probably has a mouse. The drawback with this approach include
touch events briefly firing hover events on tap/touch, so you could
get false positives.
sniff for mobile user agents. This is a bad idea,
and goes against the very core of Modernizr. Please don't do it.
So to me #1 pretty much sums it up. However, that answers your question but doesn't give you a solution. You mention "one of our PC's in the office..." Is this by chance an internal only application? I've occasionally run across situations where internal special use or one off pages may require some individual treatment for whatever reason (like one of our employees having a touch based AIO with a mouse attached). What I'll do then is append a ?hasmouse onto the end of the url and give the user that link to bookmark. Then inside javascript after your var isTouchDevice but before your if, insert this code to undo it:
if (location.search == '?hasmouse') {
isTouchDevice = false;
}
Again, thats sort of a no frills way for just internal use.
I have been using this for a while and it seems to work reliably. I wounder if it's worth it sometimes, but it does work.
The idea here is to capture actual touchdown events to trigger touch mode and use mousemove to trigger mouse mode. The problem is IE does not trigger touch events, but pointer events. The great thing about pointer events is you can check if it's mouse or touch!
The problem is all other browsers fire a fake mousemove just after a touch event. It's truly maddening!
You can see it work on this codepen
//First check if this is a touch device:
this.isTouch = 'ontouchstart' in window || (navigator.msMaxTouchPoints > 0);
// Some vars we'll need later
var lastTouch = 0
var lastCheck = 0
//Then set up our event listeners:
function initEvents() {
//handle touch/mouse devices detect mouse so that touch is toggled off
if (this.isTouch) {
$(document).on(" touchstart mousemove " + msPointerEvent('move'), function(e) {
e = e.originalEvent
//browser has pointer events
var pe = window.PointerEvent || window.MSPointerEvent
// handle ie pointer events (polyfill functions are at bottom of answer)
if (e.type == msPointerEvent('move')) {
var touchEvent = msPointerType(e) == 'touch'
if (touchEvent)
lastTouch = e.timeStamp;
if (!this.isTouch && touchEvent)
return setupTouch.call(this, true)
else if (this.isTouch && !touchEvent)
return setupTouch.call(this, false)
}
// Handle all other browser touch events
if (e.type == "touchstart") {
console.log('touchstart fired')
lastTouch = e.timeStamp;
if (!this.isTouch)
setupTouch.call(this, true);
}
// test mouse move and set up mouse mode if real
else if (!pe && e.type == "mousemove" && this.isTouch) {
if (realMouseDown.call(this, e)) {
setupTouch.call(this, false)
}
}
}.bind(this));
}
}
initEvents()
// Here is where we get clever. It turns out that the fake mousemove will fire in less than 500ms of the touch so we use that to detect fakes. Then of course do something special for IE:
function realMouseDown(e) {
var touchDif = e.timeStamp - lastTouch
var mouseDif = e.timeStamp - lastCheck
// false mouse event will get fired within 500ms of a touch (touchDif > 500)
// (required for all browsers false mouse after touch event)
var real = touchDif > 500
lastCheck = e.timeStamp;
console.log('real=', real, ' mDif ='+mouseDif, ' tDif ='+touchDif)
return real
}
// Now for some IE polyfill because they cant seem to make up their mind what to do.
// IE pointer event polyfill
function msPointerEvent(type) {
var n = ""
if (window.PointerEvent) // IE 11
n = 'pointer' + type
else if (window.MSPointerEvent) // IE 10
n = 'MSPointer' + type[0].toUpperCase() + type.substr(1);
return n
}
// IE pointer type polyfill
function msPointerType(e) {
var pt = ['zero', 'one', 'touch', 'pen', 'mouse']
return typeof e.pointerType == 'string' ? e.pointerType : pt[e.pointerType]
}
// And finally do what you need...
// make required changes for touch / mouse
var $output = $('#output')
function setupTouch(state) {
console.log('TouchMode=', state)
if (state)
this.isTouch = true
else
this.isTouch = false
$output.html('Touch mode changed to = '+state)
}
//First check if this is a touch device:
this.isTouch = 'ontouchstart' in window || (navigator.msMaxTouchPoints > 0);
// Some vars we'll need later
var lastTouch = 0
var lastCheck = 0
//Then set up our event listeners:
function initEvents() {
//handle touch/mouse devices detect mouse so that touch is toggled off
if (this.isTouch) {
$(document).on(" touchstart mousemove " + msPointerEvent('move'), function(e) {
e = e.originalEvent
//browser has pointer events
var pe = window.PointerEvent || window.MSPointerEvent
// handle ie pointer events (polyfill functions are at bottom of answer)
if (e.type == msPointerEvent('move')) {
var touchEvent = msPointerType(e) == 'touch'
if (touchEvent)
lastTouch = e.timeStamp;
if (!this.isTouch && touchEvent)
return setupTouch.call(this, true)
else if (this.isTouch && !touchEvent)
return setupTouch.call(this, false)
}
// Handle all other browser touch events
else if (e.type == "touchstart") {
console.log('touchstart fired')
lastTouch = e.timeStamp;
if (!this.isTouch)
setupTouch.call(this, true);
}
// test mouse move and set up mouse mode if real
else if (!pe && e.type == "mousemove" && this.isTouch) {
if (realMouseDown.call(this, e)) {
setupTouch.call(this, false)
}
}
}.bind(this));
}
}
initEvents()
// Here is where we get clever. It turns out that the fake mousemove will fire in less than 500ms of the touch so we use that to detect fakes:
function realMouseDown(e) {
var touchDif = e.timeStamp - lastTouch
var mouseDif = e.timeStamp - lastCheck
// false mouse event will get fired within 500ms of a touch (touchDif > 500)
// (required for all browsers false mouse after touch event)
var real = touchDif > 500
lastCheck = e.timeStamp;
console.log('real=', real, ' mDif =' + mouseDif, ' tDif =' + touchDif)
return real
}
// IE pointer event polyfill
function msPointerEvent(type) {
var n = ""
if (window.PointerEvent) // IE 11
n = 'pointer' + type
else if (window.MSPointerEvent) // IE 10
n = 'MSPointer' + type[0].toUpperCase() + type.substr(1);
return n
}
// IE pointer type polyfill
function msPointerType(e) {
var pt = ['zero', 'one', 'touch', 'pen', 'mouse']
return typeof e.pointerType == 'string' ? e.pointerType : pt[e.pointerType]
}
// make required changes for touch / mouse
var $output = $('#output')
function setupTouch(state) {
console.log('TouchMode=', state)
if (state) {
this.isTouch = true
$output.addClass('is-touch')
} else {
this.isTouch = false
$output.removeClass('is-touch')
}
$output.html('Touch mode changed to = ' + state)
}
body {
pointer-evetns: none;
}
#output.is-touch {
background-color: blue;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output">
Touch or movethe mose on the result window to change the TouchMode state.
</div>
You can check for type of Pointer Event, that attached to Your object.
Please see example for hover below:
$('.element').on('pointerenter', function (e) {
if (e.pointerType == 'mouse') {
$(this).addClass('hover');
}
}).on('pointerleave', function (e) {
if (e.pointerType == 'mouse') {
$(this).removeClass('hover');
}
});
And use your css:
.element.hover {
color: red;
}

event.wheelDelta returns undefined

So I'm trying to disable scrolling on my page when my lightbox opens, and I found this really usefull script that does exactly that. (http://jsfiddle.net/mrtsherman/eXQf3/3/), unfortunately, when I use it on my own page, it disabled scrolling in my lightbox as well. I started to debug the code with alerts only to find out that event.wheelDelta returns "undefined" on my page, while in the JSFiddle, it returns -120.
The event object in a jQuery event handler does not reflect the real event. wheelDelta is a non-standard event propertyIE and Opera, available through the originalEvent property of the jQuery event.
In jQuery 1.7+, the detail property is not available at the jQuery Event object. So, you should also use event.originalEvent.detail to for this property at the DOMMouseScroll event. This method is backwards-compatible with older jQuery versions.
event.originalEvent.wheelDelta
Demo: http://jsfiddle.net/eXQf3/22/
See also: http://api.jquery.com/category/events/event-object/
$.fn.wheel = function (callback) {
return this.each(function () {
$(this).on('mousewheel DOMMouseScroll', function (e) {
e.delta = null;
if (e.originalEvent) {
if (e.originalEvent.wheelDelta) e.delta = e.originalEvent.wheelDelta / -40;
if (e.originalEvent.deltaY) e.delta = e.originalEvent.deltaY;
if (e.originalEvent.detail) e.delta = e.originalEvent.detail;
}
if (typeof callback == 'function') {
callback.call(this, e);
}
});
});
};
and use like this:
$('body').wheel(function (e) {
e.preventDefault();
$('#myDiv').append($('<div>').text(e.delta));
});
big thx to #Mark for jquery-fying the function (:
$(this).on('mousewheel DOMMouseScroll', function(e){
var dat = $(e.delegateTarget).data(); //in case you have set some, read it here.
var datx = dat.x || 0; // just to show how to get specific out of jq-data object
var eo = e.originalEvent;
var xy = eo.wheelDelta || -eo.detail; //shortest possible code
var x = eo.wheelDeltaX || (eo.axis==1?xy:0);
var y = eo.wheelDeltaY || (eo.axis==2?xy:0); // () necessary!
console.log(x,y);
});
works in Webkit and FF, can not proof IE here :(
When I need WheelDelta, I pass the event along to this little gem...
function getWheelDelta(event) {
return event.wheelDelta || -event.detail || event.originalEvent.wheelDelta || -event.originalEvent.detail || -(event.originalEvent.deltaY * 25) || null;
}
Example jQuery where I used it to obtain the delta for scrolling elements with the overflow:hidden CSS rule...
$('#parent-id').on('wheel mousewheel DOMMouseScroll', function(event) {
var node = $('#child-id');
node.prop('scrollTop', parseFloat(node.prop('scrollTop')) - (getWheelDelta(event) / 2));
return false;
});
Note: Reverse scroll direction by adding delta instead of subtracting like example above.
I have tried your solution, psycho brm, and I have changed the function extractDelta(e) to make it works in Firefox. Instead of e.detail I have used e.originalEvent.detail because Firefox returned an undefined delta. I have uploaded the solution and my test code to this post. I hope it helps.
function extractDelta(e) {
if (e.wheelDelta) {
return e.wheelDelta;
}
if (e.originalEvent.detail) {
return e.originalEvent.detail * -40;
}
if (e.originalEvent && e.originalEvent.wheelDelta) {
return e.originalEvent.wheelDelta;
}
}

Get mouse wheel events in jQuery?

Is there a way to get the mouse wheel events (not talking about scroll events) in jQuery?
​$(document).ready(function(){
$('#foo').bind('mousewheel', function(e){
if(e.originalEvent.wheelDelta /120 > 0) {
console.log('scrolling up !');
}
else{
console.log('scrolling down !');
}
});
});
Binding to both mousewheel and DOMMouseScroll ended up working really well for me:
$(window).bind('mousewheel DOMMouseScroll', function(event){
if (event.originalEvent.wheelDelta > 0 || event.originalEvent.detail < 0) {
// scroll up
}
else {
// scroll down
}
});
This method is working in IE9+, Chrome 33, and Firefox 27.
Edit - Mar 2016
I decided to revisit this issue since it's been a while. The MDN page for the scroll event has a great way of retrieving the scroll position that makes use of requestAnimationFrame, which is highly preferable to my previous detection method. I modified their code to provide better compatibility in addition to scroll direction and position:
(function() {
var supportOffset = window.pageYOffset !== undefined,
lastKnownPos = 0,
ticking = false,
scrollDir,
currYPos;
function doSomething(scrollPos, scrollDir) {
// Your code goes here...
console.log('scroll pos: ' + scrollPos + ' | scroll dir: ' + scrollDir);
}
window.addEventListener('wheel', function(e) {
currYPos = supportOffset ? window.pageYOffset : document.body.scrollTop;
scrollDir = lastKnownPos > currYPos ? 'up' : 'down';
lastKnownPos = currYPos;
if (!ticking) {
window.requestAnimationFrame(function() {
doSomething(lastKnownPos, scrollDir);
ticking = false;
});
}
ticking = true;
});
})();
See the Pen Vanilla JS Scroll Tracking by Jesse Dupuy (#blindside85) on CodePen.
This code is currently working in Chrome v50, Firefox v44, Safari v9, and IE9+
References:
https://developer.mozilla.org/en-US/docs/Web/Events/scroll
https://developer.mozilla.org/en-US/docs/Web/Events/wheel
As of now in 2017, you can just write
$(window).on('wheel', function(event){
// deltaY obviously records vertical scroll, deltaX and deltaZ exist too.
// this condition makes sure it's vertical scrolling that happened
if(event.originalEvent.deltaY !== 0){
if(event.originalEvent.deltaY < 0){
// wheeled up
}
else {
// wheeled down
}
}
});
Works with current Firefox 51, Chrome 56, IE9+
There's a plugin that detects up/down mouse wheel and velocity over a region.
Answers talking about "mousewheel" event are refering to a deprecated event. The standard event is simply "wheel". See https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel
This worked for me:)
//Firefox
$('#elem').bind('DOMMouseScroll', function(e){
if(e.originalEvent.detail > 0) {
//scroll down
console.log('Down');
}else {
//scroll up
console.log('Up');
}
//prevent page fom scrolling
return false;
});
//IE, Opera, Safari
$('#elem').bind('mousewheel', function(e){
if(e.originalEvent.wheelDelta < 0) {
//scroll down
console.log('Down');
}else {
//scroll up
console.log('Up');
}
//prevent page fom scrolling
return false;
});
from stackoverflow
Here is a vanilla solution. Can be used in jQuery if the event passed to the function is event.originalEvent which jQuery makes available as property of the jQuery event. Or if inside the callback function under we add before first line: event = event.originalEvent;.
This code normalizes the wheel speed/amount and is positive for what would be a forward scroll in a typical mouse, and negative in a backward mouse wheel movement.
Demo: http://jsfiddle.net/BXhzD/
var wheel = document.getElementById('wheel');
function report(ammout) {
wheel.innerHTML = 'wheel ammout: ' + ammout;
}
function callback(event) {
var normalized;
if (event.wheelDelta) {
normalized = (event.wheelDelta % 120 - 0) == -0 ? event.wheelDelta / 120 : event.wheelDelta / 12;
} else {
var rawAmmount = event.deltaY ? event.deltaY : event.detail;
normalized = -(rawAmmount % 3 ? rawAmmount * 10 : rawAmmount / 3);
}
report(normalized);
}
var event = 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll';
window.addEventListener(event, callback);
There is also a plugin for jQuery, which is more verbose in the code and some extra sugar: https://github.com/brandonaaron/jquery-mousewheel
This is working in each IE, Firefox and Chrome's latest versions.
$(document).ready(function(){
$('#whole').bind('DOMMouseScroll mousewheel', function(e){
if(e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0) {
alert("up");
}
else{
alert("down");
}
});
});
I was stuck in this issue today and found this code is working fine for me
$('#content').on('mousewheel', function(event) {
//console.log(event.deltaX, event.deltaY, event.deltaFactor);
if(event.deltaY > 0) {
console.log('scroll up');
} else {
console.log('scroll down');
}
});
use this code
knob.bind('mousewheel', function(e){
if(e.originalEvent.wheelDelta < 0) {
moveKnob('down');
} else {
moveKnob('up');
}
return false;
});
The plugin that #DarinDimitrov posted, jquery-mousewheel, is broken with jQuery 3+. It would be more advisable to use jquery-wheel which works with jQuery 3+.
If you don't want to go the jQuery route, MDN highly cautions using the mousewheel event as it's nonstandard and unsupported in many places. It instead says that you should use the wheel event as you get much more specificity over exactly what the values you're getting mean. It's supported by most major browsers.
my combination looks like this. it fades out and fades in on each scroll down/up. otherwise you have to scroll up to the header, for fading the header in.
var header = $("#header");
$('#content-container').bind('mousewheel', function(e){
if(e.originalEvent.wheelDelta > 0) {
if (header.data('faded')) {
header.data('faded', 0).stop(true).fadeTo(800, 1);
}
}
else{
if (!header.data('faded')) header.data('faded', 1).stop(true).fadeTo(800, 0);
}
});
the above one is not optimized for touch/mobile, I think this one does it better for all mobile:
var iScrollPos = 0;
var header = $("#header");
$('#content-container').scroll(function () {
var iCurScrollPos = $(this).scrollTop();
if (iCurScrollPos > iScrollPos) {
if (!header.data('faded')) header.data('faded', 1).stop(true).fadeTo(800, 0);
} else {
//Scrolling Up
if (header.data('faded')) {
header.data('faded', 0).stop(true).fadeTo(800, 1);
}
}
iScrollPos = iCurScrollPos;
});
If using mentioned jquery mousewheel plugin, then what about to use the 2nd argument of event handler function - delta:
$('#my-element').on('mousewheel', function(event, delta) {
if(delta > 0) {
console.log('scroll up');
}
else {
console.log('scroll down');
}
});
I think many key things are a bit all over the place and I needed to read all the answers to make my code work as I wanted, so I will post my findings in just one place:
You should use "wheel" event over the other deprecated or browser specific events.
Many people here is getting something wrong: the opposite of x>0 is x<=0 and the opposite of x<0 is x>=0, many of the answers in here will trigger scrolling down or up incorrectly when x=0 (horizontal scrolling).
Someone was asking how to put sensitivity on it, for this you can use setTimeout() with like 50 ms of delay that changes some helper flag isWaiting=false and you protect yourself with if(isWaiting) then don't do anything. When it fires you manually change isWaiting=true and just below this line you start the setTimeout again who will later change isWaiting=false after 50 ms.
I got same problem recently where
$(window).mousewheel was returning undefined
What I did was $(window).on('mousewheel', function() {});
Further to process it I am using:
function (event) {
var direction = null,
key;
if (event.type === 'mousewheel') {
if (yourFunctionForGetMouseWheelDirection(event) > 0) {
direction = 'up';
} else {
direction = 'down';
}
}
}

Categories

Resources