Detect if a scroll event is triggered manually in jQuery - javascript

This question was already asked here a long time ago:
Detect jquery event trigger by user or call by code
But it has never been answered conclusively (or maybe I'm simply not able to search properly).
Is it possible to detect whether a scroll event has been triggered by the user or by the jQuery animate function?
I am trying to prevent the scroll event to trigger itself while doing something like this:
$(document).scroll(function(){
$("html").stop(true);
var number = 400; //some other stuff is happening here
clearTimeout(tout);
tout = setTimeout(function(){
if(top == $(document).scrollTop()){
$("html").animate({
scrollTop: (number),
easing: "easeInQuad",
duration: 110
});
}
},120);
});
This code seems to be suitable:
$('#scroller').scroll(function(e) {
if (e.originalEvent) {
console.log('scroll happen manual scroll');
} else {
console.log('scroll happen by call');
}
});
But the originalEvent object isn't able to detect the animate trigger properly.
Is there any other way to do this?

Maybe :animated selector will help you:
$('#scroller').scroll(function(e) {
if ($(this).is(':animated')) {
console.log('scroll happen by animate');
} else if (e.originalEvent) {
// scroll happen manual scroll
console.log('scroll happen manual scroll');
} else {
// scroll happen by call
console.log('scroll happen by call');
}
});
Demo

I don't know how well this works with touch screen devices but this works for me on desktop at least
$(window).on('mousewheel', function(){
//code that will only fire on manual scroll input
});
$(window).scroll(function(){
//code that will fire on both mouse scroll and code based scroll
});
I don't think there is a way to only target the animated scroll (the accepted answer didn't work for me).
UPDATE: Warning!
Unfortunately, 'mousewheel' doesn't seem to pick up on users who manually grab the scroll bar and drag it or users who use the scroll bar arrow buttons :(
This still works ok for touch screen devices as their swipes seem to count as mouse scrolls. This isn't a great solution for desktop users though.

Using #Tony's accepted answer and #DanielTonon's comment I came up with the following solution:
var animatedScroll = false;
var lastAnimatedScroll = false;
$(window).scroll(function(event){
lastAnimatedScroll = animatedScroll;
animatedScroll = $('html, body').is(':animated');
});
This seems to solve the issue mentioned whereby jquery removes the .is(':animated') then scrolls one more pixel, which leads to .is(':animated') ending on a false. By storing the second to last version of .is(':animated') you can be (more) sure whether or not the scroll was an animated one or not.
When you want to know if the scroll was animated or not just check the lastAnimatedScroll variable.
This has NOT been thoroughly tested by me but has been correct on many page refreshes so I will assume it works well enough.

After attempting to implement the various solutions in this issue I came up with a different approach that is working well for me.
I use a manual boolean for whether an animation is running:
var isRunningAnimation = false;
and set it to true just before animating, and false in the jQuery animate callback function:
isRunningAnimation = true;
$('html').animate({
scrollLeft: 100,
scrollTop: 100
}, 400, 'swing', function() {
isRunningAnimation = false;
});
and then in the scroll listener just check that boolean:
$('scroll', function() {
if (!isRunningAnimation) {
// If we made it to here, the animation isn't running
}
});
Of course technically if the user decides to manually scroll during the animation, that won't trigger the on scroll logic either, but that seems like enough of an edge case to not worry about.

I would suggest First of all create a javascript function
// Attaching scroll event when document/window is loaded
function OnFirstLoad() {
if (document.attachEvent) {
document.attachEvent('onscroll', scrollEvent);
} else if (document.addEventListener) {
document.addEventListener('scroll', scrollEvent, false);
}
}
then, use either
window.onload = OnFirstLoad;
Or
$(document).ready(function () {
OnFirstLoad();
});
In This scroll event is a function
function scrollEvent(e) {
var body = document.body,
html = document.documentElement;
var docHeight = Math.max(body.scrollHeight, body.offsetHeight,
html.clientHeight, html.scrollHeight, html.offsetHeight);
var currentScroll = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
// implement your logic according to requirement
}

If you want to bind with jquery selector and check for event
$('#div.class').bind('scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove', function (e) {
if (e.which > 0 || e.type == "mousedown" || e.type == "mousewheel" || e.type == "touchmove") {
// any code
}
})

jQuery(document).on('click', 'p.questions__text a[data-clickid="delay_next_delivery"]', function(ele){
if(ele.originalEvent.isTrusted){
// human
} else {
// non human
}
});

Related

trigger function if scroll reaches a certain point only once. Not every time

I want to check if my users arrive at a certain point in my page. SO I created the following JS code:
$(document).on('scroll', function() {
if($(this).scrollTop()>=$('#page2').position().top){
alert("trigger");
}
})
Which checks if the users reached my id="page2". But I want this to trigger ONLY once, no matter if the users goes back up and back down, right now it gets trigger everytime the page2.position().top = scrollTop.
How can I do this ?
You can use event.namespace and off() to unbind event handler after execution of desired statement.
$(document).on('scroll.something', function() {
if ($(this).scrollTop() >= $('#page2').position().top) {
//Do something
//Unbind the event
$(document).off('scroll.something')
}
})
You can use this code to achieve your desired output.
var checkonce = false;
$(document).on('scroll', function() {
if($(this).scrollTop()>=$('#page2').position().top){
if(checkonce == false) {
alert("trigger");
checkonce = true;
}
}
});
You can just off the scroll event on your document after the first scroll has reached.
Edit: Also it would be better if you name your events, Which will help us remove the specific event by using the name. (Satpal already mentioned this in his answer before me, I am improving my answer standard as well.)
$(document).on('scroll.Page2ScrollEvent', function() {
if($(this).scrollTop()>=$('#page2').position().top){
$(this).off('scroll.Page2ScrollEvent'); // remove specific scroll event.
alert("trigger");
}
})

menu hide and toggle strange behavior

$(function(){
$(".OpenTopMenu").click(function (e) {
$("#top_menu").slideToggle("fast");
e.stopPropagation();
});
$(document).click(function() {
$("#top_menu").hide();
});
$(document).on("touchend", function (event) {
if (!$(event.target).closest("#top_menu").length) {
$("#top_menu").hide();
}
});
});
Hi all, i ran into a strange problem with toggle and hide.
As you can see in my code. If i touch the menu button (.OpenTopMenu) the menu (#top_menu) toggle.
And here its the problem. If #top_menu is visible so when i touch on .OpenTopMenu, #top_menu will hide then toggle to visible again. So i can't really hide #top_menu on touching the menu button (.OpenTopMenu).
Can someone help me with this?
Thanks
Your touchend and click are basically doing the same thing. For mobile uses it's always good to know that a "click" can actually be seen as two events that rapidly follow each other, namely the "mousedown" and "mouseup" event, the last one triggering the "click". On mobile devices, the "click" is triggered at the same time as your "touchend". Now there's also an event called "touchstart" which is triggered when a user put's his / her finger on the glass.
You are right now wondering what all this has to do with your question. Well, it has to do with your document click..
Personally I would solve your problem in the following way;
var userClick = function(){
//you will need something that determines whether your user is
//using a mobile device or not.
return (Browser.isMobile)? "touchend" : "click";
};
var menu = {
isOnMenu:false,
isOnMenu_reset:null,
attachEvents:function(){
$('#top_menu').on('mouseenter',function(){
menu.isOnMenu = true;
}).on('mouseleave',function(){
menu.isOnMenu = false;
}).on('touchstart',function(){
clearTimeout(menu.isOnMenu_reset);
menu.isOnMenu = true;
}).on('touchend',function(){
menu.isOnMenu_reset = setTimeout(function(){
menu.isOnMenu = false;
},30);
});
$('.OpenTopMenu').on(userClick(),function(){
$("#top_menu").slideToggle("fast");
});
$(document).on(userClick(),function(){
if(!menu.isOnMenu){
$('#top_menu').slideToggle("fast");
}
});
},
init:function(){
menu.attachEvents();
}
};
$(function(){
menu.init();
});
Try to change your $(document).click() by somthing like $(".OpenTopMenu").blur(). This might not work with old browsers.
I only wanted click and touched for testing purpose.
But it only have to work with touchend. This is the working code that i finally use. Thanks.
$(document).ready(function(){
$(".OpenTopMenu").click(function(){
$("#top_menu").slideToggle("fast");
});
});
$(document).on("touchend", function(event){
var $trigger = $(".OpenTopMenu");
if($trigger !== event.target && !$trigger.has(event.target).length){
$("#top_menu").slideUp("fast");
}
});
I tried earlier with
!event.target.hasClass('OpenTopMenu') instead of $trigger !== event.target
in the if condition but it doesn't work. Can someone tell me why the upper code work and this one not?

How to add swipe feature to a image gallery?

I have an image gallery and I want to add swipe feature to it,next and prev big image. I don't want to use any pluggin. I have some code, I tried some but I was unable to make it work. Any advice is highly appreciated.
$(document).on("pagecreate","#pageone",function(){
$("img").on("swipeleft",function(){
console.log("Left");
});
$("img").on("swiperight",function(){
console.log("Right");
});
});
Jsfiddle
Thanks!
the swipeleft event listener is not available with only jQuery. You can use jQuery Mobile, or craft your own using the touchstart, touchmove, and touchend. Assuming you only want to execute something once, the following code should do:
var swiping = false;
$('#div').on('touchmove', function (event) {
swiping = true;
})
$('#div').on('touchstart', function (event) {
setTimeout(function() {
if ( swiping = true ) {
console.log('swiping');
}
}, 50)
})
The setTimeout likely isn't necessary since touchmove begins at the same time as touchstart - but I left it there in case any given browser performs differently.

jQuery Click event triggers more times on every window resize

People!
This is the first time I come here to ask something, so far, always when I had a problem, I could find a good answer here. So, in first place, thanks for this amazing community!
Now let's go to the problem:
I'm doing a responsive menu that check the window.resize event and, when it fits the minimum browser width, a click function for a button is allowed. If the browser width is greater, then the click function is unbound. I need to do this because the same element that is the button on the mobile version, is a visual element on the desktop version.
The problem is that, with the code that I have now, when the page is loaded, the click function works fine. But, if I resize the browser and click on the element again, it triggers more than once the state, sometimes leaving the impression that the function isn't triggered. And, if I resize the browser again, it triggers the click function more than the last time I clicked. Really annoying.
To help understand what is happening, I've made a simple example. Here's is the simple code (just to check the click function issue):
HTML:
<ul>
<li><span class="sub-toggle">Testing 01</span></li>
<li><span class="sub-toggle">Testing 02</span></li>
<li><span class="sub-toggle">Testing 03</span></li>
</ul>
CSS:
.sub-toggle{
display:block;
padding: 20px;
}
.sub-toggle.active{
background-color: #ffcc00;
color: #fff;
}
Javascript (jQuery):
jQuery(function($){
var i = 1;
// check if browser size is compatible with click event
onResize = function() {
// if browser size is ok, do the click function
if($(window).width() <= 480){
// click function
$('.sub-toggle').click(function(){
alert('click');
if($(this).hasClass('active')){
alert('active');
$(this).removeClass('active');
} else {
$(this).addClass('active');
}
});
} else{
// if browser size is greater than expected, unbind the click function
$('.sub-toggle').removeClass('active').unbind('click');
}
// just checking how many times the resize function is triggered
console.log('resize: '+ i);
i++;
}
$(document).ready(onResize);
var timer;
$(window).bind('resize', function(){
timer && clearTimeout(timer);
timer = setTimeout(onResize, 500);
});
});
(Edited to remove some unnecessary code)
If you want to see it in action, I've made a Fiddle (try resize the output frame to see it working): http://jsfiddle.net/C7ppv/1/
Maybe I've missing something really stupid, since I don't have a huge knowledge in JavaScript. But what I want to do is just trigger the click event once, even if multiple resizes.
I hope I could explain well my problem. I've searched and didn't found a solution for this issue (or maybe I just didn't know really well what to look for).
Any help would be appreciated!
Your code currently binds a new click events every time the method onResize is called and the window width is less than or equal to 480px.
Simply unbind any existing click events on the .sub-toggle element before binding a new one.
$('.sub-toggle').unbind('click').click(function() {
...
});
DEMO
The resize event is triggered multiple times during resizing, and each time you're binding a new click handler. My suggestion: bind only once, from outside the resize handler, and set a flag while resizing to let the click handler know if it should do something or not.
Then you won't even need to defer the handling of resize with setTimeout as you're doing.
DEMO
jQuery(function($){
var i = 1;
// flag to allow clicking
var clickAllowed = true;
// click function
$('.sub-toggle').click(function(){
if(clickAllowed) {
alert('click');
if($(this).hasClass('active')){
alert('active');
$(this).removeClass('active');
} else {
$(this).addClass('active');
}
}
});
// check if browser size is compatible with click event
onResize = function() {
//if browser size is ok, do the click function
if($(window).width() <= 480){
clickAllowed = true;
}
else{
// if browser size is greater than expected, disallow clicking
clickAllowed = false;
}
// just checking how many times the resize function is triggered
console.log('resize: '+ i);
i++;
}
$(document).ready(onResize);
var timer;
$(window).bind('resize', onResize);
});
Move $('.sub-toggle').click(function(){...} outside the onResize event handler and move if($(window).width() <= 480){...} into the click handler.

Javascript: do an action after user is done scrolling

I'm trying to figure out a way to do this. I have a list of boxes, each about 150px high. I am using javascript (and jquery) and want that, after a user scrolls down a page, the page will auto scroll so that the boxes line up with the rest of the page (that is to say, if the user scrolls and the y position of the page is not divisible by 150, it will scroll to that nearest point).
Now, I at the moment, I know I can activate an event using the .scroll() jquery event, and I can make it move to a certain point using .scrollTop(). But every pixel the user moves the scroll bar activates the whole function. So is there a way to delay the function from firing until the user hasn't scrolled, and if they should begin to scroll again, the function will halt?
As you are already using jQuery, have a look at Ben Alman's doTimeout plugin which already handles the debouncing of methods (which is what you are after).
Example shamelessly stolen from his website:
$(window).scroll(function(){
$.doTimeout( 'scroll', 250, function(){
// do something computationally expensive
});
});
This is basically the same as Šime Vidas' answer, but less complex:
var scrollTimeout = null;
$(window).scroll(function(){
if (scrollTimeout) clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(function(){yourFunctionGoesHere()},500);
});
500 is the delay. Should be ok for mouse scroll.
Sure, in the event handler for the scroll event, fire off a setTimeout for 100 or 200 milliseconds later. Have that setTimeout function you set inside of the scroll event handler do the positioning logic you mention above. Also have the scroll event handler clear any timeouts set by itself. This way, the last time the scroll event fires, the setTimeout function will get to complete as the scroll event has not cleared it.
The code:
var scrollTimeout = null;
var scrollendDelay = 500; // ms
$(window).scroll(function() {
if ( scrollTimeout === null ) {
scrollbeginHandler();
} else {
clearTimeout( scrollTimeout );
}
scrollTimeout = setTimeout( scrollendHandler, scrollendDelay );
});
function scrollbeginHandler() {
// this code executes on "scrollbegin"
document.body.style.backgroundColor = "yellow";
}
function scrollendHandler() {
// this code executes on "scrollend"
document.body.style.backgroundColor = "gray";
scrollTimeout = null;
}
This would be a scenario, where vanilla JavaScript would be useful.
var yourFunction = function(){
// do something computationally expensive
}
$(window).scroll(function(){
yfTime=setTimeout("yourFunction()",250);
});

Categories