Safari and $(window).on('scroll') - javascript

It seems like in Safari ( ver: 9.1 ), neither jQuery nor vanilla JavaScript catches the scroll event.
My goal is to catch the scroll position and if it is below 50px, fix the nav bar by adding a class, and otherwise remove that class.
The code snippet I placed below works in Chrome, Firefox and IE but not in Safari.
$(window).on('scroll', function(){
if( $(window).scrollTop()>50 ){
$('.navbar').addClass('nav-fixed');
}
else {
$('.navbar').removeClass('nav-fixed');
}
});

I'm experiencing the same thing here in Safari v. 10.0.1.
window.addEventListener('scroll',function() {
// Nothing happens here in Safari. Scroll event is never fired.
});
EDIT: I had an issue with my CSS that fixed the problem. I had some old stuff for scroll snap points (which I don't need) that I forgot to delete from my CSS. On a div surrounding my content I had
height: 100vh;
So it makes sense that the scroll event on the window would not be fired, because I would be scrolling inside my div instead.

Related

Disable scroll but still track when user attempts to scroll

Quick disclaimer–I know there are similar questions but they don't provide the answer I'm looking for.
I'm trying to disable scrolling entirely when the page loads but still be able to listen to when user attempts to scroll. The goal is to trigger an animation function when user attempts to scroll and once the animation completes, the scroll would be re-enabled back to it's normal state.
I've tried to disable scroll and play my animation after user tries to scroll like this:
function blogHeaderMagic() {
//disable scroll function
disableScroll()
//my animation and on a callback you'll see I call allowScroll function to allow scroll normally once animation completes
TweenMax.to("#post-hero-media-wrapper", 1, {height:54, onComplete: allowScroll });
scrolled = true
}
document.onscroll = function() {
if( scrolled == false){
blogHeaderMagic();
}
}
And while this works great, in Chrome or Safari, it isn't such a smooth effect because when user first attempts to scroll, scroll is enabled so they can scroll like 100px from the top and only then scroll locks. This is why I would first like to disable the scroll and when user attempts to scroll (although they won't be able to) I would like to detect their attempt to scroll and trigger my animation function on that detection. Is this possible?
Answer
You could set the body tag to overflow:hidden; which won't allow the user to scroll and use these event handler, then put back the overflow property to whatever it was (probably auto if you didn't changed it in the first place).
// IE9, Chrome, Safari, Opera
document.body.addEventListener("mousewheel", MouseWheelHandler, false);
// Firefox
document.body.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
function MouseWheelHandler() {
alert('scrolling with the mouse');
document.body.style.overflow = 'auto'
document.body.removeEventListener("mousewheel", MouseWheelHandler, false);
document.body.removeEventListener("DOMMouseScroll", MouseWheelHandler, false);
}
Interesting links
I did a quick codepen example. See here
Also this article
Edit----------------
Also found this Stack Overflow question
I dont think that there is a pure JS way of disabling all scroll while still detecting scroll events from the user (just reread your question) that's why I think the overflow solution is the simplest/most elegant solution (that I could come up with).
You could always detect scroll the set the scroll position to the top with something like window.scroll(0,0) and/or window.scrollTo(0,0). From what I have tested it doesn't seem to work quite well.

window.scrollBy fires before hash/anchor scroll in Chrome and Safari

Here's a frustrating problem. I use the following in script inside of a jQuery load block:
window.scrollBy(0,-100);
I do it because I set a div to be fixed at the top of the page through scrolling, and this line will compensate so that the anchor you've clicked to (http://page.html#foo) is seen where it should be.
It works great in Firefox. In Chrome and Safari, it doesn't, because the load event appears to happen before the browser scrolls to the anchor.
Any suggestions?
I came across into the same problem, and this is my work out. (A hack actually)
// Clicking on the navigation bar
$('.navbar-nav a').click(function(e) {
// Blocking default clicking event
e.preventDefault();
// Scroll to the anchored element with a delay
setTimeout(function() {$('#talks')[0].scrollIntoView();}, 5);
// Compensate the scrolling with another delay
setTimeout(function() {scrollBy(0, -$('#info').height())}, 10);
}
It seems like it's a Safari bug.
I also came across this problem. Using a timeout, even 0 ms, seems to work without visible jumping. Try this:
window.setTimeout()
{
window.scrollBy(0, -100);
}, 0);

prevent app dragging but permit scroll of inner div elements in ios

I have a web app that I am adapting to iOS5 using Phonegap. Everything works except for one issue:
My inner <div>s, which are set to scroll if overflowed and scroll perfectly if overflowed in Chrome do not scroll at all on the iPad.
--I have disabled app dragging by disabling touchmove;
--i have implemented (perhaps incorrectly?) the -webkit-overflow-scrolling:touch CSS property that is, apparently, new to iOS5 like so:
overflow: scroll;
-webkit-overflow-scrolling:touch;
Nothing seems to work.
If I comment out the javascript (from Phonegap) that disables app dragging (ie. touchmove), then the scrolling works but it drags and scrolls the entire app.
Any help would be appreciated.
This is more of a hack, but you can do some checking to see if you're touching the div or not, and depending on that, you prevent scrolling like so:
document.body.addEventListener('touchmove', function (e){
if(document.elementFromPoint(e.pageX, e.pageY) !== document.getElementById('yourdiv'){
e.preventDefault();
}
}, false);
MDN reference
I was able to prevent dragging of the main app/page by using the following code:
var myDiv = document.getElementById('idMyDiv');
myDiv.addEventListener('touchmove', function (e){
e.preventDefault();
}, false);

scrollTop() returns 0 in chrome

I want to check if page is scrolled after it has finished loading and I'm using this code:
$(document).ready(function(){
alert($(window).scrollTop());
});
It works well in Firefox but allways returns 0 in Chrome. Why is this?
Actually Firefox is the only browser that doesn't return 0 for $(window).scrollTop() on domReady or window.onload. Chrome, Safari and IE all return 0. The only safe way to get correct position of scrollbar on domReady is, as mentioned in another answer above, to set an event handler on window's scroll event as below:
$(document).ready(function(){
$(window).scroll(function() {
console.log($(window).scrollTop());
$(window).unbind('scroll');
});
});
$(window).scrollTop() will return 0 when the window isn't scrollable.
The chrome restores the pre-refresh scroll position once the DOM is loaded. So, getting scrollTop on scroll event instead of ready event will work.
I also had the problem that scrollTop() always returned 0 in Chrome, whether I used it on window, on document or on 'html,body'. I finally found out that css was the problem:
html, body {
height: 100%;
overflow-x: hidden;
}
Actually, I don't know exactly why because you can remove parts of this code and then it works again. Strange but problem solved ;)
Try the following code in a page that has some text and a link in the bottom of the page. Remember to have enough text or blank lines in order to make a scroll of the page until you can see the my button
$(document).ready(function () {
alert($(window).scrollTop()); // first load if you did not scroll = 0
$("#button").click(function () { alert($(window).scrollTop()); });
// hitting the button that is located on bottom of page
// - i had to scroll the page = xxx (68 in may case)
});
It is normal in your case to get 0 because the page is not scrolled, the offset between your position and the top of the page is 0.
I tried in Chrome, FF6, IE9.
I hope i was helpful.
I had the same problem but got fixed by adding the document declaration:
<!DOCTYPE html>

javascript scroll event for iPhone/iPad?

I can't seem to capture the scroll event on an iPad.
None of these work, what I am doing wrong?
window.onscroll=myFunction;
document.onscroll=myFunction;
window.attachEvent("scroll",myFunction,false);
document.attachEvent("scroll",myFunction,false);
They all work even on Safari 3 on Windows.
Ironically, EVERY browser on the PC supports window.onload= if you don't mind clobbering existing events. But no go on iPad.
The iPhoneOS does capture onscroll events, except not the way you may expect.
One-finger panning doesn’t generate any events until the user stops panning—an onscroll event is generated when the page stops moving and redraws—as shown in Figure 6-1.
Similarly, scroll with 2 fingers fires onscroll only after you've stopped scrolling.
The usual way of installing the handler works e.g.
window.addEventListener('scroll', function() { alert("Scrolled"); });
// or
$(window).scroll(function() { alert("Scrolled"); });
// or
window.onscroll = function() { alert("Scrolled"); };
// etc
(See also https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)
For iOS you need to use the touchmove event as well as the scroll event like this:
document.addEventListener("touchmove", ScrollStart, false);
document.addEventListener("scroll", Scroll, false);
function ScrollStart() {
//start of scroll event for iOS
}
function Scroll() {
//end of scroll event for iOS
//and
//start/end of scroll event for other browsers
}
Sorry for adding another answer to an old post but I usually get a scroll event very well by using this code (it works at least on 6.1)
element.addEventListener('scroll', function() {
console.log(this.scrollTop);
});
// This is the magic, this gives me "live" scroll events
element.addEventListener('gesturechange', function() {});
And that works for me. Only thing it doesn't do is give a scroll event for the deceleration of the scroll (Once the deceleration is complete you get a final scroll event, do as you will with it.) but if you disable inertia with css by doing this
-webkit-overflow-scrolling: none;
You don't get inertia on your elements, for the body though you might have to do the classic
document.addEventListener('touchmove', function(e) {e.preventDefault();}, true);
I was able to get a great solution to this problem with iScroll, with the feel of momentum scrolling and everything https://github.com/cubiq/iscroll The github doc is great, and I mostly followed it. Here's the details of my implementation.
HTML:
I wrapped the scrollable area of my content in some divs that iScroll can use:
<div id="wrapper">
<div id="scroller">
... my scrollable content
</div>
</div>
CSS:
I used the Modernizr class for "touch" to target my style changes only to touch devices (because I only instantiated iScroll on touch).
.touch #wrapper {
position: absolute;
z-index: 1;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: hidden;
}
.touch #scroller {
position: absolute;
z-index: 1;
width: 100%;
}
JS:
I included iscroll-probe.js from the iScroll download, and then initialized the scroller as below, where updatePosition is my function that reacts to the new scroll position.
# coffeescript
if Modernizr.touch
myScroller = new IScroll('#wrapper', probeType: 3)
myScroller.on 'scroll', updatePosition
myScroller.on 'scrollEnd', updatePosition
You have to use myScroller to get the current position now, instead of looking at the scroll offset. Here is a function taken from http://markdalgleish.com/presentations/embracingtouch/ (a super helpful article, but a little out of date now)
function getScroll(elem, iscroll) {
var x, y;
if (Modernizr.touch && iscroll) {
x = iscroll.x * -1;
y = iscroll.y * -1;
} else {
x = elem.scrollTop;
y = elem.scrollLeft;
}
return {x: x, y: y};
}
The only other gotcha was occasionally I would lose part of my page that I was trying to scroll to, and it would refuse to scroll. I had to add in some calls to myScroller.refresh() whenever I changed the contents of the #wrapper, and that solved the problem.
EDIT: Another gotcha was that iScroll eats all the "click" events. I turned on the option to have iScroll emit a "tap" event and handled those instead of "click" events. Thankfully I didn't need much clicking in the scroll area, so this wasn't a big deal.
Since iOS 8 came out, this problem does not exist any more. The scroll event is now fired smoothly in iOS Safari as well.
So, if you register the scroll event handler and check window.pageYOffset inside that event handler, everything works just fine.
After some testing on the ios, I found that this is the way to go for ios and desktop, if you are not worried of that delay of 120ms on desktop. Works like a charm.
let isScrolling;
document.addEventListener("scroll", () => {
// Clear our timeout throughout the scroll
window.clearTimeout( isScrolling );
// Set a timeout to run after scrolling ends
isScrolling = setTimeout(function() {
// Run the callback
console.log( 'Scrolling has stopped.' );
}, 120);
});

Categories