jQuery iOS Safari – 如何禁用过度滚动但允许可滚动的 div 正常滚动?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10238084/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
iOS Safari – How to disable overscroll but allow scrollable divs to scroll normally?
提问by Jeff
I'm working on an iPad-based web app, and need to prevent overscrolling so that it seems less like a web page. I'm currently using this to freeze the viewport and disable overscroll:
我正在开发基于 iPad 的网络应用程序,需要防止过度滚动,使其看起来不像网页。我目前正在使用它来冻结视口并禁用过度滚动:
document.body.addEventListener('touchmove',function(e){
e.preventDefault();
});
This works great to disable overscroll but my app has several scrollable divs, and the above code prevents them from scrolling.
这非常适合禁用过度滚动,但我的应用程序有几个可滚动的 div,并且上面的代码阻止它们滚动。
I'm targeting iOS 5 and above only so I've avoided hacky solutions like iScroll. Instead I'm using this CSS for my scrollable divs:
我只针对 iOS 5 及更高版本,所以我避免了像 iScroll 这样的 hacky 解决方案。相反,我将这个 CSS 用于我的可滚动 div:
.scrollable {
-webkit-overflow-scrolling: touch;
overflow-y:auto;
}
This works without the document overscroll script, but doesn't solve the div scrolling problem.
这在没有文档过度滚动脚本的情况下有效,但不能解决 div 滚动问题。
Without a jQuery plugin,is there any way to use the overscroll fix but exempt my $('.scrollable') divs?
如果没有 jQuery 插件,有什么方法可以使用 overscroll 修复但免除我的 $('.scrollable') div?
EDIT:
编辑:
I found something that's a decent solution:
我找到了一个不错的解决方案:
// Disable overscroll / viewport moving on everything but scrollable divs
$('body').on('touchmove', function (e) {
if (!$('.scrollable').has($(e.target)).length) e.preventDefault();
});
The viewport still moves when you scroll past the beginning or end of the div. I'd like to find a way to disable that as well.
当您滚动超过 div 的开头或结尾时,视口仍会移动。我也想找到一种方法来禁用它。
回答by Tyler Dodge
This solves the issue when you scroll past the beginning or end of the div
这解决了当您滚动超过 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();
});
Note that this won't work if you want to block whole page scrolling when a div does not have overflow. To block that, use the following event handler instead of the one immediately above (adapted from this question):
请注意,如果您想在 div 没有溢出时阻止整个页面滚动,这将不起作用。要阻止它,请使用以下事件处理程序而不是上面的事件处理程序(改编自此问题):
$('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();
}
});
回答by Kuba Holuj
Using Tyler Dodge's excellent answerkept lagging on my iPad, so I added some throttling code, now it's quite smooth. There is some minimal skipping sometimes while scrolling.
使用 Tyler Dodge 的出色答案在我的 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();
});
Also, adding the following CSS fixes some rendering glitches (source):
此外,添加以下 CSS 修复了一些渲染故障(源代码):
.scrollable {
overflow: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
.scrollable * {
-webkit-transform: translate3d(0,0,0);
}
回答by Jonathan Tonge
First prevent default actions on your entire document as usual:
首先像往常一样阻止对整个文档的默认操作:
$(document).bind('touchmove', function(e){
e.preventDefault();
});
Then stop your class of elements from propagating to the document level. This stops it from reaching the function above and thus e.preventDefault() is not initiated:
然后阻止您的元素类传播到文档级别。这会阻止它到达上面的函数,因此不会启动 e.preventDefault():
$('.scrollable').bind('touchmove', function(e){
e.stopPropagation();
});
This system seems to be more natural and less intensive than calculating the class on all touch moves. Use .on() rather than .bind() for dynamically generated elements.
这个系统似乎比计算所有触摸移动的类更自然,更不密集。对动态生成的元素使用 .on() 而不是 .bind() 。
Also consider these meta tags to prevent unfortunate things from happening while using your scrollable div:
还要考虑这些元标记,以防止在使用可滚动 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" />
回答by DeveloperJoe
Can you just add a little more logic into your overscroll disabling code to make sure the targeted element in question is not one that you would like to scroll? Something like this:
你能不能在你的过度滚动禁用代码中添加更多的逻辑,以确保有问题的目标元素不是你想要滚动的元素?像这样的东西:
document.body.addEventListener('touchmove',function(e){
if(!$(e.target).hasClass("scrollable")) {
e.preventDefault();
}
});
回答by Elias Fyksen
Best solution to this is css/html: Make a div to wrap your elements in, if you dont have it already And set it to position fixed and overflow hidden. Optional, set height and width to 100% if you want it to fill the whole screen and nothing but the whole screen
对此的最佳解决方案是 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>
回答by Johan
Check if the scrollable element is already scrolled to the top when trying to scroll up or to the bottom when trying to scroll down and then preventing the default action to stop the entire page from moving.
在尝试向上滚动时检查可滚动元素是否已经滚动到顶部或在尝试向下滚动时滚动到底部,然后阻止阻止整个页面移动的默认操作。
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();
}
});
回答by nrutas
I was looking for a way to prevent all body scrolling when there's a popup with a scrollable area (a "shopping cart" popdown that has a scrollable view of your cart).
当有一个带有可滚动区域的弹出窗口(一个具有购物车可滚动视图的“购物车”弹出窗口)时,我正在寻找一种方法来防止所有身体滚动。
I wrote a far more elegant solution using minimal javascript to just toggle the class "noscroll" on your body when you have a popup or div that you'd like to scroll (and not "overscroll" the whole page body).
我使用最少的 javascript 编写了一个更优雅的解决方案,当您有一个想要滚动的弹出窗口或 div(而不是“过度滚动”整个页面主体)时,只需在您的主体上切换类“noscroll”。
while desktop browsers observe overflow:hidden -- iOS seems to ignore that unless you set the position to fixed... which causes the whole page to be a strange width, so you have to set the position and width manually as well. use this css:
而桌面浏览器观察到溢出:隐藏——iOS 似乎忽略了这一点,除非你将位置设置为固定......这导致整个页面的宽度很奇怪,所以你也必须手动设置位置和宽度。使用这个CSS:
.noscroll {
overflow: hidden;
position: fixed;
top: 0;
left: 0;
width: 100%;
}
and this jquery:
和这个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();
});
回答by Pi?aDesign
I've work a little workarround without jquery. Not perfert but works fine (especially if you have a scroll-x in a scoll-y) https://github.com/pinadesign/overscroll/
我在没有 jquery 的情况下做了一些工作。不完美但工作正常(特别是如果你在 scoll-y 中有一个 scroll-x)https://github.com/pinadesign/overscroll/
Fell free to participate and improve it
自由参与并改进它
回答by Nicolas Bouvrette
While disabling all "touchmove" events might seem like a good idea, as soon as you need other scrollable elements on the page it will cause problems. On top of that, if you only disable "touchmove" events on certain elements (e.g. body if you want the page to be non-scrollable), as soon as it is enabled anywhere else, IOS will cause unstoppable propagation in Chrome when the URL bar toggles.
虽然禁用所有“touchmove”事件似乎是个好主意,但一旦您需要页面上的其他可滚动元素,它就会导致问题。最重要的是,如果您仅禁用某些元素上的“touchmove”事件(例如,如果您希望页面不可滚动,则一旦在其他任何地方启用它,IOS 将在 Chrome 中导致无法阻止的传播,当 URL栏切换。
While I cannot explain this behavior, it looks like the only way to prevent seems to set the body's position to fixed
. The only problem doing is that you will lose the position of the document - this is especially annoying in modals for example. One way to solve it would be to use these simple VanillaJS functions:
虽然我无法解释这种行为,但似乎唯一的预防方法似乎是将身体的位置设置为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);
}
}
Using this solution you can have a fixed document and any other element in your page can overflow by using simple CSS (e.g., overflow: scroll;
). No need for special classes or anything else.
使用此解决方案,您可以拥有一个固定的文档,并且页面中的任何其他元素都可以通过使用简单的 CSS(例如,overflow: scroll;
)而溢出。不需要特殊课程或其他任何东西。
回答by jcbdrn
This solution doesn't require you to put a scrollable class on all your scrollable divs so is more general. Scrolling is allowed on all elements which are, or are children of, INPUT elements contenteditables and overflow scroll or autos.
此解决方案不需要您在所有可滚动 div 上放置可滚动类,因此更通用。所有属于或属于 INPUT 元素 contenteditables 和溢出滚动或 autos 的元素都允许滚动。
I use a custom selector and I also cache the result of the check in the element to improve performance. No need to check the same element every time. This may have a few issues as only just written but thought I'd share.
我使用自定义选择器,并且我还在元素中缓存检查结果以提高性能。无需每次都检查相同的元素。这可能有一些问题,因为只是刚刚写的,但我想我会分享。
$.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();
}
});
}