iPhone/iPad的JavaScript滚动事件?

115

我似乎无法捕获iPad上的滚动事件。 这些都不起作用,我做错了什么?

window.onscroll=myFunction;

document.onscroll=myFunction;

window.attachEvent("scroll",myFunction,false);

document.attachEvent("scroll",myFunction,false);

所有这些方法甚至在Windows上的Safari 3上都能工作。 具有讽刺意味的是,如果您不介意覆盖现有事件,PC上的每个浏览器都支持window.onload=。但在iPad上无法实现。

6个回答

156

iPhoneOS可以捕获onscroll事件,但不是你想象的那种方式。

单指滑动不会生成任何事件,直到用户停止滑动并重新绘制页面时才会生成onscroll事件,如图6-1所示。

同样地,使用两个手指滚动只有在停止滚动后才会触发onscroll

安装处理程序的常规方法如下:

window.addEventListener('scroll', function() { alert("Scrolled"); });
// or
$(window).scroll(function() { alert("Scrolled"); });
// or
window.onscroll = function() { alert("Scrolled"); };
// etc 

(另请参见https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)


25
谢谢,这迫使我进一步研究问题。实际答案是,在iPhone/iPad上检测视口顶部只能使用 window.pageYOffset ,而不能像其他所有浏览器/硬件一样使用 document.body.scrollTop || document.documentElement.scrollTop - ck_
6
@ck_ 很遗憾,在iPad上,“window.pageYOffset”在减速阶段不会更新,只有在滚动结束时才会更新。 - DATEx2
2
直接覆盖window.onscroll是一种不好的做法。更好的方法是添加事件监听器。使用window.addEventListener('scroll', function () { alert('scrolled!'); }); - Kevin Dice
4
嗯,实际上,现在我的 iPad 上无论何时滚动、拖动或减速时,window.onscroll 都会触发。是否有什么改变了?请问需要帮助吗? - commonpike
Safari(和其他一些浏览器)在iOS 8中改用WKWebView。但Chrome和Cordova仍然使用UIWebView,因此它们仍然存在持续滚动事件未发出的问题。不过,Cordova有一个WKWebView插件可供使用。 - jiku

114

在 iOS 上,你需要同时使用 touchmove 事件和 scroll 事件,代码如下:

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
}

40
该事件仅在用户主动滚动时触发,而在惯性滚动的减速阶段不会触发。 - Jon Tirsen
我修改了代码,加入了iOS的滚动结束检测代码。 - George Filippakos
您可能还需要添加一个“touchend”监听器,它将类似于桌面上的滚动。 - BingeBoy
如果您想立即获得更新,请在touchmove处理程序的末尾添加event.preventDefault()。但是,这样您就必须手动实现滚动,而不是让操作系统来处理。这很少是可取的。 - Tom Auger
1
请注意,这不会在滚动阶段提供访问权限。 - Ben Sewards
3
已经过去三年了,我无法在减速期间即时更新滚动位置。我该如何编写类似地图的应用程序? - FlavorScape

40

很抱歉在一个旧帖子中添加了另一个答案,但是我通常通过使用这段代码非常好地获得滚动事件(它至少在6.1上有效)

element.addEventListener('scroll', function() {
    console.log(this.scrollTop);
});

// This is the magic, this gives me "live" scroll events
element.addEventListener('gesturechange', function() {});

对我来说这是可行的。唯一无法实现的是提供滚动减速的滚动事件(一旦减速完成,您将获得最终滚动事件,请按照您的想法处理它。),但如果您使用CSS禁用惯性,可以通过执行以下操作来实现:

-webkit-overflow-scrolling: none;
您的元素不会获得惯性,但是对于页面主体,您可能需要执行经典操作。

您的元素不会获得惯性,但是对于页面主体,您可能需要执行经典操作。

document.addEventListener('touchmove', function(e) {e.preventDefault();}, true);

9
为什么要对一个正确的答案进行负评,而不是留下评论来解释其中的问题? - Dave Mackintosh
3
因为投票反对很容易且快捷。 :/ - Zeshan Ahmed
5
根据作者的意图,答案总是“有效”的...没有人会故意发布无效的答案。但是,他们应该解释为什么要给出反对意见。 - Matthieu Charbonnier
3
“gesturechange”事件是非标准的,仅被Safari浏览器支持:https://developer.mozilla.org/en-US/docs/Web/Events/gesturechange - Nicolas Bouvrette

6
我使用iScroll解决了这个问题,它具有动量滚动的感觉和其他功能。 https://github.com/cubiq/iscroll GitHub文档很好,我基本上遵循了它。以下是我的实现细节。
HTML: 我用一些div包装了可滚动区域的内容,以供iScroll使用:
<div id="wrapper">
  <div id="scroller">
    ... my scrollable content
  </div>
</div>

CSS: 我使用了 Modernizr 类中的 "touch" 以仅针对触摸设备进行样式更改(因为我只在触摸设备上实例化 iScroll)。

.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: 我从iScroll下载中包含了iscroll-probe.js,然后按照下面的方式初始化滚动条,其中updatePosition是我的函数,用于响应新的滚动位置。

# coffeescript
if Modernizr.touch
  myScroller = new IScroll('#wrapper', probeType: 3)
  myScroller.on 'scroll', updatePosition
  myScroller.on 'scrollEnd', updatePosition

现在必须使用myScroller来获取当前位置,而不是查看滚动偏移量。以下是从http://markdalgleish.com/presentations/embracingtouch/中提取的函数(一篇非常有帮助的文章,但现在有点过时了)

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}; 
}

唯一的问题是,有时我会失去我想要滚动到的页面的一部分,并且它会拒绝滚动。每当我更改#wrapper的内容时,我不得不添加一些调用myScroller.refresh()的代码,这样就解决了这个问题。
编辑:另一个问题是iScroll会吞掉所有"click"事件。我打开了选项,让iScroll发出"tap"事件并处理那些事件,而不是"click"事件。幸运的是,在滚动区域中我不需要太多点击,所以这不是什么大问题。

1

自从iOS 8发布以来,这个问题就不存在了。现在,在iOS Safari中,滚动事件可以平稳地触发。

因此,如果您注册了scroll事件处理程序并在该事件处理程序内检查window.pageYOffset,一切都能正常工作。


1
它确实存在于某些用例中,比如使用Cordova。https://developer.telerik.com/featured/scroll-event-change-ios-8-big-deal/ - Diosney
8
根据我的经验,截至2019年,当前iOS设备仍存在这个问题。 - Chris
仍然在运行iOS 15的iOS设备上遇到此问题。 - Philip

0

在 iOS 上进行了一些测试后,我发现这是适用于 iOS 和桌面端的最佳方案,如果您不担心桌面端的 120ms 延迟。非常完美地解决了问题。

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);
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接