Related
I want to make the sticky-nav to act similar(scroll is off when the menu is expanded) to this website's nav(http://amandagerhardsen.com/#cloudbusting/4) when expanded.
How do I do it?
var Boxlayout = (function () {
var $el = $('#sticky-nav'),
$sections = $el.children('section'),
// work panels
$workPanelsContainer = $('#bl-panel-work-items'),
// close work panel trigger
$closeWorkItem = $workPanelsContainer.find('nav > span.hidemenu'),
transEndEventNames = {
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition': 'transitionend',
'OTransition': 'oTransitionEnd',
'msTransition': 'MSTransitionEnd',
'transition': 'transitionend'
},
// transition end event name
transEndEventName = transEndEventNames[Modernizr.prefixed('transition')],
// support css transitions
supportTransitions = Modernizr.csstransitions;
function init() {
initEvents();
}
function initEvents() {
$sections.each(function () {
var $section = $(this);
// expand the clicked section and scale down the others
$section.on('click', function () {
if (!$section.data('open')) {
$section.data('open', true).addClass('bl-expand bl-expand-top');
$el.addClass('bl-expand-item');
}
}).find('span.hidemenu').on('click', function () {
// close the expanded section and scale up the others
$section.data('open', false).removeClass('bl-expand').on(transEndEventName, function (event) {
if (!$(event.target).is('section')) return false;
$(this).off(transEndEventName).removeClass('bl-expand-top');
});
if (!supportTransitions) {
$section.removeClass('bl-expand-top');
}
$el.removeClass('bl-expand-item');
return false;
});
});
// clicking on a work item: the current section scales down and the respective work panel slides up
$workItems.on('click', function (event) {
// scale down main section
$sectionWork.addClass('bl-scale-down');
// show panel for this work item
$workPanelsContainer.addClass('bl-panel-items-show');
var $panel = $workPanelsContainer.find("[data-panel='" + $(this).data('panel') + "']");
currentWorkPanel = $panel.index();
$panel.addClass('bl-show-work');
return false;
});
// navigating the work items: current work panel scales down and the next work panel slides up
$nextWorkItem.on('click', function (event) {
if (isAnimating) {
return false;
}
isAnimating = true;
var $currentPanel = $workPanels.eq(currentWorkPanel);
currentWorkPanel = currentWorkPanel < totalWorkPanels - 1 ? currentWorkPanel + 1 : 0;
var $nextPanel = $workPanels.eq(currentWorkPanel);
$currentPanel.removeClass('bl-show-work').addClass('bl-hide-current-work').on(transEndEventName, function (event) {
if (!$(event.target).is('div')) return false;
$(this).off(transEndEventName).removeClass('bl-hide-current-work');
isAnimating = false;
});
if (!supportTransitions) {
$currentPanel.removeClass('bl-hide-current-work');
isAnimating = false;
}
$nextPanel.addClass('bl-show-work');
return false;
});
// clicking the work panels close button: the current work panel slides down and the section scales up again
$closeWorkItem.on('click', function (event) {
// scale up main section
$sectionWork.removeClass('bl-scale-down');
$workPanelsContainer.removeClass('bl-panel-items-show');
$workPanels.eq(currentWorkPanel).removeClass('bl-show-work');
return false;
});
}
return {
init: init
};
})();
Here is a fiddle: http://jsfiddle.net/77P2e/
Be careful to unlock scrolling again when done, or this could be very annoying for the user!
Setup code
var $window = $(window), previousScrollTop = 0, scrollLock = false;
$window.scroll(function(event) {
if(scrollLock) {
$window.scrollTop(previousScrollTop);
}
previousScrollTop = $window.scrollTop();
});
To lock scroll position:
scrollLock = true;
And to unlock again...
scrollLock = false;
As an example use, you could lock the window scroll position when the mouse enters the navigation area, and unlock it again when the mouse leaves:
$("nav")
.mouseenter(function(){ scrollLock = true; })
.mouseleave(function(){ scrollLock = false; });
In my opinion the accepted answer is not what should be achieved, as the window.scroll() function will be still running (endlessly), even if the 'event' has occured.
The window.scroll() function is an event handler. So use on() to bind the event and off() to unbind it (after the 'event' has occured).
$(window).on('scroll', function() { // bind event handler
var offset = $(window).scrollTop();
console.log("page Y-Offset: ", offset); // just to see it working
if(offset >= 100) $(window).off('scroll'); // unbind the event handler when the condition is met
});
The Javascript solution is a little janky for me, on mobile. It's like it scrolls a little bit and then snaps back into place.
However, I figured out a way to do it much more cleanly, without any jank, just by changing CSS's overflow property on the part you don't want to scroll. Here's the code in d3 but the concept should be pretty clear:
var body = d3.select('body');
var preventScroll = function () {
body.style('overflow', 'hidden');
},
allowScroll = function () {
body.style('overflow', 'scroll');
};
d3.select('#sticky-nav')
.on('touchmove', preventScroll)
.on('touchstart', preventScroll)
.on('touchend', allowScroll)
.on('touchcancel', allowScroll);
As I was using jquery animation,
if ($(window).scrollTop() >= $('.btn').offset().top + $('.btn').outerHeight() - window.innerHeight)
{
$(".tab").stop();
}
I did this and it worked.
.btn is the button. That .tab div would stop if it scrolls to that position.
If you're using jquery animation you can try using the stop() function on the animated object.
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");
}
}
I'd like to set something up on my site where when you scroll within 15% of the bottom of the page an element flyouts from the side... I'm not sure how to get started here... should I add a listener for a scroll function or something?
I'm trying to recreate the effect at the bottom of this page: http://www.nytimes.com/2011/01/25/world/europe/25moscow.html?_r=1
update
I have this code....
console.log(document.body.scrollTop); //shows 0
console.log(document.body.scrollHeight * 0.85); //shows 1038.7
if (document.body.scrollTop > document.body.scrollHeight * 0.85) {
console.log();
$('#flyout').animate({
right: '0'
},
5000,
function() {
});
}
the console.log() values aren't changing when I scroll to the bottom of the page. The page is twice as long as my viewport.
[Working Demo]
$(document).ready(function () {
var ROOT = (function () {
var html = document.documentElement;
var htmlScrollTop = html.scrollTop++;
var root = html.scrollTop == htmlScrollTop + 1 ? html : document.body;
html.scrollTop = htmlScrollTop;
return root;
})();
// may be recalculated on resize
var limit = (document.body.scrollHeight - $(window).height()) * 0.85;
var visible = false;
var last = +new Date;
$(window).scroll(function () {
if (+new Date - last > 30) { // more than 30 ms elapsed
if (visible && ROOT.scrollTop < limit) {
setTimeout(function () { hide(); visible = false; }, 1);
} else if (!visible && ROOT.scrollTop > limit) {
setTimeout(function () { show(); visible = true; }, 1);
}
last = +new Date;
}
});
});
I know this is an old topic, but the above code that received the check mark was also triggering the $(window).scroll() event listener too many times.
I guess twitter had this same issue at one point. John Resig blogged about it here: http://ejohn.org/blog/learning-from-twitter/
$(document).ready(function(){
var ROOT = (function () {
var html = document.documentElement;
var htmlScrollTop = html.scrollTop++;
var root = html.scrollTop == htmlScrollTop + 1 ? html : document.body;
html.scrollTop = htmlScrollTop;
return root;
})();
// may be recalculated on resize
var limit = (document.body.scrollHeight - $(window).height()) * 0.85;
var visible = false;
var last = +new Date;
var didScroll = false;
$(window).scroll(function(){
didScroll = true;
})
setInterval(function(){
if(didScroll){
didScroll = false;
if (visible && ROOT.scrollTop < limit) {
hideCredit();
visible = false;
} else if (!visible && ROOT.scrollTop > limit) {
showCredit();
visible = true;
}
}
}, 30);
function hideCredit(){
console.log('The hideCredit function has been called.');
}
function showCredit(){
console.log('The showCredit function has been called.');
}
});
So the difference between the two blocks of code is when and how the timer is called. In this code the timer is called off the bat. So every 30 millaseconds, it checks to see if the page has been scrolled. if it's been scrolled, then it checks to see if we've passed the point on the page where we want to show the hidden content. Then, if that checks true, the actual function then gets called to show the content. (In my case I've just got a console.log print out in there right now.
This seems to be better to me than the other solution because the final function only gets called once per iteration. With the other solution, the final function was being called between 4 and 5 times. That's got to be saving resources. But maybe I'm missing something.
bad idea to capture the scroll event, best to use a timer and every few milliseconds check the scroll position and if in the range you need then execute the necessary code for what you need
Update: in the past few years the best practice is to subscribe to the event and use a throttle avoiding excessive processing https://lodash.com/docs#throttle
Something like this should work:
$(window).scroll(function() {
if (document.body.scrollTop > document.body.scrollHeight * 0.85) {
// flyout
}
});
document.body.scrollTop may not work equally well on all browsers (it actually depends on browser and doctype); so we need to abstract that in a function.
Also, we need to flyout only one time. So we can unbind the event handler after having flyed out.
And we don't want the flyout effect to slow down scrolling, so we will run our flytout function out of the event loop (by using setTimeout()).
Here is the final code:
// we bind the scroll event, with the 'flyout' namespace
// so we can unbind easily
$(window).bind('scroll.flyout', (function() {
// this function is defined only once
// it is private to our event handler
function getScrollTop() {
// if one of these values evaluates to false, this picks the other
return (document.documentElement.scrollTop||document.body.scrollTop);
}
// this is the actual event handler
// it has the getScrollTop() in its scope
return function() {
if (getScrollTop() > (document.body.scrollHeight-$(window).height()) * 0.85) {
// flyout
// out of the event loop
setTimeout(function() {
alert('flyout!');
}, 1);
// unbind the event handler
// so that it's not call anymore
$(this).unbind('scroll.flyout');
}
};
})());
So in the end, only getScrollTop() > document.body.scrollHeight * 0.85 is executed at each scroll event, which is acceptable.
The flyout effect is ran only one time, and after the event has returned, so it won't affect scrolling.
Hey all, I need a jQuery action to fire when a user scrolls past certain locations on the page. Is this even possible with jQuery? I have looked at .scroll in the jQuery API and I don't think this is what I need. It fires every time the user scrolls, but I need it to fire just when a user passes a certain area.
Use the jquery event .scroll()
$(window).on('scroll', function() {
var y_scroll_pos = window.pageYOffset;
var scroll_pos_test = 150; // set to whatever you want it to be
if(y_scroll_pos > scroll_pos_test) {
//do stuff
}
});
http://jsfiddle.net/babumxx/hpXL4/
Waypoints in jQuery should do it:
http://imakewebthings.github.com/jquery-waypoints/
$('#my-el').waypoint(function(direction) {
console.log('Reached ' + this.element.id + ' from ' + direction + ' direction.');
});
jQuery waypoints plugin documentation: http://imakewebthings.com/waypoints/guides/jquery-zepto/
To fire any action only once on a single page I have modified jondavid's snippet as following.
jQuery(document).ready(function($){
$triggered_times = 0;
$(window).on('scroll', function() {
var y_scroll_pos = window.pageYOffset;
var scroll_pos_test = 150; // set to whatever you want it to be
if(y_scroll_pos > scroll_pos_test && $triggered_times == 0 ) {
//do your stuff over here
$triggered_times = 1; // to make sure the above action triggers only once
}
});
})
Scroll down to Run code snippet
Here you can check example of working code snippet;
jQuery(document).ready(function($){
$triggered_times = 0;
$(window).on('scroll', function() {
var y_scroll_pos = window.pageYOffset;
var scroll_pos_test = 150; // set to whatever you want it to be
if(y_scroll_pos > scroll_pos_test && $triggered_times == 0 ) {
alert('This alert is triggered after you have scroll down to 150px')
$triggered_times = 1; // to make sure the above action triggers only once
}
});
})
p {
height: 1000px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<p>scroll down this block to get an alert</p>
</body>
Is it possible to implement "long press" in JavaScript (or jQuery)? How?
(source: androinica.com)
HTML
Long press
JavaScript
$("a").mouseup(function(){
// Clear timeout
return false;
}).mousedown(function(){
// Set timeout
return false;
});
There is no 'jQuery' magic, just JavaScript timers.
var pressTimer;
$("a").mouseup(function(){
clearTimeout(pressTimer);
// Clear timeout
return false;
}).mousedown(function(){
// Set timeout
pressTimer = window.setTimeout(function() { ... Your Code ...},1000);
return false;
});
Based on Maycow Moura's answer, I wrote this. It also ensures that the user didn't do a right click, which would trigger a long press and works on mobile devices. DEMO
var node = document.getElementsByTagName("p")[0];
var longpress = false;
var presstimer = null;
var longtarget = null;
var cancel = function(e) {
if (presstimer !== null) {
clearTimeout(presstimer);
presstimer = null;
}
this.classList.remove("longpress");
};
var click = function(e) {
if (presstimer !== null) {
clearTimeout(presstimer);
presstimer = null;
}
this.classList.remove("longpress");
if (longpress) {
return false;
}
alert("press");
};
var start = function(e) {
console.log(e);
if (e.type === "click" && e.button !== 0) {
return;
}
longpress = false;
this.classList.add("longpress");
if (presstimer === null) {
presstimer = setTimeout(function() {
alert("long click");
longpress = true;
}, 1000);
}
return false;
};
node.addEventListener("mousedown", start);
node.addEventListener("touchstart", start);
node.addEventListener("click", click);
node.addEventListener("mouseout", cancel);
node.addEventListener("touchend", cancel);
node.addEventListener("touchleave", cancel);
node.addEventListener("touchcancel", cancel);
You should also include some indicator using CSS animations:
p {
background: red;
padding: 100px;
}
.longpress {
-webkit-animation: 1s longpress;
animation: 1s longpress;
}
#-webkit-keyframes longpress {
0%, 20% { background: red; }
100% { background: yellow; }
}
#keyframes longpress {
0%, 20% { background: red; }
100% { background: yellow; }
}
You can use taphold event of jQuery mobile API.
jQuery("a").on("taphold", function( event ) { ... } )
I created long-press-event (0.5k pure JS) to solve this, it adds a long-press event to the DOM.
Listen for a long-press on any element:
// the event bubbles, so you can listen at the root level
document.addEventListener('long-press', function(e) {
console.log(e.target);
});
Listen for a long-press on a specific element:
// get the element
var el = document.getElementById('idOfElement');
// add a long-press event listener
el.addEventListener('long-press', function(e) {
// stop the event from bubbling up
e.preventDefault()
console.log(e.target);
});
Works in IE9+, Chrome, Firefox, Safari & hybrid mobile apps (Cordova & Ionic on iOS/Android)
Demo
While it does look simple enough to implement on your own with a timeout and a couple of mouse event handlers, it gets a bit more complicated when you consider cases like click-drag-release, supporting both press and long-press on the same element, and working with touch devices like the iPad. I ended up using the longclick jQuery plugin (Github), which takes care of that stuff for me. If you only need to support touchscreen devices like mobile phones, you might also try the jQuery Mobile taphold event.
For modern, mobile browsers:
document.addEventListener('contextmenu', callback);
https://developer.mozilla.org/en-US/docs/Web/Events/contextmenu
jQuery plugin. Just put $(expression).longClick(function() { <your code here> });. Second parameter is hold duration; default timeout is 500 ms.
(function($) {
$.fn.longClick = function(callback, timeout) {
var timer;
timeout = timeout || 500;
$(this).mousedown(function() {
timer = setTimeout(function() { callback(); }, timeout);
return false;
});
$(document).mouseup(function() {
clearTimeout(timer);
return false;
});
};
})(jQuery);
$(document).ready(function () {
var longpress = false;
$("button").on('click', function () {
(longpress) ? alert("Long Press") : alert("Short Press");
});
var startTime, endTime;
$("button").on('mousedown', function () {
startTime = new Date().getTime();
});
$("button").on('mouseup', function () {
endTime = new Date().getTime();
longpress = (endTime - startTime < 500) ? false : true;
});
});
DEMO
For cross platform developers (Note All answers given so far will not work on iOS):
Mouseup/down seemed to work okay on android - but not all devices ie (samsung tab4). Did not work at all on iOS.
Further research its seems that this is due to the element having selection and the native magnification interupts the listener.
This event listener enables a thumbnail image to be opened in a bootstrap modal, if the user holds the image for 500ms.
It uses a responsive image class therefore showing a larger version of the image.
This piece of code has been fully tested upon (iPad/Tab4/TabA/Galaxy4):
var pressTimer;
$(".thumbnail").on('touchend', function (e) {
clearTimeout(pressTimer);
}).on('touchstart', function (e) {
var target = $(e.currentTarget);
var imagePath = target.find('img').attr('src');
var title = target.find('.myCaption:visible').first().text();
$('#dds-modal-title').text(title);
$('#dds-modal-img').attr('src', imagePath);
// Set timeout
pressTimer = window.setTimeout(function () {
$('#dds-modal').modal('show');
}, 500)
});
The Diodeus's answer is awesome, but it prevent you to add a onClick function, it'll never run hold function if you put an onclick. And the Razzak's answer is almost perfect, but it run hold function only on mouseup, and generally, the function runs even if user keep holding.
So, I joined both, and made this:
$(element).on('click', function () {
if(longpress) { // if detect hold, stop onclick function
return false;
};
});
$(element).on('mousedown', function () {
longpress = false; //longpress is false initially
pressTimer = window.setTimeout(function(){
// your code here
longpress = true; //if run hold function, longpress is true
},1000)
});
$(element).on('mouseup', function () {
clearTimeout(pressTimer); //clear time on mouseup
});
You could set the timeout for that element on mouse down and clear it on mouse up:
$("a").mousedown(function() {
// set timeout for this element
var timeout = window.setTimeout(function() { /* … */ }, 1234);
$(this).mouseup(function() {
// clear timeout for this element
window.clearTimeout(timeout);
// reset mouse up event handler
$(this).unbind("mouseup");
return false;
});
return false;
});
With this each element gets its own timeout.
This worked for me:
const a = document.querySelector('a');
a.oncontextmenu = function() {
console.log('south north');
};
https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/oncontextmenu
You can use jquery-mobile's taphold. Include the jquery-mobile.js and the following code will work fine
$(document).on("pagecreate","#pagename",function(){
$("p").on("taphold",function(){
$(this).hide(); //your code
});
});
Most elegant and clean is a jQuery plugin:
https://github.com/untill/jquery.longclick/,
also available as packacke:
https://www.npmjs.com/package/jquery.longclick.
In short, you use it like so:
$( 'button').mayTriggerLongClicks().on( 'longClick', function() { your code here } );
The advantage of this plugin is that, in contrast to some of the other answers here, click events are still possible. Note also that a long click occurs, just like a long tap on a device, before mouseup. So, that's a feature.
I needed something for longpress keyboard events, so I wrote this.
var longpressKeys = [13];
var longpressTimeout = 1500;
var longpressActive = false;
var longpressFunc = null;
document.addEventListener('keydown', function(e) {
if (longpressFunc == null && longpressKeys.indexOf(e.keyCode) > -1) {
longpressFunc = setTimeout(function() {
console.log('longpress triggered');
longpressActive = true;
}, longpressTimeout);
// any key not defined as a longpress
} else if (longpressKeys.indexOf(e.keyCode) == -1) {
console.log('shortpress triggered');
}
});
document.addEventListener('keyup', function(e) {
clearTimeout(longpressFunc);
longpressFunc = null;
// longpress key triggered as a shortpress
if (!longpressActive && longpressKeys.indexOf(e.keyCode) > -1) {
console.log('shortpress triggered');
}
longpressActive = false;
});
In vanila JS if need to detect long-click after click released:
document.addEventListener("mousedown", longClickHandler, true);
document.addEventListener("mouseup", longClickHandler, true);
let startClick = 0;
function longClickHandler(e){
if(e.type == "mousedown"){
startClick = e.timeStamp;
}
else if(e.type == "mouseup" && startClick > 0){
if(e.timeStamp - startClick > 500){ // 0.5 secound
console.log("Long click !!!");
}
}
}
May need to use timer if need to check long-click while clicking. But for most case after release click is enought.
For me it's work with that code (with jQuery):
var int = null,
fired = false;
var longclickFilm = function($t) {
$body.css('background', 'red');
},
clickFilm = function($t) {
$t = $t.clone(false, false);
var $to = $('footer > div:first');
$to.find('.empty').remove();
$t.appendTo($to);
},
touchStartFilm = function(event) {
event.preventDefault();
fired = false;
int = setTimeout(function($t) {
longclickFilm($t);
fired = true;
}, 2000, $(this)); // 2 sec for long click ?
return false;
},
touchEndFilm = function(event) {
event.preventDefault();
clearTimeout(int);
if (fired) return false;
else clickFilm($(this));
return false;
};
$('ul#thelist .thumbBox')
.live('mousedown touchstart', touchStartFilm)
.live('mouseup touchend touchcancel', touchEndFilm);
You can check the time to identify Click or Long Press [jQuery]
function AddButtonEventListener() {
try {
var mousedowntime;
var presstime;
$("button[id$='" + buttonID + "']").mousedown(function() {
var d = new Date();
mousedowntime = d.getTime();
});
$("button[id$='" + buttonID + "']").mouseup(function() {
var d = new Date();
presstime = d.getTime() - mousedowntime;
if (presstime > 999/*You can decide the time*/) {
//Do_Action_Long_Press_Event();
}
else {
//Do_Action_Click_Event();
}
});
}
catch (err) {
alert(err.message);
}
}
You can use jquery Touch events. (see here)
let holdBtn = $('#holdBtn')
let holdDuration = 1000
let holdTimer
holdBtn.on('touchend', function () {
// finish hold
});
holdBtn.on('touchstart', function () {
// start hold
holdTimer = setTimeout(function() {
//action after certain time of hold
}, holdDuration );
});
like this?
target.addEeventListener("touchstart", function(){
// your code ...
}, false);