I'm looking to get an event to fire when one scrolls "up" from $(window).scrollTop == 0.
If you have the following code:
$(window).scroll(function(){
console.log("scrolling")
});
On a page where the document < window height then that event never fires because $(window).scrollTop isn't changing, but this doesn't mean that there's no mouse scroll input. I want an event to fire on mouse scroll regardless if the page is moving or not.
Seems like what you are looking for:
http://jsfiddle.net/n8eVQ/
$(document).on('mousewheel DOMMouseScroll MozMousePixelScroll', function(event, delta) {
console.log('mousewheel');
//you could trigger window scroll handler
$(window).triggerHandler('scroll');
});
Other way is to capture scroll event on modern browsers which support event capturing phase (IE>8). This can be used for any dynamic element. As jQuery doesn't implement capturing phase, you have to use javascript addEventListener() method. Here an example implementing logic to get scrolling direction for a textarea:
document.addEventListener('scroll', function (event) {
var $elm = $(event.target);
if ($elm.is('textarea')) { // or any other filtering condition
// do some stuff
var direction = $elm.scrollTop() > ($elm.data('scrollTop') || 0) ? "down" : "up";
$elm.data('scrollTop', $elm.scrollTop());
console.log('scrolling', direction);
}
}, true);
-DEMO-
document.addEventListener('DOMMouseScroll', callbackFunction, false);
Solution for firefox; for other browsers see #roasted solution
Related
in my js code I have pretty simple event listener listening for a click -
element.addeventlistener('click', ()=>{
#do somthing
})
the issue is that when I am scrolling on IOS (iphone) - touching this element to start the scroll, triggers the event listener.
Is there a way to prevent a event listener on iphone, unless no scrolling is to follow?
i.e. do something if clicked but scrolling doesn't follow
alternatively the might be a completely different solution (but I am trying to avoid a library)
thanks
W
ANSWER
After reviewing the answer given below (which does work) as described, this issue was still persisting. This gave me cause to review my CSS on which I found that on IOS mobile (iphone) - the CSS psudo selector :focus is activated when you scroll over an item.
I added a media query to only allow :focus to be used on desktop size devices or above, which solved the issue.
I have a found a possible solution on another question in Stackoverflow: Question
Basically you add a scroll listener to your window. This will then set a global boolean called disable_click_flag to true when scrolling. If the user wasn't scrolling for at least 250ms it will be set to false.
When the boolean is true then the click event isn't able to go trough.
var disable_click_flag = false;
var timeout = null;
window.addEventListener('scroll', () => {
disable_click_flag = true;
if(timeout) clearTimeout(timeout);
timeout = setTimeout(function(){ disable_click_flag = false }, 250);
}
element.addeventlistener('click', () => {
if(disable_click_flag === false{
#do somthing
}
})
I'm not an expert, but I would try:
var prevScroll = pageYOffset;
window.addEventListener("scroll", () => prevScroll = pageYOffset);
window.addEventListener("click", (e) => {
if (pageYOffset !== prevScroll) return;
// your code
});
Please note that this code is not tested, however I think it should work.
Hi guys I am using the scroll function on this script but it fires each time a user scrolls. I want it to only fire once when the user scrolls down to #ror. I tried using the fired variable to check if it has already been fired but that didn't seem to work. I know some people have answered this before but this is where i got the fired solution from and cant get it to work only once. Anyone think they can help please?
$( window ).scroll(function() {
var fired = 0;
console.log(fired);
if(fired == 0){
$('#ror').html('');
$('#ror').goalProgress({
goalAmount: 100,
currentAmount: 75,
textBefore: 'progress bar',
textAfter: '',
offset: 10,
});
fired=1;
}
});
You need to move the fired variable outside the scroll function.
As you are doing it now you are reinitializing the fired variable and setting it to 0 each time the scroll event gets fired.
var fired = 0;
$(window).scroll(function() {
console.log(fired);
if(fired == 0){
$('#ror').html('');
$('#ror').goalProgress({
goalAmount: 100,
currentAmount: 75,
textBefore: 'progress bar',
textAfter: '',
offset: 10,
});
fired=1;
}
});
To detect when a given #target scrolls into view, you can look at it's top position, and check if that position is already inside the viewport.
$('#target').offset().top - $(window).outerHeight() > $(window).scrollTop();
That left part of the equation is constant (as long as you don't move anything around, or change the size of the viewport). Therefore it may be wise to move that outside your event handler function. You need to keep in mind that the scroll event is rather expensive, since it fires constantly when you are scrolling, and the browser is already quite busy with the rendering of the viewport.
When the work is done, you can remove the event handler.
$(window).off(event);
So your final code would look something like this:
var triggerAtY = $('#target').offset().top - $(window).outerHeight();
$(window).scroll(function(event) {
// #target not yet in view
if (triggerAtY > $(window).scrollTop()) {
return;
}
// run your task
// remove this event handler
$(this).off(event);
});
Have a look at the demo: https://jsfiddle.net/6whnfa02/1/
Docs:
http://api.jquery.com/offset/
http://api.jquery.com/outerHeight/
http://api.jquery.com/scrollTop/
http://api.jquery.com/off/
$(window).scroll(function(event) {
var eT = $('#ror').offset().top,
wH = $(this).height(),
wSt = $(this).scrollTop();
if(wSt > (eT-wH)) {
alert('you have scrolled to the ror!');
//detach scroll event handler, as we dont want it to fire again
$(this).off(event);
}
}
The above code checks if user has scrolled down to an element. If yes, alert something and detach the scroll event handler for window. You can refer jquery documentation to see the meaning of offset, height and scrollTop.
Now, as #Pevera pointer out, it is costly to attach event handler to window scroll, you should be aware of that. If you have some heavy code execution inside scroll callback, it will hamper in scrolling the page. So, if you have to attach handler to window scroll, run the scroll callback code within a timeout callback. It ensures to run the scroll callback code after certain delay which helps to scroll the page better. Rewriting the above code with timeout will look like this:
var timeout = null;
$(window).scroll(function (event) {
if (!timeout) {
// set a timeout to run after 250ms
timeout = setTimeout(function () {
clearTimeout(timeout);
timeout = null;
var eT = $('#ror').offset().top,
wH = $(this).height(),
wSt = $(this).scrollTop();
if (wSt > (eT-wH)){
alert('you have scrolled to the ror!');
//detach scroll event handler, as we dont want it to fire again
$(this).off(event);
}
}, 250);
}
});
Everytime user scrolls the page, a timeout is set to run after(atleast) 250ms. In the timeout callback, we remove this timeout handler and check if user has scrolled to the element. If yes, we alert something and detach the scroll handler for window so that it doesn't run again.
Please refer to this FIDDLE for better understanding.
More info on this stackoverflow post and John Resig's blog post.
I have a functionality to implement based on scrolling.
Here is small snippet ::
$(window).bind('scroll',function(event)
{
console.log(event.type);
//Task to do
}
Here I want to differentiate that whether the binding is done by mouseScroll or by dragging a scroll.
By inspecting the event.type both are returning me "scroll" as event type.
You could use the wheel DOM event to detect mousewheel events:
var isMouseScroll = false;
window.addEventListener('wheel',function(e)
{
console.log('mouse wheel');
isMouseScroll = true;
});
window.addEventListener('scroll',function(e)
{
if(!isMouseScroll) {
console.log('scroll');
}
isMouseScroll = false;
});
JSFiddle
Note - don't confuse wheel with the deprecated, non-standard mousewheel event.
I have setup a slider, here is jsfiddler http://jsfiddle.net/zZv5B/. How can I enable it for touch devises, I want to be able to swipe through panel area to slide next and prev slide. any idea would be really appreciated.
var currentIndex = 0;// store current pane index displayed
var ePanes = $('#slider .panel');// store panes collection
function showPane(index){// generic showPane
// hide current pane
ePanes.eq(currentIndex).stop(true, true).fadeOut();
// set current index : check in panes collection length
currentIndex = index;
if(currentIndex < 0) currentIndex = ePanes.length-1;
else if(currentIndex >= ePanes.length) currentIndex = 0;
// display pane
ePanes.eq(currentIndex).stop(true, true).fadeIn();
// menu selection
$('.nav li').removeClass('current').eq(currentIndex).addClass('current');
}
// bind ul links
$('.nav li').click(function(ev){ showPane($(this).index());});
// bind previous & next links
$('.previous').click(function(){ showPane(currentIndex-1);});
$('.next').click(function(){ showPane(currentIndex+1);});
// apply start pane
showPane(0);
Use the events (more here) 'touchstart' and 'touchend' and get the start X position and the end x position. You can then compare the two and determine which direction the touch/swipe has happend.
var xStart, xEnd;
$('.wrap').on('mousedown touchstart', function (e) {
//get start x position
xStart = e.pageX;
}).on('mouseup touchend', function (e) {
//get the end x position
xEnd = e.originalEvent.pageX;
if (xStart != xEnd) {
//swiped
if (xStart < xEnd) {
console.log('Right');
showPane(currentIndex + 1);
}
if (xStart > xEnd) {
console.log('Left');
showPane(currentIndex - 1);
}
}
});
example fiddle - not sure how browser compatibile this is.
Or you could just use my fave touch enabled slider swipejs
UPDATE:
To make it work correctly for mobile changed xEnd = e.originalEvent.pageX as per #User543294's comment
Also a new fiddle example to using e.changedTouches[0].pageX as per MDN documetation
I just came across TouchSwipe. It seems pretty nice, straightforward, and robust.
Maybe something like this?
var swipe_obj = {
swipeLeft: function(event, direction, distance, duration, fingerCount) {
$(".next").click(); },
swipeRight: function(event, direction, distance, duration, fingerCount) {
$(".previous").click(); },
};
$(".nav li").swipe(swipe_obj);
Many touch devices also trigger a click, after a touch event. They usually follow a secuence similar to this: a touch happens, then it will be fired the events "touchstart" -> "touchend" -> "click". Where that last click it's a fake one, and has a huge delay respect native events. But it also works.
So, if efficiencie don't care, don't worry. Your slider will work as you expected it will do.
But if you are interested in to achieve a good performance, I recommend you to handle native events directly. Which can be done using Modernizr dinamic testing.
It's a simple idea:
1 Download and link the Modernizr library into your code.
2 Test at the first line of your code which kind of events you can expect, based on device capabilities. A single line of javascript will be enough:
var actualEvent = (Modernizr.touch) ? 'touchstart' : 'click';
Extra points if you do this before onReady occurs.
3 Move your current handlers into the "on" form:
$('selector').on(actualEvent, callback)
Then you will be always handling the real event, not the simulated click fired by tablets and phones. Because the test at the beginning ensures you that "actualEvent" will be "touchstart" just on touchable devices and "click" for the rest.
Of course, you can substitute "touchstar" by the touch event you want. But I think that "touchstar" is the one who best suits your needs.
How to implement a before start event to have a change to change the position and place in the DOM of the draggable element before jQueryUI start to drag?
You could extent prototype method:
SEE DEMO
var oldMouseStart = $.ui.draggable.prototype._mouseStart;
$.ui.draggable.prototype._mouseStart = function (event, overrideHandle, noActivation) {
this._trigger("beforeStart", event, this._uiHash());
oldMouseStart.apply(this, [event, overrideHandle, noActivation]);
};
$("#draggable").draggable({
beforeStart: function () {
console.log('beforeStart::');
}
});
I found that a method passed as the "helper" option to the sortable will get called before "start" and is passed (as the second argument) the item that has been clicked. You could do what you need to do in this method and then just return the element itself (the default "original" helper behavior). I'm using this to set the height of my container so that it doesn't collapse and trigger scrolling of the browser window. Here's what that looks like:
$(list).sortable({
helper: function(event, element) {
// it's too late if we wait until start to do this
$(this).css('height', this.$().height());
return element;
}
})
I didn't dare to access jQuery UI private variables, so I implemented it like this:
// The mouse interaction sequence for dragging starts with a mousedown action.
element.on('mousedown', function() {
// Mouseup cancels dragging. This is a boring click.
element.one('mouseup', function() {
element.off('mousemove.mynamespace');
});
// Moving the mouse while holding mousedown is dragging.
// This is also what sets off jQuery UI draggable,
// but we registered our event listeners first.
element.one('mousemove.mynamespace', function() {
// !! Your beforeStart code here.
});
});
// Initialize jQuery UI draggable AFTER our own event listeners.
element.draggable();
For that I used mouseup and mousedown:
var timeout;
$('.draggable').mousedown(function() {
$('#dragContainer').append($(this));
$(this).css({
top: 0,
left: 0
});
});
$('.draggable').draggable();
I also used mouseup to reset the old parent and position if the mousedown was actually a click and not a drag.
It would be nice to have a beforeStart event which work with the distance option but I didn't find it...