2012-04-19 133 views
92

我正在研究基于iPad的网络应用程序,并且需要防止过度滚动,以使其看起来不像网页。我目前使用这种冻结视口并禁用反弹时:iOS Safari - 如何禁用overscroll,但允许可滚动的div正常滚动?

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

这个伟大的工程来禁用反弹时,但我的应用程序有几个滚动的div,并上面的代码防止它们滚动

我只针对iOS 5及以上版本,所以我避免了像iScroll这样的hacky解决方案。相反,我使用这个CSS我可滚动的div:

.scrollable { 
    -webkit-overflow-scrolling: touch; 
    overflow-y:auto; 
} 

此作品,未经文档反弹时剧本,但没有解决的div滚动问题。

没有一个jQuery插件,有没有办法使用反弹时修复,但免除我的$(“滚动”)的div?

编辑:

我发现的东西,是一个不错的解决方案:

// Disable overscroll/viewport moving on everything but scrollable divs 
$('body').on('touchmove', function (e) { 
     if (!$('.scrollable').has($(e.target)).length) e.preventDefault(); 
}); 

当你滚过div的开头或结尾的视域依然继续。我想找到一种方法来禁用它。

+0

尝试了你的最后一个也没有工作要么 – 2012-10-19 11:44:12

+0

我可以保持视口不移动,当你滚动过去的div的末尾,明确捕获滚动div的父级滚动事件,而不是让它实际上滚动。如果您使用jQuery Mobile的很有道理在页面级别,像这样做:。 $(“DIV [数据角色=‘页面’]”)上(“滚动”,功能(E){É .preventDefault();}); – 2013-02-18 20:47:35

+0

https://github.com/lazd/iNoBounce创建奇迹 – 2015-04-25 08:06:52

回答

7

您是否可以在您的overscroll禁用代码中添加更多逻辑以确保问题中的目标元素不是您想要滚动的元素?事情是这样的:

document.body.addEventListener('touchmove',function(e){ 
    if(!$(e.target).hasClass("scrollable")) { 
     e.preventDefault(); 
    } 
}); 
+3

谢谢...这似乎是_should_工作,但它不。另外,它不应该是“可滚动”,而不是“.scrollable”(点)? – Jeff 2012-05-08 19:18:29

+0

正确!修复它,谢谢。 – DeveloperJoe 2013-01-21 08:23:34

+1

它看起来像是最深的嵌套元素,接收触摸事件,所以你可能需要检查所有的父母,看看你是否在一个可滚动的div。 – 2013-02-18 05:02:57

5

检查是否想尝试向下滚动,然后阻止默认操作,以移动停止整个页面时可达滚动或底部时滚动的元素已经滚动到顶部。

var touchStartEvent; 
$('.scrollable').on({ 
    touchstart: function(e) { 
     touchStartEvent = e; 
    }, 
    touchmove: function(e) { 
     if ((e.originalEvent.pageY > touchStartEvent.originalEvent.pageY && this.scrollTop == 0) || 
      (e.originalEvent.pageY < touchStartEvent.originalEvent.pageY && this.scrollTop + this.offsetHeight >= this.scrollHeight)) 
      e.preventDefault(); 
    } 
}); 
+1

尝试了一个不工作 – 2012-10-19 11:40:40

+0

我不得不检查e.originalEvent.touches [0] .pageY而不是e.originalEvent.pageY。它的工作,但只有当你已经在滚动div的末尾。当滚动正在进行时(例如,您的滚动速度非常快),一旦到达可滚动div的末端,它就不会停止。 – Sashwat 2013-08-21 15:57:08

83

当你滚过DIV

var selScrollable = '.scrollable'; 
// Uses document because document will be topmost level in bubbling 
$(document).on('touchmove',function(e){ 
    e.preventDefault(); 
}); 
// Uses body because jQuery on events are called off of the element they are 
// added to, so bubbling would not work if we used document instead. 
$('body').on('touchstart', selScrollable, function(e) { 
    if (e.currentTarget.scrollTop === 0) { 
    e.currentTarget.scrollTop = 1; 
    } else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) { 
    e.currentTarget.scrollTop -= 1; 
    } 
}); 
// Stops preventDefault from being called on document if it sees a scrollable div 
$('body').on('touchmove', selScrollable, function(e) { 
    e.stopPropagation(); 
}); 

注意的开头或结尾,如果你想阻止整个页面滚动,当一个div没有这不会工作,这样就解决了问题溢出。要阻止,请使用以下事件处理程序,而不是一个正上方(改编自this question):

$('body').on('touchmove', selScrollable, function(e) { 
    // Only block default if internal div contents are large enough to scroll 
    // Warning: scrollHeight support is not universal. (https://stackoverflow.com/a/15033226/40352) 
    if($(this)[0].scrollHeight > $(this).innerHeight()) { 
     e.stopPropagation(); 
    } 
}); 
+0

如果可滚动区域内有iframe,并且用户开始在该iframe上滚动,则这不起作用。有没有解决方法? – Timo 2013-03-08 15:56:48

+2

工作得很好 - 这绝对比直接针对'.scrollable'更好(这是我最初尝试解决这个问题的方法)。 如果你是一个JavaScript的noob,并希望简单的代码删除这些处理程序的下面的处理程序,这两条线对我来说很好! '$(文件).off( 'touchmove');' 和 '$( '身体')关( 'touchmove touchstart', '.scrollable');' – Devin 2013-08-06 22:47:31

+0

它的工作完美的我。非常感谢,你救了我几个小时! – marcgg 2013-10-15 21:06:19

22

使用泰勒道奇的优秀answer一直落后于我的iPad,所以我加了一些限制代码,现在它的相当光滑。有时在滚动时有一些最小的跳过。

// Uses document because document will be topmost level in bubbling 
$(document).on('touchmove',function(e){ 
    e.preventDefault(); 
}); 

var scrolling = false; 

// Uses body because jquery on events are called off of the element they are 
// added to, so bubbling would not work if we used document instead. 
$('body').on('touchstart','.scrollable',function(e) { 

    // Only execute the below code once at a time 
    if (!scrolling) { 
     scrolling = true; 
     if (e.currentTarget.scrollTop === 0) { 
      e.currentTarget.scrollTop = 1; 
     } else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) { 
      e.currentTarget.scrollTop -= 1; 
     } 
     scrolling = false; 
    } 
}); 

// Prevents preventDefault from being called on document if it sees a scrollable div 
$('body').on('touchmove','.scrollable',function(e) { 
    e.stopPropagation(); 
}); 

而且,把下面的CSS修复了一些渲染毛刺(source):

.scrollable { 
    overflow: auto; 
    overflow-x: hidden; 
    -webkit-overflow-scrolling: touch; 
} 
.scrollable * { 
    -webkit-transform: translate3d(0,0,0); 
} 
+0

如果里面有一个iframe可滚动区域和用户开始滚动在该iframe。有没有一种解决方法? – Timo 2013-03-08 15:57:41

+1

似乎工作完美的向后拖,拖,但向下也将移动Safari浏览器。 – Abadaba 2013-04-11 15:26:15

+1

一个真棒解决方案......多谢:) – 2014-04-23 09:03:22

0

这里有一个仄兼容的解决方案

if (!$(e.target).hasClass('scrollable') && !$(e.target).closest('.scrollable').length > 0) { 
     console.log('prevented scroll'); 
     e.preventDefault(); 
     window.scroll(0,0); 
     return false; 
    } 
12

在整个文档像往常一样首先防止默认操作:

$(document).bind('touchmove', function(e){ 
    e.preventDefault();   
}); 

然后停止将您的元素类传播到文档级别。这将停止其到达上述因而函数e.preventDefault()不被启动:

$('.scrollable').bind('touchmove', function(e){ 
    e.stopPropagation(); 
}); 

该系统似乎是更加自然和比计算上的所有移动触摸屏类较不密集。动态生成的元素使用.on()而不是.bind()。

也可以考虑这些元标签,以防止不幸的事情,从您在使用滚动DIV发生:

<meta content='True' name='HandheldFriendly' /> 
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0' name='viewport' /> 
<meta name="viewport" content="width=device-width" /> 
3

我已经工作了一点workarround没有jQuery的。不perfert但工作正常(尤其是如果你有一个scoll-Y滚动-X)https://github.com/pinadesign/overscroll/

下跌免费参加,并提高其

+1

与杰夫有同样的问题,尝试每一个答案,你的工作。谢谢! – 2013-07-18 15:08:38

+0

只有当.scrollable div有足够的内容导致溢出时,接受的答案才适用于我。如果没有溢出,“反弹”效应仍然存在。然而,这完美的作品,谢谢! – 2013-10-14 15:11:56

4

我一直在寻找一种方式来阻止所有的身体滚动有一个时弹出一个可滚动区域(一个“购物车”弹出窗口,其中有一个可滚动的购物车视图)。

我写了一个非常优雅的解决方案,使用最小的javascript来切换类“noscroll”在你的身体上,当你想要滚动的弹出窗口或div(而不是“overscroll”整个页面主体) 。

虽然桌面浏览器观察到溢出:隐藏 - iOS似乎忽略了,除非您将位置设置为固定......这会导致整个页面变成一个奇怪的宽度,所以您必须手动设置位置和宽度为好。使用这个CSS:

.noscroll { 
    overflow: hidden; 
    position: fixed; 
    top: 0; 
    left: 0; 
    width: 100%; 
} 

与这个jQuery:

/* fade in/out cart popup, add/remove .noscroll from body */ 
$('a.cart').click(function() { 
    $('nav > ul.cart').fadeToggle(100, 'linear'); 
    if ($('nav > ul.cart').is(":visible")) { 
     $('body').toggleClass('noscroll'); 
    } else { 
     $('body').removeClass('noscroll'); 
    } 
}); 

/* close all popup menus when you click the page... */ 
$('body').click(function() { 
    $('nav > ul').fadeOut(100, 'linear'); 
    $('body').removeClass('noscroll'); 
}); 

/* ... but prevent clicks in the popup from closing the popup */ 
$('nav > ul').click(function(event){ 
    event.stopPropagation(); 
}); 
+0

这是非常有用的,并且是一种最小的方法,正是我所需要的。将位置设置为固定,顶部为0;左:0;宽度:100%;是我失踪的元素。这对于飞出菜单也很有用。 – bdanin 2015-04-07 12:24:11

0

这个工作对我来说(普通的JavaScript)

var fixScroll = function (className, border) { // className = class of scrollElement(s), border: borderTop + borderBottom, due to offsetHeight 
var reg = new RegExp(className,"i"); var off = +border + 1; 
function _testClass(e) { var o = e.target; while (!reg.test(o.className)) if (!o || o==document) return false; else o = o.parentNode; return o;} 
document.ontouchmove = function(e) { var o = _testClass(e); if (o) { e.stopPropagation(); if (o.scrollTop == 0) { o.scrollTop += 1; e.preventDefault();}}} 
document.ontouchstart = function(e) { var o = _testClass(e); if (o && o.scrollHeight >= o.scrollTop + o.offsetHeight - off) o.scrollTop -= off;} 
} 

fixScroll("fixscroll",2); // assuming I have a 1px border in my DIV 

HTML:

<div class="fixscroll" style="border:1px gray solid">content</div> 
1

这个解决方案没有按不要求你在你所有的scrollab上放置一个可滚动的类所以更为普遍。在INPUT元素contenteditables和overflow scroll或autos的所有元素或子元素上都允许滚动。

我使用自定义选择器,并且还将检查结果缓存在元素中以提高性能。无需每次都检查相同的元素。这可能有几个问题,因为只是写了,但我想分享。

$.expr[':'].scrollable = function(obj) { 
    var $el = $(obj); 
    var tagName = $el.prop("tagName"); 
    return (tagName !== 'BODY' && tagName !== 'HTML') && (tagName === 'INPUT' || $el.is("[contentEditable='true']") || $el.css("overflow").match(/auto|scroll/)); 
}; 
function preventBodyScroll() { 
    function isScrollAllowed($target) { 
     if ($target.data("isScrollAllowed") !== undefined) { 
      return $target.data("isScrollAllowed"); 
     } 
     var scrollAllowed = $target.closest(":scrollable").length > 0; 
     $target.data("isScrollAllowed",scrollAllowed); 
     return scrollAllowed; 
    } 
    $('body').bind('touchmove', function (ev) { 
     if (!isScrollAllowed($(ev.target))) { 
      ev.preventDefault(); 
     } 
    }); 
} 
0

试试这个它会工作完美。

$('body.overflow-hidden').delegate('#skrollr-body','touchmove',function(e){ 
    e.preventDefault(); 
    console.log('Stop skrollrbody'); 
}).delegate('.mfp-auto-cursor .mfp-content','touchmove',function(e){ 
    e.stopPropagation(); 
    console.log('Scroll scroll'); 
}); 
3

最好的解决办法是CSS/HTML: 让一个div为包装的元素,如果没有它已经 ,并将其设置位置予以确定和溢出隐藏。可选,设置宽度和高度为100%,如果你想让它填满整个屏幕并没有什么,但整个画面

#wrapper{ 
 
    height: 100%; 
 
    width: 100%; 
 
    position: fixed; 
 
    overflow: hidden; 
 
}
<div id="wrapper"> 
 
    <p>All</p> 
 
    <p>Your</p> 
 
    <p>Elements</p> 
 
</div>

+0

优秀。我把''wrapper''改成''html,body''。 – commonpike 2017-01-25 19:33:09

+0

这是个好主意 – 2017-02-05 19:48:36

1

虽然禁用所有“touchmove”事件似乎是个好主意,只要你需要页面上的其他可滚动元素,它会导致问题。最重要的是,如果您只在某些元素上禁用了“touchmove”事件(例如,如果您希望该页面不可滚动,则为body),只要在其他地方启用该事件,IOS就会在Chrome中导致无法阻止的传播酒吧切换。

虽然我无法解释这种行为,它看起来像防止的唯一途径似乎身体的位置设置为fixed。唯一的问题是你会失去文档的位置 - 例如,这对于模态尤其恼人。解决这个问题是使用这些简单的VanillaJS功能的一种方法:

function disableDocumentScrolling() { 
    if (document.documentElement.style.position != 'fixed') { 
     // Get the top vertical offset. 
     var topVerticalOffset = (typeof window.pageYOffset != 'undefined') ? 
      window.pageYOffset : (document.documentElement.scrollTop ? 
      document.documentElement.scrollTop : 0); 
     // Set the document to fixed position (this is the only way around IOS' overscroll "feature"). 
     document.documentElement.style.position = 'fixed'; 
     // Set back the offset position by user negative margin on the fixed document. 
     document.documentElement.style.marginTop = '-' + topVerticalOffset + 'px'; 
    } 
} 

function enableDocumentScrolling() { 
    if (document.documentElement.style.position == 'fixed') { 
     // Remove the fixed position on the document. 
     document.documentElement.style.position = null; 
     // Calculate back the original position of the non-fixed document. 
     var scrollPosition = -1 * parseFloat(document.documentElement.style.marginTop); 
     // Remove fixed document negative margin. 
     document.documentElement.style.marginTop = null; 
     // Scroll to the original position of the non-fixed document. 
     window.scrollTo(0, scrollPosition); 
    } 
} 

使用该解决方案,你可以有一个固定的文件,并在你的页面可以通过使用简单的CSS溢出任何其他元素(例如,overflow: scroll;)。无需特殊课程或其他任何东西。

0

我只好用简单的令人惊讶的运气:

body { 
    height: 100vh; 
} 

它的伟大工程,以禁用反弹时的弹出式窗口或菜单,并没有强制浏览器栏出现使用位置时,如:固定的。但是 - 您需要在设置固定高度之前保存滚动位置,并在隐藏弹出窗口时将其恢复,否则,浏览器将滚动到顶部。