javascript scroll event for iPhone/iPad?
Asked Answered
B

6

116

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.

Bunche answered 19/5, 2010 at 7:18 Comment(0)
S
156

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)

Sapphirine answered 19/5, 2010 at 7:25 Comment(5)
Thanks, that forced me to look further at the problem. The real answer was detecting the top of the viewport on the iphone/ipad only works with window.pageYOffset and not document.body.scrollTop||document.documentElement.scrollTop like every other browser/hardware in existence.Bunche
@Bunche Unfortunatelly on IPAD window.pageYOffset does not get updated in the deceleration phase, but only when scroll finishes.Broiler
It is bad practice to directly overwrite window.onscroll. Adding an event listener would be better. Use window.addEventListener('scroll', function (){ alert('scrolled!'); });Schmooze
ehm .. actually, window.onscroll is firing on my ipad all the time now, while panning, and after panning, while decelerating. did something change ?Policyholder
Safari (and maybe others) changed to using WKWebView with iOS 8. Chrome and Cordova still use UIWebView, so they still exhibit the issue with continuous scroll events not being issued. There's a WKWebView plug-in for Cordova though.Augmentative
S
114

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
}
Selway answered 20/3, 2012 at 12:49 Comment(6)
This event is only fired when the user is actively scrolling it is not fired during the deceleration phase of momentum scrolling.Retrenchment
i changed the code to include the end scroll detection code for iOSSelway
You might also want to add a listener for "touchend" which will act similar to scroll on desktop.Homologate
If you want to get an update right away, add event.preventDefault() to the end of your touchmove handler. But then you have to manually implement scrolling, rather than let the OS do it. Which is rarely recommendable.Scurf
Note that this does not provide access to the during scroll phaseCriminate
it's been three years. I can't get a realtime update of scroll position during deceleration. how am I supposed to write my maps-like app?????Buttercup
C
40

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);
Cuirbouilli answered 19/6, 2013 at 15:32 Comment(4)
Why downvote a valid answer and not leave a comment explaining the problem with it?Cuirbouilli
because downvoting is easy and quick. :/Auk
An answer is ALWAYS "valid" according to its author...Nobody posts a knowingly invalid answer. But yes, they should have explained why they downvoted.Strangeness
gesturechange events is non standard and only supported by Safari: developer.mozilla.org/en-US/docs/Web/Events/gesturechangeEndowment
M
6

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.

Mailable answered 17/4, 2014 at 18:5 Comment(0)
P
1

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.

Privett answered 24/8, 2018 at 11:52 Comment(3)
It does exists yet in some use cases like using Cordova. developer.telerik.com/featured/…Jamijamie
My experience is that this still remains a problem with current iOS devices in 2019.Breadfruit
Still experiencing this problem with iOS devices running iOS 15Harmattan
P
0

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);
});
Pseudaxis answered 16/8, 2022 at 9:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.