Как отключить резиновую ленту в веб-приложениях iOS?
Это:
$('body').on('touchmove', function(e) { e.preventDefault(); });
Работает, но отключит прокрутку по всей странице, что далеко не идеально.
Это:
$('*').on('touchstart', function(e){
var element = $(this).get(0);
if ( element.scrollTop <= 0 ) element.scrollTop = 1;
if ( element.scrollTop + element.offsetHeight >= element.scrollHeight ) element.scrollTop = element.scrollHeight - element.offsetHeight - 1;
});
Работает на страницах с областью прокрутки. Однако, когда нет ничего, чтобы прокрутить, он снова покажет резиновую полосу.
Итак, мой вопрос:
Как вы можете отключить эффект резиновой ленты и по-прежнему удерживать области -webkit-overflow-scrolling
прокручиваемыми?
[Обновление]
Лучшее решение
Отключить прокрутку всех незаметных элементов, таких как панель вкладок или панель навигации.
anElement.addEventListener('touchmove', function( event ){ event.preventDefault() };
Прикрепите обработчик прокрутки к прокручиваемым элементам, таким как основной контент.
anElement.addEventListener('touchstart', function( event ){
if( this.scrollTop === 0 ) {
this.scrollTop += 1;
} else if( this.scrollTop + this.offsetHeight >= this.scrollHeight ) {
this.scrollTop -= 1;
}
}
Ответы
Ответ 1
Раньше в тот же самый выпуск недавно был SPA, где резисторная лента <body>
отвлекала от опыта, но мне нужна прокрутка в подзонах. Большое спасибо dSquared предложениям, поскольку метод 1 работал лучше всего для меня. Вот мое небольшое расширение его предложения, которое я реализовал в проекте для работы, который просматривает все дерево, чтобы найти какие-либо элементы (а не только div), которые имеют класс .scroll
на нем:
// Prevent rubber-banding of the body, but allow for scrolling elements
$('body').on('touchmove', function (e) {
var searchTerms = '.scroll, .scroll-y, .scroll-x',
$target = $(e.target),
parents = $target.parents(searchTerms);
if (parents.length || $target.hasClass(searchTerms)) {
// ignore as we want the scroll to happen
// (This is where we may need to check if at limit)
} else {
e.preventDefault();
}
});
И вот как выглядит CSS:
body {
height: 100%;
overflow: hidden;
}
.scroll, .scroll-y, .scroll-x {
-webkit-overflow-scrolling: touch;
}
.scroll > *, .scroll-y > *, .scroll-x > * {
-webkit-transform : translateZ(0);
}
.scroll { overflow: auto; }
.scroll-y { overflow-y: auto; }
.scroll-x { overflow-x: auto; }
Вам нужна только одна библиотека (jQuery или Zepto), и вы получаете естественную прокрутку с импульсом и без резинки на теле. Кроме того, я добавил translateZ, чтобы исправить некоторые проблемы, которые у меня были с элементами, исчезающими во время прокрутки, и его можно использовать для GPU ускоряет ваши элементы,
НО (и это большой, но), как указывает dSquared, все резиновые полосы страницы, когда элемент прокрутки находится на своем пределе и попытался прокрутить дальше. Лично я считаю, что это неудача, поэтому я продолжаю работать над этим, просто хочу вмешаться, пытаясь понять это. Добавление проверки вдоль строк кода OP может быть ответом, но я не пробовал его.
ОБНОВЛЕНИЕ (10/7/12):
После многих работ я получил следующий код, отлично работающий в iOS6 (нигде не тестировался). Никакой резиновой ленты на теле, никаких проблем, когда на пределе области прокрутки, и у нее есть прокручивающая производительность на всем протяжении. Очевидно, это намного больше кода, но я думаю, что это даст поведение, наиболее близкое к целям OP.
(function registerScrolling($) {
var prevTouchPosition = {},
scrollYClass = 'scroll-y',
scrollXClass = 'scroll-x',
searchTerms = '.' + scrollYClass + ', .' + scrollXClass;
$('body').on('touchstart', function (e) {
var $scroll = $(e.target).closest(searchTerms),
targetTouch = e.originalEvent.targetTouches[0];
// Store previous touch position if within a scroll element
prevTouchPosition = $scroll.length ? { x: targetTouch.pageX, y: targetTouch.pageY } : {};
});
$('body').on('touchmove', function (e) {
var $scroll = $(e.target).closest(searchTerms),
targetTouch = e.originalEvent.targetTouches[0];
if (prevTouchPosition && $scroll.length) {
// Set move helper and update previous touch position
var move = {
x: targetTouch.pageX - prevTouchPosition.x,
y: targetTouch.pageY - prevTouchPosition.y
};
prevTouchPosition = { x: targetTouch.pageX, y: targetTouch.pageY };
// Check for scroll-y or scroll-x classes
if ($scroll.hasClass(scrollYClass)) {
var scrollHeight = $scroll[0].scrollHeight,
outerHeight = $scroll.outerHeight(),
atUpperLimit = ($scroll.scrollTop() === 0),
atLowerLimit = (scrollHeight - $scroll.scrollTop() === outerHeight);
if (scrollHeight > outerHeight) {
// If at either limit move 1px away to allow normal scroll behavior on future moves,
// but stop propagation on this move to remove limit behavior bubbling up to body
if (move.y > 0 && atUpperLimit) {
$scroll.scrollTop(1);
e.stopPropagation();
} else if (move.y < 0 && atLowerLimit) {
$scroll.scrollTop($scroll.scrollTop() - 1);
e.stopPropagation();
}
// If only moving right or left, prevent bad scroll.
if(Math.abs(move.x) > 0 && Math.abs(move.y) < 3){
e.preventDefault()
}
// Normal scrolling behavior passes through
} else {
// No scrolling / adjustment when there is nothing to scroll
e.preventDefault();
}
} else if ($scroll.hasClass(scrollXClass)) {
var scrollWidth = $scroll[0].scrollWidth,
outerWidth = $scroll.outerWidth(),
atLeftLimit = $scroll.scrollLeft() === 0,
atRightLimit = scrollWidth - $scroll.scrollLeft() === outerWidth;
if (scrollWidth > outerWidth) {
if (move.x > 0 && atLeftLimit) {
$scroll.scrollLeft(1);
e.stopPropagation();
} else if (move.x < 0 && atRightLimit) {
$scroll.scrollLeft($scroll.scrollLeft() - 1);
e.stopPropagation();
}
// If only moving up or down, prevent bad scroll.
if(Math.abs(move.y) > 0 && Math.abs(move.x) < 3){
e.preventDefault();
}
// Normal scrolling behavior passes through
} else {
// No scrolling / adjustment when there is nothing to scroll
e.preventDefault();
}
}
} else {
// Prevent scrolling on non-scrolling elements
e.preventDefault();
}
});
})(jQuery);
Ответ 2
К сожалению, для этого не существует исправления "волшебной пули", поскольку прокрутка резиновой полосы на Mobile Safari является встроенной "функцией" самого браузера. Используя любой механизм прокрутки по умолчанию, предоставляемый браузером, вы в конечном итоге получите прокрутку резиновой ленты.
Есть два способа предложить следующее:
Метод 1
Привяжите к событию touchmove
в элементе </body>
и проверьте цель события touchmove
, чтобы увидеть, хотите ли вы его запустить или нет:
HTML
<div class="scroll">
<p>...</p>
<p>...</p>
</div>
JS
$('body').on('touchmove', function(e) {
// this is the node the touchmove event fired on
// in this example it would be the </p> element
target = e.target;
// we need to find the parent container
// we get it like so; assumes div as parent
parent = $(e.target).closest('div');
// check if the parent is a scroll window by class //
if ($(parent).hasClass('scroll')){
// ignore as we want the scroll to happen
} else {
e.preventDefault();
}
});
Пример JSFiddle здесь
Этот метод использует прокрутку по умолчанию браузера по умолчанию, однако у него есть недостаток, что у вас все еще будет прокрутка резиновой ленты, когда наверху или внизу прокрутки </div>
.
Метод 2
Привязать к событию touchmove
элемента </body>
, как и раньше, однако в этом случае мы предотвращаем события Все touchmove
и полагаемся на отличный iScroll 4 для обработки прокрутки, например:
HTML
<div id="wrapper">
<div id="scroller">
<p>...</p>
<p>...</p>
</div>
</div>
JS
$(document).ready(function(){
// prevent all scroll //
$('body').on('touchmove', function(e) {
e.preventDefault();
});
// apply iscroll to scrolling element
// requires use of id
var newscroll = new iScroll('wrapper');
});
Пример JSFiddle здесь
Это мой предпочтительный метод, поскольку он блокирует прокрутку всех резиновых полос и обеспечивает приятную область прокрутки, однако он полагается на использование плагина.
Я надеюсь, что это поможет
Ответ 3
Здесь используется решение, использующее jQuery и Hammer.js(jquery-реализация). Эти две библиотеки, но если вы работаете на мобильном телефоне, скорее всего, вы захотите включить Hammer.
Для каждого события перетаскивания, которое пузырится вверх (так что без прокрутки drag-взаимодействий можно использовать stopPropagation), обработчик проверяет, пузырился ли он через какие-либо элементы с прокруткой class=, если да, будет ли пользователь прокручиваться в пределах разрешенных границ этого scrollContainer, и только тогда он разрешает естественную прокрутку.
$("body").hammer().on('drag swipe', function(e){
var scrollTarget = $(e.gesture.target).closest(".scrollable");
if(scrollTarget.length)
{
var scrollTopMax = scrollTarget[0].scrollHeight - scrollTarget.outerHeight();
if(scrollTopMax > 0){
var scrollTop = scrollTarget.scrollTop();
if(scrollTop > 0 && scrollTop < scrollTopMax){
//console.log("scrolling in the middle");
}
else if(scrollTop <= 0 && e.gesture.deltaY < 0){
//console.log("scrolling from top");
}
else if(scrollTop >= scrollTopMax && e.gesture.deltaY > 0){
//console.log("scrolling from bottom");
}
else{
//console.log("trying to scroll out of boundaries");
e.gesture.preventDefault();
}
}
else{
//console.log("content to short to scroll");
e.gesture.preventDefault();
}
}
else{
//console.log("no containing element with class=scrollable");
e.gesture.preventDefault();
}
});
Убить перетаскивание с помощью пинча и т.д.; если необходимо, чтобы масштабирование было масштабируемым пользователем
$("body").hammer().on('doubletap rotate pinch', function(e){
e.gesture.preventDefault();
});
Протестировано на ios7/safari, android4.3/webview и android4.3/firefoxMobile25 и единственное решение, которое не сломалось.
Ответ 4
Я написал, на мой взгляд, лучшее решение этой проблемы. Он вообще отключит прокрутку, если элемент не прокручивается.
/********************************************************************************
* Disable rubber band (c)2013 - Mark van Wijnen | www.CrystalMinds.nl
********************************************************************************/
$(function(){
var scrollY = 0;
$(document).on('touchstart', function( e ){
scrollY = e.originalEvent.touches.item(0).clientY;
});
$(document).on('touchmove', function( e ){
var scrollPos = e.target.scrollTop;
var scrollDelta = scrollY - e.originalEvent.touches.item(0).clientY;
var scrollBottom = scrollPos + $(e.target).height();
scrollY = e.originalEvent.touches.item(0).clientY;
if ( $(e.target).css( 'overflow-y' ) != 'scroll' || ( scrollDelta < 0 && scrollPos == 0 ) || ( scrollDelta > 0 && scrollBottom == e.target.scrollHeight ) )
e.preventDefault();
});
});
Ответ 5
Основываясь на ответе @Mark, мы придумали эту альтернативу, которая, похоже, работает. Замените .page_list
именами классов прокручиваемых элементов.
var INITIAL_Y = 0; // Tracks initial Y position, needed to kill Safari bounce effect
function kill_safari_bounce() {
$( document ).on( 'touchstart', function( e ){
INITIAL_Y = e.originalEvent.touches[0].clientY;
});
$( document ).on( 'touchmove', function( e ) {
// Get scrollable ancestor if one exists
var scrollable_ancestor = $( e.target ).closest( '.page_list' )[0];
// Nothing scrollable? Block move.
if ( !scrollable_ancestor ) {
e.preventDefault();
return;
}
// If here, prevent move if at scrollable boundaries.
var scroll_delta = INITIAL_Y - e.originalEvent.touches[0].clientY;
var scroll_pos = scrollable_ancestor.scrollTop;
var at_bottom = (scroll_pos + $(scrollable_ancestor).height()) == scrollable_ancestor.scrollHeight;
if ( (scroll_delta < 0 && scroll_pos == 0) ||
(scroll_delta > 0 && at_bottom) ){
e.preventDefault();
}
});
}
Ответ 6
Наконец, я использовал несколько методов, и эти коды являются рабочей версией.
Но вы должны включить hammer.js
CSS
.scrollable{
overflow:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;
*{-webkit-transform:translate3d(0,0,0);}
}
JAVASCRIPT
$(document).on("touchmove",function(e){
e.preventDefault();
});
$("body").on("touchstart",".scrollable",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;
}
});
$("body").on("touchmove",".scrollable",function(e){
e.stopPropagation();
});
$("body").hammer().on("pinch",function(e){
e.gesture.preventDefault();
});
Ответ 7
Просто добавьте -webkit-overflow-scrolling: auto;
в div, который вы хотите предотвратить от прыгания