I'm making a single-page website that reacts to mouse wheel events.
var supportsWheel = false;
function DoSomething (e) {
if (e.type == "wheel") supportsWheel = true;
else if (supportsWheel) return;
var delta = ((e.deltaY || -e.wheelDelta || e.detail) >> 10) || 1;
//if mousewheel is going up
if (delta > 0) {
slideDown();
}
//if mousewheel is going down
else if (delta < 0) {
slideUp();
}
}
document.addEventListener('wheel', DoSomething);
document.addEventListener('mousewheel', DoSomething);
document.addEventListener('DOMMouseScroll', DoSomething);
So this works perfectly in chrome and safari but in firefox, both directions of the mouse wheel activate slideDown() function. As opposed to declaring slideUp when the mouse wheel goes up and slideDown when it's going down. Any ideas how to fix it?
This is because you normalize deltaY = 0 as down, and that you probably have some smooth-scrolling set-up in Firefox, or in your OS:
This smooth scrolling feature will make your delta get linearly smaller, until they reach 0. So, when you initiate a Wheel event from your mouse/scrollpad, it will often end with an event which deltaY will be 0.
You can check this theory with this modified code, which will output the cases where the deltaY is 0 separately.
var supportsWheel = false;
function DoSomething(e) {
if (e.type == "wheel") supportsWheel = true;
else if (supportsWheel) return;
var e_delta = (e.deltaY || -e.wheelDelta || e.detail);
var delta = e_delta && ((e_delta >> 10) || 1) || 0;
if (delta > 0) {
slideDown();
} else if (delta < 0) {
slideUp();
} else if (!delta) {
donnotSlide();
}
}
document.addEventListener('wheel', DoSomething);
function slideDown() {
console.log('down');
}
function slideUp() {
console.log('up');
}
function donnotSlide() {
console.log('was zero');
}
Use your mouse wheel
But to say the truth, I don't really see why you do normalize this delta info... Usually you want to react to its values.
Related
I have a custom script which advances a small icon upon wheel scroll. It works well but it's not advancing the element as quickly as I would like. I'd like to increase the distance that the element (pill) moves per wheel scroll. How can I alter the code to facilitate this? Thanks for any insight. Code:
function wheel(e) {
var modelContentWrapper = $('.model-content-wrapper');
var howModelWorks_steps = $('#howModelWorks_steps');
var currentIndex = $('.model-content.active', modelContentWrapper).index();
var $pill = $('.step_' + (currentIndex + 1) + ' > a.clickable-icon');
var $li = $('ul.steps li');
var $pillStep = ($li.width()) / wheelSpeed;
direction = 'right';
if ((e.wheelDelta && e.wheelDelta >= 0) || (e.detail && e.detail < 0)) {
wheelValue++;
if ((firstElement && parseInt($pill.css('margin-left')) > initialIconLeft) || (!firstElement)) {
$pill.css('margin-left', (parseInt($pill.css('margin-left')) - $pillStep) + 'rem');
}
if (wheelValue >= wheelSpeed) {
wheelValue = wheelValue - wheelSpeed;
forceModelBackward();
}
//direction = 'left';
}
else {
wheelValue--;
//direction = 'right';
if (!lastElement) {
$pill.css('margin-left', (parseInt($pill.css('margin-left')) + $pillStep) + 'rem');
}
if (Math.abs(wheelValue) == wheelSpeed) {
wheelValue = wheelValue + wheelSpeed;
forceModelForward();
}
}
//if (wheelValue > (wheelSpeed * 5) || wheelValue < (wheelSpeed * -5)) {
if (stepsCounter == 1 || stepsCounter == 4) {
enableScroll();
}
preventDefault(e);
}
Try adding the follow to your event listener..
capture: true,
passive: true
Passive Event Listeners allow you to attach un-cancelable handlers to events, letting browsers optimize around your event listeners. The browser can then, for example, keep scrolling at native speed without waiting for your event handlers to finish executing.
Usage
Probably what is used mostly:
// Really, if you're using wheel, you should instead be using the 'scroll' event,
// as it's passive by default.
document.addEventListener('wheel', (evt) => {
// ... do stuff with evt
}, true)
You’ll need to replace it with this:
document.addEventListener('wheel', (evt) => {
// ... do stuff with evt
}, {
capture: true,
passive: true
})
Copied information from alligator dot io
I'm checking the scroll direction with wheelDelta but it returns lots of scroll properties at once. I only need to know if it's up or down and it shouldn't return a value under 500ms after first trigger. I tried to play with setTimeout but couldn't solve it nicely. Any ideas?
var distance = $('.section-two').offset().top,
$window = $(window);
$window.scroll(function() {
if ( $window.scrollTop() >= distance ) {
$('body').css('overflow', 'hidden');
setTimeout(function(){
$(document).bind('mousewheel', function(evt) {
var delta = evt.originalEvent.wheelDelta;
if(delta > 0){
console.log('scrolled up')
} else {
console.log('scrolled down');
}
})
}, 500);
}
});
Here's the codepen example
Can you try unbinding the 'mousewheel' event before you bind it. It seems like, it binds it over and over again. Add $(document).unbind('mousewheel'); before the $(document).bind('mousewheel', function(evt) { line.
Try using Lodash to throttle the event. You should be able to store the previous offset and figure out the scroll direction from that:
var lastScrollTop = 0;
function scrollHandler() {
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
if (scrollTop > lastScrollTop){
console.info('scrolled down');
} else {
console.info('scrolled up');
}
lastScrollTop = scrollTop;
}
window.addEventListener('scroll', _.throttle(scrollHandler, 500))
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
solved with a more proper setTimeout: codepen example
var doMouseWheel = true;
$(document).bind("mousewheel", function(event, delta)
{
// it'll just not do anything while it's false
if (!doMouseWheel)
return;
// here's where you were unbinding, now just turning false
doMouseWheel = false;
// do whatever else you wanted
var delta = event.originalEvent.wheelDelta;
if(delta > 0){
console.log('scrolled up')
} else {
console.log('scrolled down');
}
// here's where you were rebinding, now wait 200ms and turn on
setTimeout(turnWheelBackOn, 800);
});
function turnWheelBackOn() { doMouseWheel = true; }
So my problem is this,
I was wondering if there was a way to detect if a user right clicks and left clicks simultaneously and commit an action if doing so in jQuery. It seems like the code can only detect one right click or one left click at a time in mouse events.
UPDATE
Thanks for the answers, I decided ultimately that feature would be too funky to have for users when I was trying to implement it. Hopefully these answers will be able to help other people too.
You can create variables to hold the state of each mouse button individually and use that to determine whether both buttons are down:
window.isLeftMouseDown = false;
window.isRightMouseDown = false;
$(document).on('mousedown', function(e) {
if (e.which == 1)
window.isLeftMouseDown = true;
else if (e.which == 3)
window.isRightMouseDown = true;
if (window.isLeftMouseDown && window.isRightMouseDown) {
console.log('both down');
}
});
$(document).on('mouseup', function(e) {
if (e.which == 1)
window.isLeftMouseDown = false;
else if (e.which == 3)
window.isRightMouseDown = false;
});
https://jsfiddle.net/2j151tpt/
Something like this. It uses ES2015.
let left = false;
document.addEventListener('mousedown', e => {
if (e.button === 0) {
left = true;
} else if (e.button === 2 && left) {
fireYourFunctionForTwoButtons();
}
})
document.addEventListener('mouseup', e => {
if (e.button === 0) {
left = false;
}
});
My proposal is based on the elapsed time in milliseconds between the two consecutive clicks:
$(function () {
$('#btn').data('last-left-click', 0).on('mousedown contextmenu', function (e) {
var nowTime = Date.now();
if (e.which == 1) {
$(this).data('last-left-click', nowTime);
}
if (e.which == 3) {
// if less then 300 milliseconds....
if ((nowTime - $(this).data('last-left-click')) < 300) {
console.log('Both left and right clicked in sequence in less then 300 milliseconds!');
}
$(this).data('last-left-click', 0);
}
});
});
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<button id="btn">Click Me</button>
i have a function which works for mouse wheel and i want to use the same function for swipe.
// Mouse Wheel support
this.MouseWheel =
{
init: function()
{
// Init mouse wheel listener
if(window.addEventListener)
{
my.ImageFlowDiv.addEventListener('DOMMouseScroll', my.MouseWheel.get, false);
}
my.Helper.addEvent(my.ImageFlowDiv,'mousewheel',my.MouseWheel.get);
},
get: function(event)
{
var delta = 0;
if (!event)
{
event = window.event;
}
if (event.wheelDelta)
{
delta = event.wheelDelta / 120;
}
else if (event.detail)
{
delta = -event.detail / 3;
}
if (delta)
{
my.MouseWheel.handle(delta);
}
my.Helper.suppressBrowserDefault(event);
},
handle: function(delta)
{
alert('handle called');
var change = false;
var newImageID = 0;
if(delta > 0)
{
if(my.imageID >= 1)
{
newImageID = my.imageID -3;
change = true;
}
}
else
{
if(my.imageID < (my.max-1))
{
newImageID = my.imageID +4;
change = true;
}
}
/* Glide to next (mouse wheel down) / previous (mouse wheel up) image */
if(change)
{
//alert('new image id='+newImageID);
my.glideOnEvent(newImageID);
}
}
};
Earlier i used the touchswipe plugin and it was working fine but i want to use another plugin as touchswipe is disturbing touch support.
$("#imageFlow").swipe({
swipe:function(event, direction, distance, duration, fingerCount) {
alert('swipe called');
if(direction == 'left') {
my.MouseWheel.handle(-1);
} else if (direction == 'right') {
my.MouseWheel.handle(1);
}
}
});
Have you taken a look at Hammer JS? I find their syntax is fairly simple to use and does not interfere with my other touch events.
Your code could then look similar to this:
Hammer($('#imageFlow')).on("swipe", function(event) {
console.log('swipe called'); //(instead of alert)
if (event.gesture.direction === 'left'){
my.MouseWheel.handle(-1);
} else if (event.gesture.direction === 'right'){
my.MouseWheel.handle(1);
};
});
In the docs
Also bear in mind to use 3 equals signs in JavaScript. so:
if (myName === 'Jean-Marie'){//dostuff}
instead of
if (myName == 'Jean-Marie'){//dostuff}
I'm trying to set up swipe events for a site I'm working on. Basically, when someone swipes the content container, child div elements of that will change content depending on the page one is on. The actual div "contentwrapper" that I am attaching the swipe listeners to does not change.
To do some animations, I have a previous and next page container that stores content. I removed the ajax function where I populate these for simplicity.
This works when going forwards but when going backwards, I seem to lose the preventDefault behavior and the whole page moves with my finger swipe. This problem only happens intermittently and aways when going backwards.
// Slightly modified code by Dave Dunkin
// http://rabblerule.blogspot.com/2009/08/detecting-swipe-in-webkit.html
function addSwipeListener(el, listener)
{
var startX;
var dx;
var direction;
function cancelTouch()
{
el.removeEventListener('touchmove', onTouchMove);
el.removeEventListener('touchend', onTouchEnd);
startX = null;
startY = null;
direction = null;
}
function onTouchMove(e)
{
if (e.touches.length > 1)
{
cancelTouch();
}
else
{
dx = e.touches[0].pageX - startX;
var dy = e.touches[0].pageY - startY;
if (direction == null)
{
direction = dx;
}
else if ((direction < 0 && dx > 0) || (direction > 0 && dx < 0) || Math.abs(dy) > 400)
{
cancelTouch();
}
}
}
function onTouchEnd(e)
{
cancelTouch();
if (Math.abs(dx) > 30)
{
listener({ target: el, direction: dx > 0 ? 'right' : 'left' });
dx = 0;
}
}
function onTouchStart(e)
{
e.preventDefault();
e.stopPropagation();
if (e.touches.length == 1)
{
startX = e.touches[0].pageX;
startY = e.touches[0].pageY;
el.addEventListener('touchmove', onTouchMove, false);
el.addEventListener('touchend', onTouchEnd, false);
}
}
el.addEventListener('touchstart', onTouchStart, false);
}
Add Swipe Listener
addSwipeListener(document.getElementById("contentwrapper"), function(e) {
swipePageChange(e);
});
function swipePageChange(e) {
if(e.direction == "left") {
moveforward();
}
if(e.direction == "right") {
movebackward();
}
}
Page Movement Events
function moveforward() {
$("#previouspagecontainer").css("z-index","20");
$("#newpagecontainer").css("z-index","40");
$("#previouspage").html($("#circular").html())
$("#newpagecontainer")[0].style.webkitAnimationName = 'flippageright';
$("#newpagecontainer")[0].addEventListener('webkitAnimationEnd', function() {
$("#currentpagecontainer").css("z-index","30");
$("#newpagecontainer")[0].style.webkitAnimationName = '';
$("#circular").html($("#nextpage").html());
});
return false;
}
function movebackward() {
$("#previouspagecontainer").css("z-index","40");
$("#currentpagecontainer").css("z-index","30");
$("#newpagecontainer").css("z-index","20");
$("#previouspagecontainer")[0].style.webkitAnimationName = 'flippageleft';
$("#previouspagecontainer")[0].addEventListener('webkitAnimationEnd', function() {
$("#previouspagecontainer")[0].style.webkitAnimationName = '';
$("#circular").html($("#previouspage").html());
});
return false;
}
The problem was caused by an unrelated element being removed from the DOM. I'm not quite sure why this resulted in breaking the swipe events but stopping this practice fixed my issue.