So I currently use something like:
$(window).resize(function(){resizedw();});
But this gets called many times while resizing process goes on. Is it possible to catch an event when it ends?
You can use setTimeout() and clearTimeout()
function resizedw(){
// Haven't resized in 100ms!
}
var doit;
window.onresize = function(){
clearTimeout(doit);
doit = setTimeout(resizedw, 100);
};
Code example on jsfiddle.
I had luck with the following recommendation: http://forum.jquery.com/topic/the-resizeend-event
Here's the code so you don't have to dig through his post's link & source:
var rtime;
var timeout = false;
var delta = 200;
$(window).resize(function() {
rtime = new Date();
if (timeout === false) {
timeout = true;
setTimeout(resizeend, delta);
}
});
function resizeend() {
if (new Date() - rtime < delta) {
setTimeout(resizeend, delta);
} else {
timeout = false;
alert('Done resizing');
}
}
Thanks sime.vidas for the code!
This is the code that I write according to #Mark Coleman answer:
$(window).resize(function() {
clearTimeout(window.resizedFinished);
window.resizedFinished = setTimeout(function(){
console.log('Resized finished.');
}, 250);
});
Thanks Mark!
Internet Explorer provides a resizeEnd event. Other browsers will trigger the resize event many times while you're resizing.
There are other great answers here that show how to use setTimeout and the .throttle, .debounce methods from lodash and underscore, so I will mention Ben Alman's throttle-debounce jQuery plugin which accomplishes what you're after.
Suppose you have this function that you want to trigger after a resize:
function onResize() {
console.log("Resize just happened!");
};
Throttle Example
In the following example, onResize() will only be called once every 250 milliseconds during a window resize.
$(window).resize( $.throttle( 250, onResize) );
Debounce Example
In the following example, onResize() will only be called once at the end of a window resizing action. This achieves the same result that #Mark presents in his answer.
$(window).resize( $.debounce( 250, onResize) );
There is an elegant solution using the Underscore.js So, if you are using it in your project you can do the following -
$( window ).resize( _.debounce( resizedw, 500 ) );
This should be enough :) But, If you are interested to read more on that, you can check my blog post - http://rifatnabi.com/post/detect-end-of-jquery-resize-event-using-underscore-debounce(deadlink)
There is a much simpler method to execute a function at the end of the resize than calculate the delta time between two calls, simply do it like this :
var resizeId;
$(window).resize(function() {
clearTimeout(resizeId);
resizeId = setTimeout(resizedEnded, 500);
});
function resizedEnded(){
...
}
And the equivalent for Angular2 :
private resizeId;
#HostListener('window:resize', ['$event'])
onResized(event: Event) {
clearTimeout(this.resizeId);
this.resizeId = setTimeout(() => {
// Your callback method here.
}, 500);
}
For the angular method, use the () => { } notation in the setTimeout to preserve the scope, otherwise you will not be able to make any function calls or use this.
One solution is extend jQuery with a function, e.g.: resized
$.fn.resized = function (callback, timeout) {
$(this).resize(function () {
var $this = $(this);
if ($this.data('resizeTimeout')) {
clearTimeout($this.data('resizeTimeout'));
}
$this.data('resizeTimeout', setTimeout(callback, timeout));
});
};
Sample usage:
$(window).resized(myHandler, 300);
You can store a reference id to any setInterval or setTimeout. Like this:
var loop = setInterval(func, 30);
// some time later clear the interval
clearInterval(loop);
To do this without a "global" variable you can add a local variable to the function itself. Ex:
$(window).resize(function() {
clearTimeout(this.id);
this.id = setTimeout(doneResizing, 500);
});
function doneResizing(){
$("body").append("<br/>done!");
}
You can use setTimeout() and clearTimeout() in conjunction with jQuery.data:
$(window).resize(function() {
clearTimeout($.data(this, 'resizeTimer'));
$.data(this, 'resizeTimer', setTimeout(function() {
//do something
alert("Haven't resized in 200ms!");
}, 200));
});
Update
I wrote an extension to enhance jQuery's default on (& bind)-event-handler. It attaches an event handler function for one or more events to the selected elements if the event was not triggered for a given interval. This is useful if you want to fire a callback only after a delay, like the resize event, or else.
https://github.com/yckart/jquery.unevent.js
;(function ($) {
var methods = { on: $.fn.on, bind: $.fn.bind };
$.each(methods, function(k){
$.fn[k] = function () {
var args = [].slice.call(arguments),
delay = args.pop(),
fn = args.pop(),
timer;
args.push(function () {
var self = this,
arg = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(self, [].slice.call(arg));
}, delay);
});
return methods[k].apply(this, isNaN(delay) ? arguments : args);
};
});
}(jQuery));
Use it like any other on or bind-event handler, except that you can pass an extra parameter as a last:
$(window).on('resize', function(e) {
console.log(e.type + '-event was 200ms not triggered');
}, 200);
http://jsfiddle.net/ARTsinn/EqqHx/
Mark Coleman's answer is certainly far better than the selected answer, but if you want to avoid the global variable for the timeout ID (the doit variable in Mark's answer), you could do one of the following:
(1) Use a an immediately invoked function expression (IIFE) to create a closure.
$(window).resize((function() { // This function is immediately invoked
// and returns the closure function.
var timeoutId;
return function() {
clearTimeout(timeoutId);
timeoutId = setTimeout(function() {
timeoutId = null; // You could leave this line out.
// Code to execute on resize goes here.
}, 100);
};
})());
(2) Use a property of the event handler function.
$(window).resize(function() {
var thisFunction = arguments.callee;
clearTimeout(thisFunction.timeoutId);
thisFunction.timeoutId = setTimeout(function() {
thisFunction.timeoutId = null; // You could leave this line out.
// Code to execute on resize goes here.
}, 100);
});
This is what I use for delaying repeated actions, it can be called in multiple places in your code:
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
Usage:
$(window).resize(function () {
debounce(function() {
//...
}, 500);
});
ResizeStart and ResizeEnd events for window
http://jsfiddle.net/04fLy8t4/
I implemented a function which trigs two events on the user DOM element:
resizestart
resizeend
Code:
var resizeEventsTrigger = (function () {
function triggerResizeStart($el) {
$el.trigger('resizestart');
isStart = !isStart;
}
function triggerResizeEnd($el) {
clearTimeout(timeoutId);
timeoutId = setTimeout(function () {
$el.trigger('resizeend');
isStart = !isStart;
}, delay);
}
var isStart = true;
var delay = 200;
var timeoutId;
return function ($el) {
isStart ? triggerResizeStart($el) : triggerResizeEnd($el);
};
})();
$("#my").on('resizestart', function () {
console.log('resize start');
});
$("#my").on('resizeend', function () {
console.log('resize end');
});
window.onresize = function () {
resizeEventsTrigger( $("#my") );
};
This is a modification to Dolan's code above, I've added a feature which checks the window size at the start of the resize and compares it to the size at the end of the resize, if size is either bigger or smaller than the margin (eg. 1000) then it reloads.
var rtime = new Date(1, 1, 2000, 12,00,00);
var timeout = false;
var delta = 200;
var windowsize = $window.width();
var windowsizeInitial = $window.width();
$(window).on('resize',function() {
windowsize = $window.width();
rtime = new Date();
if (timeout === false) {
timeout = true;
setTimeout(resizeend, delta);
}
});
function resizeend() {
if (new Date() - rtime < delta) {
setTimeout(resizeend, delta);
return false;
} else {
if (windowsizeInitial > 1000 && windowsize > 1000 ) {
setTimeout(resizeend, delta);
return false;
}
if (windowsizeInitial < 1001 && windowsize < 1001 ) {
setTimeout(resizeend, delta);
return false;
} else {
timeout = false;
location.reload();
}
}
windowsizeInitial = $window.width();
return false;
}
Here is VERY simple script to trigger both a 'resizestart' and 'resizeend' event on the window object.
There is no need to muck around with dates and times.
The d variable represents the number of milliseconds between resize events before triggering the resize end event, you can play with this to change how sensitive the end event is.
To listen to these events all you need to do is:
resizestart: $(window).on('resizestart', function(event){console.log('Resize Start!');});
resizeend:
$(window).on('resizeend', function(event){console.log('Resize End!');});
(function ($) {
var d = 250, t = null, e = null, h, r = false;
h = function () {
r = false;
$(window).trigger('resizeend', e);
};
$(window).on('resize', function (event) {
e = event || e;
clearTimeout(t);
if (!r) {
$(window).trigger('resizestart', e);
r = true;
}
t = setTimeout(h, d);
});
}(jQuery));
i wrote a litte wrapper function on my own...
onResize = function(fn) {
if(!fn || typeof fn != 'function')
return 0;
var args = Array.prototype.slice.call(arguments, 1);
onResize.fnArr = onResize.fnArr || [];
onResize.fnArr.push([fn, args]);
onResize.loop = function() {
$.each(onResize.fnArr, function(index, fnWithArgs) {
fnWithArgs[0].apply(undefined, fnWithArgs[1]);
});
};
$(window).on('resize', function(e) {
window.clearTimeout(onResize.timeout);
onResize.timeout = window.setTimeout("onResize.loop();", 300);
});
};
Here is the usage:
var testFn = function(arg1, arg2) {
console.log('[testFn] arg1: '+arg1);
console.log('[testFn] arg2: '+arg2);
};
// document ready
$(function() {
onResize(testFn, 'argument1', 'argument2');
});
(function(){
var special = jQuery.event.special,
uid1 = 'D' + (+new Date()),
uid2 = 'D' + (+new Date() + 1);
special.resizestart = {
setup: function() {
var timer,
handler = function(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
} else {
evt.type = 'resizestart';
jQuery.event.handle.apply(_self, _args);
}
timer = setTimeout( function(){
timer = null;
}, special.resizestop.latency);
};
jQuery(this).bind('resize', handler).data(uid1, handler);
},
teardown: function(){
jQuery(this).unbind( 'resize', jQuery(this).data(uid1) );
}
};
special.resizestop = {
latency: 200,
setup: function() {
var timer,
handler = function(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout( function(){
timer = null;
evt.type = 'resizestop';
jQuery.event.handle.apply(_self, _args);
}, special.resizestop.latency);
};
jQuery(this).bind('resize', handler).data(uid2, handler);
},
teardown: function() {
jQuery(this).unbind( 'resize', jQuery(this).data(uid2) );
}
};
})();
$(window).bind('resizestop',function(){
//...
});
Well, as far as the window manager is concerned, each resize event is its own message, with a distinct beginning and end, so technically, every time the window is resized, it is the end.
Having said that, maybe you want to set a delay to your continuation? Here's an example.
var t = -1;
function doResize()
{
document.write('resize');
}
$(document).ready(function(){
$(window).resize(function(){
clearTimeout(t);
t = setTimeout(doResize, 1000);
});
});
I guess my case might be different from some others but I had a problem only with orientation change on iOS but wanted the resize event to run immediately. I used the ScreenOrientation API:
screen.orientation.addEventListener('change', (e) => {});
I took a slightly different tack and relied on mouseUp as the end of the resize event. trackSize is called on documentReady and the initial value of wide is set then, too.
var THRESHOLD = 784;
var TALL = 125, SHORT = 50;
var wide = (window.document.body.clientWidth >= THRESHOLD );
function trackSize() {
if( !wide ) {
setHeight( TALL );
} else {
setHeight( SHORT );
}
parent.window.addEventListener('resize', onResize);
}
function onResize(e) {
parent.window.removeEventListener('resize', onResize);
parent.window.addEventListener('mouseup', onMouseUp) ;
}
function onMouseUp(e) {
parent.window.removeEventListener('mouseup', onMouseUp);
wide = (window.document.body.clientWidth >= THRESHOLD);
trackSize();
}
After having set our window's initial height, we begin listening for a resize event. When it starts, we stop listening and start listening for the mouseUp event. Thus, we know that mouseUp will end the resizing. In mouseUp, we stop listening and set a toggle based on the window's width, then loop back to trackSize.
trackSize starts by setting the window's height based on the toggle -- if below the threshold, we increase height (because Bootstrap columns stack at small widths), otherwise set to standard. And then we listen again for the next resize event.
CAVEAT: This solution doesn't really work for resizing instantly using the maximize or restore window buttons. Maybe adding a test like isMouseDown and bypassing the mouse listener would suffice - I haven't yet tested that.
since the selected answer didn't actually work .. and if you're not using jquery here is a simple throttle function with an example of how to use it with window resizing
function throttle(end,delta) {
var base = this;
base.wait = false;
base.delta = 200;
base.end = end;
base.trigger = function(context) {
//only allow if we aren't waiting for another event
if ( !base.wait ) {
//signal we already have a resize event
base.wait = true;
//if we are trying to resize and we
setTimeout(function() {
//call the end function
if(base.end) base.end.call(context);
//reset the resize trigger
base.wait = false;
}, base.delta);
}
}
};
var windowResize = new throttle(function() {console.log('throttle resize');},200);
window.onresize = function(event) {
windowResize.trigger();
}
this worked for me as I did not want to use any plugins.
$(window).resize(function() {
var originalWindowSize = 0;
var currentWidth = 0;
var setFn = function () {
originalWindowSize = $(window).width();
};
var checkFn = function () {
setTimeout(function () {
currentWidth = $(window).width();
if (currentWidth === originalWindowSize) {
console.info("same? = yes")
// execute code
} else {
console.info("same? = no");
// do nothing
}
}, 500)
};
setFn();
checkFn();
});
On window re-size invoke "setFn" which gets width of window and save as "originalWindowSize". Then invoke "checkFn" which after 500ms (or your preference) gets the current window size, and compares the original to the current, if they are not the same, then the window is still being re-sized. Don't forget to remove console messages in production, and (optional) can make "setFn" self executing.
var resizeTimer;
$( window ).resize(function() {
if(resizeTimer){
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(function() {
//your code here
resizeTimer = null;
}, 200);
});
This worked for what I was trying to do in chrome. This won't fire the callback until 200ms after last resize event.
UPDATE!
Better alternative also created by me is here:
https://stackoverflow.com/a/23692008/2829600
(supports "delete functions")
ORIGINAL POST:
I wrote this simple function for handling delay in execution, useful inside jQuery .scroll() and .resize() So callback_f will run only once for specific id string.
function delay_exec( id, wait_time, callback_f ){
// IF WAIT TIME IS NOT ENTERED IN FUNCTION CALL,
// SET IT TO DEFAULT VALUE: 0.5 SECOND
if( typeof wait_time === "undefined" )
wait_time = 500;
// CREATE GLOBAL ARRAY(IF ITS NOT ALREADY CREATED)
// WHERE WE STORE CURRENTLY RUNNING setTimeout() FUNCTION FOR THIS ID
if( typeof window['delay_exec'] === "undefined" )
window['delay_exec'] = [];
// RESET CURRENTLY RUNNING setTimeout() FUNCTION FOR THIS ID,
// SO IN THAT WAY WE ARE SURE THAT callback_f WILL RUN ONLY ONE TIME
// ( ON LATEST CALL ON delay_exec FUNCTION WITH SAME ID )
if( typeof window['delay_exec'][id] !== "undefined" )
clearTimeout( window['delay_exec'][id] );
// SET NEW TIMEOUT AND EXECUTE callback_f WHEN wait_time EXPIRES,
// BUT ONLY IF THERE ISNT ANY MORE FUTURE CALLS ( IN wait_time PERIOD )
// TO delay_exec FUNCTION WITH SAME ID AS CURRENT ONE
window['delay_exec'][id] = setTimeout( callback_f , wait_time );
}
// USAGE
jQuery(window).resize(function() {
delay_exec('test1', 1000, function(){
console.log('1st call to delay "test1" successfully executed!');
});
delay_exec('test1', 1000, function(){
console.log('2nd call to delay "test1" successfully executed!');
});
delay_exec('test1', 1000, function(){
console.log('3rd call to delay "test1" successfully executed!');
});
delay_exec('test2', 1000, function(){
console.log('1st call to delay "test2" successfully executed!');
});
delay_exec('test3', 1000, function(){
console.log('1st call to delay "test3" successfully executed!');
});
});
/* RESULT
3rd call to delay "test1" successfully executed!
1st call to delay "test2" successfully executed!
1st call to delay "test3" successfully executed!
*/
var flag=true;
var timeloop;
$(window).resize(function(){
rtime=new Date();
if(flag){
flag=false;
timeloop=setInterval(function(){
if(new Date()-rtime>100)
myAction();
},100);
}
})
function myAction(){
clearInterval(timeloop);
flag=true;
//any other code...
}
I don't know is my code work for other but it's really do a great job for me. I got this idea by analyzing Dolan Antenucci code because his version is not work for me and I really hope it'll be helpful to someone.
var tranStatus = false;
$(window).resizeend(200, function(){
$(".cat-name, .category").removeAttr("style");
//clearTimeout(homeResize);
$("*").one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",function(event) {
tranStatus = true;
});
processResize();
});
function processResize(){
homeResize = setInterval(function(){
if(tranStatus===false){
console.log("not yet");
$("*").one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",function(event) {
tranStatus = true;
});
}else{
text_height();
clearInterval(homeResize);
}
},200);
}
I wrote a function that passes a function when wrapped in any resize event. It uses an interval so that the resize even isn't constantly creating timeout events. This allows it to perform independently of the resize event other than a log entry that should be removed in production.
https://github.com/UniWrighte/resizeOnEnd/blob/master/resizeOnEnd.js
$(window).resize(function(){
//call to resizeEnd function to execute function on resize end.
//can be passed as function name or anonymous function
resizeEnd(function(){
});
});
//global variables for reference outside of interval
var interval = null;
var width = $(window).width();
var numi = 0; //can be removed in production
function resizeEnd(functionCall){
//check for null interval
if(!interval){
//set to new interval
interval = setInterval(function(){
//get width to compare
width2 = $(window).width();
//if stored width equals new width
if(width === width2){
//clear interval, set to null, and call passed function
clearInterval(interval);
interval = null; //precaution
functionCall();
}
//set width to compare on next interval after half a second
width = $(window).width();
}, 500);
}else{
//logging that should be removed in production
console.log("function call " + numi++ + " and inteval set skipped");
}
}
Related
I want to be able to run the below function continuously while the mouse is down. At the moment the function excecutes once per click. I want to be able to continue running the function while mousedown and stop when the mouse is released.
$('#md').mousedown(function(e){
var pCords = e.pageY +e.pageY;
var max = $(document).height()+$(document).width();
var dg = 2*( pCords / max );
$('#div').css({ 'transform':'rotate('+dg+'deg) '});
});
This issue has been addressed in: jQuery continuous mousedown
It will involve setting up a flag that you can set to true when the first mousedown event occurs, and calling a function that only finishes once the flag is set back to false when the mouseup event occurs.
Easy way to do with time delay between execution of event handler:
var timerId = 0;
$('#md').mousedown(function(e){
timerId = setInterval(function() {
var pCords = e.pageY +e.pageY;
var max = $(document).height()+$(document).width();
var dg = 2*( pCords / max );
$('#div').css({ 'transform':'rotate('+dg+'deg) '});
}, 20); // specify the required time delay here
});
$('#md').mouseup(function(e){
clearInterval(timerId);
});
You can do it using a simple semaphore. Here's an example:
var lock = true;
$('#md').mousedown(function() {
lock = false;
setTimeout(function() {
while(!lock) {
// Do your stuff here
}
}, 0);
});
$(document).mouseup(function() {
lock = true;
});
I hope this does the trick.
The setTimeout function detaches the execution of the function and creates an asynchronous process - this makes your code non-blocking.
I'm sure there are more sophisticated ways to solve this problem. It's just that I haven't had my coffee yet. :)
like this? making a global boolean variable to track if mouse is up, then looping your function while mouse is down on that element.
var ismouseup = false;
$(document).ready(function(){
$('#md').mousedown(function(e){
ismouseup = false;
while(!ismouseup)
{
//do your thing
}
});
$('#md').mouseup(function(e){
ismouseup = true;
}
});
--edit: set ismouseup to true when mouseup. sorry.
You can use setTimeout. This will run every 1 sec when mousedown
$('#md').on({
mousedown: function () {
$(this).data('clicked', true);
var process = function() {
if ($(this).data('clicked')) {
// Do your stuff
setTimeout(process, 100);
}
};
process();
},
mouseup: function () {
$(this).data('clicked', false);
}
});
As an option you can set the handler for mousemove in mousedown and remove in mouseup as in code
function movehandler(e) {
var pCords = e.pageY + e.pageY;
var max = $('#md').height();
var dg = 20 * (pCords / max);
$('#md').css({
'transform': 'rotate(' + dg + 'deg) '
});
}
$('#md').mousedown(function (e) {
$('body').on('mousemove', movehandler);
});
$('#md').mouseup(function (e) {
$('body').off('mousemove');
});
$('body').mouseleave(function (e) {
$('body').off('mousemove');
});
Jsfiffle Example is not ideally written but you can get the idea
EDIT: Note that $('body').on('mousemove', might not be sufficient dependent on your markup.
So, I'd like to fire a function only once on scroll (using Scrollstop, given by a stackoverflow answer)
The problem is that I don't get to fire the function only once. I've tried different solutions ( .on(), setting a counter, setting it outside/inside the window.scrollstop function) but nothing worked.
I don't think it's difficult, but.. I didn't get to make it work so far.
Here's the plugin I'm using
$.fn.scrollStopped = function(callback) {
$(this).scroll(function(){
var self = this, $this = $(self);
if ($this.data('scrollTimeout')) {
clearTimeout($this.data('scrollTimeout'));
}
$this.data('scrollTimeout', setTimeout(callback,300,self));
});
};
and here's my code:
$(window).scrollStopped(function(){
if ($(".drawing1").withinViewport()) {
doNothing()
}
})
var doNothing = function() {
$('#drawing1').lazylinepainter('paint');
}
(removed the counter since it didn't work)
Live demo here
PS: the function I'd like to make happen only once is the lazyPaint. It begins when we scroll to the element but it fires once again when it ends.
Here's my version of having a function fire once while listening to the scroll event:
var fired = false;
window.addEventListener("scroll", function(){
if (document.body.scrollTop >= 1000 && fired === false) {
alert('This will happen only once');
fired = true;
}
}, true)
how about using a variable to see whether it was previously fired:
var fired = 0;
$.fn.scrollStopped = function(callback) {
$(this).scroll(function(){
if(fired == 0){
var self = this, $this = $(self);
if ($this.data('scrollTimeout')) {
clearTimeout($this.data('scrollTimeout'));
}
$this.data('scrollTimeout', setTimeout(callback,300,self));
fired = 1;
}
});
};
These anwsers didn't work for me so here's my code:
var fired = 0;
jQuery(this).scroll(function(){
if(fired == 0){
alert("fired");
fired = 1;
}
});
How about this solution?
function scrollEvent() {
var hT = $('#scroll-to').offset().top,
hH = $('#scroll-to').outerHeight(),
wH = $(window).height(),
wS = $(this).scrollTop();
if (wS > (hT+hH-wH)){
console.log('H1 on the view!');
window.removeEventListener("scroll", scrollEvent);
}
}
window.addEventListener("scroll", scrollEvent);
The question is a bit old, but as it popped up first when I search for "addeventlistener scroll once", I will add this reply. There is now a { once: true } parameter to only trigger an event once.
window.addEventListener("scroll", () => {
/* your code here */
}, { once: true });
If i want to bind an event on page scrolling i can use scroll();.
But how to fire when scroll() is ended up?
I would like to reproduce this:
$(window).scroll(function(){
//do somenthing
});
$(window).scrollSTOPPED(function(){ //--> when i'm scrolling then i stop to scrolling (so NOT when page scrollbar is at the end top or bottom :)
//do somenthing else
});
any ideas?
tiny jquery way
$.fn.scrollStopped = function(callback) {
var that = this, $this = $(that);
$this.scroll(function(ev) {
clearTimeout($this.data('scrollTimeout'));
$this.data('scrollTimeout', setTimeout(callback.bind(that), 250, ev));
});
};
After 250 ms from the last scroll event, this will invoke the "scrollStopped" callback.
http://jsfiddle.net/wtRrV/256/
lodash (even smaller)
function onScrollStopped(domElement, callback) {
domElement.addEventListener('scroll', _.debounce(callback, 250));
}
http://jsfiddle.net/hotw1o2j/
pure js (technically the smallest)
function onScrollStopped(domElement, callback, timeout = 250) {
domElement.addEventListener('scroll', () => {
clearTimeout(callback.timeout);
callback.timeout = setTimeout(callback, timeout);
});
}
https://jsfiddle.net/kpsxdcv8/15/
strange fact
clearTimeout and clearInterval params don't have to be defined and can even be wrong types or even omitted.
http://jsfiddle.net/2w5zLwvx/
the event itself doesn't exist as scroll is a single event fired everytime the user scrolls by a certain increment.
What you can do however is emulate the event.
Credit to James Padolsey for this, lifted from his webpage:.
Read it here to fully understand the code and how it is implemented.
http://james.padolsey.com/javascript/special-scroll-events-for-jquery/
(function(){
var special = jQuery.event.special,
uid1 = 'D' + (+new Date()),
uid2 = 'D' + (+new Date() + 1);
special.scrollstart = {
setup: function() {
var timer,
handler = function(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
} else {
evt.type = 'scrollstart';
jQuery.event.handle.apply(_self, _args);
}
timer = setTimeout( function(){
timer = null;
}, special.scrollstop.latency);
};
jQuery(this).bind('scroll', handler).data(uid1, handler);
},
teardown: function(){
jQuery(this).unbind( 'scroll', jQuery(this).data(uid1) );
}
};
special.scrollstop = {
latency: 300,
setup: function() {
var timer,
handler = function(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout( function(){
timer = null;
evt.type = 'scrollstop';
jQuery.event.handle.apply(_self, _args);
}, special.scrollstop.latency);
};
jQuery(this).bind('scroll', handler).data(uid2, handler);
},
teardown: function() {
jQuery(this).unbind( 'scroll', jQuery(this).data(uid2) );
}
}; })();
Probably worth noting that there are several questions related to yours, so this may be a possible duplication.
e.g.
Javascript: do an action after user is done scrolling
and Fire event after scrollling scrollbars or mousewheel with javascript
You can verify if window.scrollY == 0
$(window).scroll(function(){
if (window.scrollY == 0) {
//...
}
});
But this event will be fired at every scroll.
I prefer to be able to listen on a event. This is what I do:
The jquery plugin:
+function(jQuery){
var scrollStopEventEmitter = function(element, jQuery) {
this.scrollTimeOut = false;
this.element = element;
jQuery(element).on('scroll', $.proxy(this.onScroll, this));
}
scrollStopEventEmitter.prototype.onScroll = function()
{
if (this.scrollTimeOut != false) {
clearTimeout(this.scrollTimeOut);
}
var context = this;
this.scrollTimeOut = setTimeout(function(){ context.onScrollStop()}, 250);
}
scrollStopEventEmitter.prototype.onScrollStop = function()
{
this.element.trigger('scrollStop');
}
jQuery.fn.scrollStopEventEmitter = function(jQuery) {
return new scrollStopEventEmitter(this, jQuery);
};
}($);
In this case, window will now trigger scrollStop event
$(window).scrollStopEventEmitter($);
Now I can listen on scrollStop
$(window).on('scrollStop',function(){
// code
What I am trying to do is make a function that calls another function continuously as long as the mouse button is being held. I am doing this just so I can get a better understanding of .call() and callbacks. Here is my code:
jQuery.fn.contmousedown = function(mousedownCallback){
var interval, self = this;
jQuery(this).mousedown(function(event){
interval = setInterval(function(self, event){
mousedownCallback.call(self, event);
console.log('on');
},0);
});
jQuery(this).mouseup(function(event){
clearInterval(interval);
});
}
$(document).contmousedown(function(e){
$('#run').html(e.pageX+', '+e.pageY);
});
And the error I receive is:
Uncaught TypeError: Cannot read property 'pageX' of undefined
And of course I am receiving that about 300x a second. :) If i change my interval declaration line to interval = setInterval(function(self){ then I get 'on' logged to my console at about 300x a second but I lose the event. So my question is how can I make it so I can callback the function and pass it the event parameter?
Example - http://jsfiddle.net/ZxKxD/
Thinking about this on my commute home, I decided that it would be nice to keep both events. So here is my final code:
jQuery.fn.mousehold = function(mousedownCallback){
var interval, self = this, move_event;
jQuery(this).mousemove(function(e){
move_event = e;
});
jQuery(this).mousedown(function(click_event){
interval = setInterval(function(){
mousedownCallback.call(self, click_event, move_event);
},0);
});
jQuery(this).mouseup(function(){
clearInterval(interval);
});
jQuery(this).mouseout(function(){
clearInterval(interval);
});
}
$(document).mousehold(function(click_event, move_event){
$('#run').html(click_event.pageX+':'+move_event.pageX+', '
+click_event.pageY+':'+move_event.pageY);
});
setInterval does not pass arguments to the callback, so remove the self and event arguments. You don't "lose" the event in doing so.
$.fn.contmousedown = function(mousedownCallback)
{
var interval,
self = this;
$(this).mousedown(function(event)
{
interval = setInterval(function()
{
mousedownCallback.call(self, event);
console.log('on');
}, 0);
});
$(this).mouseup(function()
{
clearInterval(interval);
});
}
Demo: http://jsfiddle.net/mattball/9veUQ
So how can I get a continuous update on the cursor position?
Use mousemove to capture the event.
$.fn.contmousedown = function(mousedownCallback)
{
var interval,
self = this,
event;
$(this).mousemove(function(e)
{
event = e;
});
$(this).mousedown(function ()
{
interval = setInterval(function()
{
mousedownCallback.call(self, event);
}, 0);
});
$(this).mouseup(function()
{
clearInterval(interval);
});
};
Demo: http://jsfiddle.net/mattball/dVaWS/
Ok with this..
$(window).scroll(function()
{
$('.slides_layover').removeClass('showing_layover');
$('#slides_effect').show();
});
I can tell when someone is scrolling from what I understand. So with that I am trying to figure out how to catch when someone has stopped. From the above example you can see I am removing a class from a set of elements while the scrolling is occurring. However, I want to put that class back on when the user stops scrolling.
The reason for this is I am intent on having a layover show while the page is scrolling to give the page a special effect I am attempting to work on. But the one class I am trying to remove while scrolling conflicts with that effect as its a transparency effect to some nature.
$(window).scroll(function() {
clearTimeout($.data(this, 'scrollTimer'));
$.data(this, 'scrollTimer', setTimeout(function() {
// do something
console.log("Haven't scrolled in 250ms!");
}, 250));
});
Update
I wrote an extension to enhance jQuery's default on-event-handler. It attaches an event handler function for one or more events to the selected elements and calls the handler function if the event was not triggered for a given interval. This is useful if you want to fire a callback only after a delay, like the resize event, or such.
It is important to check the github-repo for updates!
https://github.com/yckart/jquery.unevent.js
;(function ($) {
var on = $.fn.on, timer;
$.fn.on = function () {
var args = Array.apply(null, arguments);
var last = args[args.length - 1];
if (isNaN(last) || (last === 1 && args.pop())) return on.apply(this, args);
var delay = args.pop();
var fn = args.pop();
args.push(function () {
var self = this, params = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(self, params);
}, delay);
});
return on.apply(this, args);
};
}(this.jQuery || this.Zepto));
Use it like any other on or bind-event handler, except that you can pass an extra parameter as a last:
$(window).on('scroll', function(e) {
console.log(e.type + '-event was 250ms not triggered');
}, 250);
http://yckart.github.com/jquery.unevent.js/
(this demo uses resize instead of scroll, but who cares?!)
Using jQuery throttle / debounce
jQuery debounce is a nice one for problems like this. jsFidlle
$(window).scroll($.debounce( 250, true, function(){
$('#scrollMsg').html('SCROLLING!');
}));
$(window).scroll($.debounce( 250, function(){
$('#scrollMsg').html('DONE!');
}));
The second parameter is the "at_begin" flag. Here I've shown how to execute code both at "scroll start" and "scroll finish".
Using Lodash
As suggested by Barry P, jsFiddle, underscore or lodash also have a debounce, each with slightly different apis.
$(window).scroll(_.debounce(function(){
$('#scrollMsg').html('SCROLLING!');
}, 150, { 'leading': true, 'trailing': false }));
$(window).scroll(_.debounce(function(){
$('#scrollMsg').html('STOPPED!');
}, 150));
Rob W suggected I check out another post here on stack that was essentially a similar post to my original one. Which reading through that I found a link to a site:
http://james.padolsey.com/javascript/special-scroll-events-for-jquery/
This actually ended up helping solve my problem very nicely after a little tweaking for my own needs, but over all helped get a lot of the guff out of the way and saved me about 4 hours of figuring it out on my own.
Seeing as this post seems to have some merit, I figured I would come back and provide the code found originally on the link mentioned, just in case the author ever decided to go a different direction with the site and ended up taking down the link.
(function(){
var special = jQuery.event.special,
uid1 = 'D' + (+new Date()),
uid2 = 'D' + (+new Date() + 1);
special.scrollstart = {
setup: function() {
var timer,
handler = function(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
} else {
evt.type = 'scrollstart';
jQuery.event.handle.apply(_self, _args);
}
timer = setTimeout( function(){
timer = null;
}, special.scrollstop.latency);
};
jQuery(this).bind('scroll', handler).data(uid1, handler);
},
teardown: function(){
jQuery(this).unbind( 'scroll', jQuery(this).data(uid1) );
}
};
special.scrollstop = {
latency: 300,
setup: function() {
var timer,
handler = function(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout( function(){
timer = null;
evt.type = 'scrollstop';
jQuery.event.handle.apply(_self, _args);
}, special.scrollstop.latency);
};
jQuery(this).bind('scroll', handler).data(uid2, handler);
},
teardown: function() {
jQuery(this).unbind( 'scroll', jQuery(this).data(uid2) );
}
};
})();
I agreed with some of the comments above that listening for a timeout wasn't accurate enough as that will trigger when you stop moving the scroll bar for long enough instead of when you stop scrolling. I think a better solution is to listen for the user letting go of the mouse (mouseup) as soon as they start scrolling:
$(window).scroll(function(){
$('#scrollMsg').html('SCROLLING!');
var stopListener = $(window).mouseup(function(){ // listen to mouse up
$('#scrollMsg').html('STOPPED SCROLLING!');
stopListner(); // Stop listening to mouse up after heard for the first time
});
});
and an example of it working can be seen in this JSFiddle
ES6 style with checking scrolling start also.
function onScrollHandler(params: {
onStart: () => void,
onStop: () => void,
timeout: number
}) {
const {onStart, onStop, timeout = 200} = params
let timer = null
return (event) => {
if (timer) {
clearTimeout(timer)
} else {
onStart && onStart(event)
}
timer = setTimeout(() => {
timer = null
onStop && onStop(event)
}, timeout)
}
}
Usage:
yourScrollableElement.addEventListener('scroll', onScrollHandler({
onStart: (event) => {
console.log('Scrolling has started')
},
onStop: (event) => {
console.log('Scrolling has stopped')
},
timeout: 123 // Remove to use default value
}))
You could set an interval that runs every 500 ms or so, along the lines of the following:
var curOffset, oldOffset;
oldOffset = $(window).scrollTop();
var $el = $('.slides_layover'); // cache jquery ref
setInterval(function() {
curOffset = $(window).scrollTop();
if(curOffset != oldOffset) {
// they're scrolling, remove your class here if it exists
if($el.hasClass('showing_layover')) $el.removeClass('showing_layover');
} else {
// they've stopped, add the class if it doesn't exist
if(!$el.hasClass('showing_layover')) $el.addClass('showing_layover');
}
oldOffset = curOffset;
}, 500);
I haven't tested this code, but the principle should work.
function scrolled() {
//do by scroll start
$(this).off('scroll')[0].setTimeout(function(){
//do by scroll end
$(this).on('scroll',scrolled);
}, 500)
}
$(window).on('scroll',scrolled);
very small Version with start and end ability
This detects the scroll stop after 1 milisecond (or change it) using a global timer:
var scrollTimer;
$(window).on("scroll",function(){
clearTimeout(scrollTimer);
//Do what you want whilst scrolling
scrollTimer=setTimeout(function(){afterScroll()},1);
})
function afterScroll(){
//I catched scroll stop.
}
Ok this is something that I've used before.
Basically you look a hold a ref to the last scrollTop().
Once your timeout clears, you check the current scrollTop() and if they are the same, you are done scrolling.
$(window).scroll((e) ->
clearTimeout(scrollTimer)
$('header').addClass('hidden')
scrollTimer = setTimeout((() ->
if $(this).scrollTop() is currentScrollTop
$('header').removeClass('hidden')
), animationDuration)
currentScrollTop = $(this).scrollTop()
)
please check the jquery mobile scrollstop event
$(document).on("scrollstop",function(){
alert("Stopped scrolling!");
});
For those Who Still Need This Here Is The Solution
$(function(){
var t;
document.addEventListener('scroll',function(e){
clearTimeout(t);
checkScroll();
});
function checkScroll(){
t = setTimeout(function(){
alert('Done Scrolling');
},500); /* You can increase or reduse timer */
}
});
This should work:
var Timer;
$('.Scroll_Table_Div').on("scroll",function()
{
// do somethings
clearTimeout(Timer);
Timer = setTimeout(function()
{
console.log('scrolling is stop');
},50);
});
Here is how you can handle this:
var scrollStop = function (callback) {
if (!callback || typeof callback !== 'function') return;
var isScrolling;
window.addEventListener('scroll', function (event) {
window.clearTimeout(isScrolling);
isScrolling = setTimeout(function() {
callback();
}, 66);
}, false);
};
scrollStop(function () {
console.log('Scrolling has stopped.');
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>
</body>
</html>