iOS Safari – 오버 스크롤을 비활성화하고 스크롤 가능한 div가 정상적으로 스크롤되도록 허용하는 방법은 무엇입니까?


100

저는 iPad 기반 웹 앱에서 작업 중이며 웹 페이지처럼 보이지 않도록 오버 스크롤을 방지해야합니다. 나는 현재 이것을 사용하여 뷰포트를 고정하고 오버 스크롤을 비활성화합니다.

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

이것은 오버 스크롤을 비활성화하는 데 효과적이지만 내 앱에는 스크롤 가능한 div가 여러 개 있으며 위의 코드는 스크롤을 방지합니다 .

저는 iOS 5 이상 만 대상으로하고 있으므로 iScroll과 같은 해키 솔루션을 피했습니다. 대신 스크롤 가능한 div에이 CSS를 사용하고 있습니다.

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

이것은 문서 오버 스크롤 스크립트없이 작동하지만 div 스크롤 문제를 해결하지 못합니다.

jQuery 플러그인이 없으면 오버 스크롤 수정을 사용하지만 $ ( '. scrollable') div를 제외하는 방법이 있습니까?

편집하다:

괜찮은 해결책을 찾았습니다.

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

div의 시작 또는 끝을지나 스크롤해도 뷰포트는 계속 이동합니다. 나는 그것을 비활성화하는 방법을 찾고 싶습니다.


시도 중 당신도 마지막 하나를 제외한 didnt 한 일
산티아고 스톨은

스크롤 가능한 div의 부모에서 스크롤 이벤트를 명시 적으로 캡처하고 실제로 스크롤하지 못하도록하여 div의 끝을지나 스크롤 할 때 뷰포트가 움직이지 않도록 할 수있었습니다. jquery 모바일을 사용하는 경우 다음과 같이 페이지 수준에서이 작업을 수행하는 것이 좋습니다. $ ( 'div [data-role = "page"]'). on ( 'scroll', function (e) {e.preventDefault ();});
Christopher Johnson


이 문제를 해결하는이 스크립트를 찾았습니다! :) github.com/lazd/iNoBounce
Jan Šafránek

게시물 위에 누군가가 게시물을 7 개월 전에 게시했다면 왜 링크를 다시 게시합니까?
Denny

답변:


84

이렇게하면 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에 오버플로가 없을 때 전체 페이지 스크롤을 차단하려는 경우에는 작동하지 않습니다. 이를 차단하려면 바로 위의 이벤트 처리기 대신 다음 이벤트 처리기를 사용하십시오 ( 이 질문 에서 수정 됨 ).

$('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();
    }
});

스크롤 가능 영역 내에 iframe이 있고 사용자가 해당 iframe에서 스크롤을 시작하면 작동하지 않습니다. 이에 대한 해결 방법이 있습니까?
Timo 2013 년

2
훌륭하게 작동했습니다. 이것은 단순히 .scrollable직접 타겟팅하는 것보다 확실히 낫습니다 (원래이 문제를 해결하기 위해 시도한 것입니다). 만약 당신이 자바 스크립트 멍청이이고이 핸들러를 제거하는 쉬운 코드를 원한다면,이 두 줄은 저에게 아주 좋습니다! $(document).off('touchmove'); 그리고 $('body').off('touchmove touchstart', '.scrollable');
Devin

그것은 나를 위해 완벽하게 작동했습니다. 고마워요, 당신은 저에게 시간을 절약했습니다!
marcgg

1
div에 스크롤 할 콘텐츠가 충분하지 않으면 작동하지 않습니다. : 누군가가 여기에 대답 별도의 질문을했다 stackoverflow.com/q/16437182/40352
크리스

두 개 이상의 ".scrollable"클래스를 허용하려면 어떻게해야합니까? 하나와 잘 작동하지만 다른 div도 스크롤 가능하게 만들어야합니다. 감사!
MeV

23

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

또한 다음 CSS를 추가하면 일부 렌더링 결함이 수정됩니다 ( source ).

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

스크롤 가능 영역 내에 iframe이 있고 사용자가 해당 iframe에서 스크롤을 시작하면 작동하지 않습니다. 이에 대한 해결 방법이 있습니까?
Timo

1
뒤로 드래그하는 데 완벽하게 작동하는 것처럼 보이지만 아래로 드래그하면 여전히 사파리가 이동합니다.
Abadaba 2013

1
멋진 해결책은 ... 고마워요 :)
아미르 샤

이것은 나를 위해 일했습니다. 감사! 이 문제를 해결하기 위해 1.5 일 이상을 보냅니다.
Achintha Samindika 2014-06-05

이것은 굉장하고 훌륭하게 작동했으며 해결책을 찾는 데 더 많은 스트레스를 덜어주었습니다. Kuba 감사합니다!
Leonard

12

먼저 평소와 같이 전체 문서에 대한 기본 작업을 방지합니다.

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

그런 다음 요소 클래스가 문서 레벨로 전파되는 것을 중지하십시오. 이렇게하면 위의 함수에 도달하지 못하므로 e.preventDefault ()가 시작되지 않습니다.

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

이 시스템은 모든 터치 동작에 대한 클래스를 계산하는 것보다 더 자연스럽고 덜 집중적으로 보입니다. 동적으로 생성 된 요소에는 .bind () 대신 .on ()을 사용하십시오.

또한 스크롤 가능한 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" />

7

오버 스크롤 비활성화 코드에 논리를 조금 더 추가하여 문제의 대상 요소가 스크롤하려는 요소가 아닌지 확인할 수 있습니까? 이 같은:

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

3
고마워요 ...이게 효과 가있는 것 같지만 그렇지 않습니다. 또한 ".scrollable"(점 포함)이 아니라 "scrollable"이어야하지 않습니까?
Jeff

1
터치 이벤트를받는 가장 깊이 중첩 된 요소 인 것 같으므로 모든 부모를 확인하여 스크롤 가능한 div에 있는지 확인해야 할 수 있습니다.
Christopher Johnson

3
jQuery를 사용하면 왜 document.body.addEventListener를 사용합니까? 이유가 있습니까?
fnagel 2014-07-10

7

이것에 대한 최선의 해결책은 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>


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

e.originalEvent.pageY 대신 e.originalEvent.touches [0] .pageY를 ​​확인해야했습니다. 작동했지만 이미 스크롤 div의 끝에있는 경우에만 가능합니다. 스크롤이 진행 중일 때 (예 : 정말 빠르게 스크롤 한 경우) 스크롤 가능한 div의 끝에 도달하면 중지되지 않습니다.
Keen Sage

4

스크롤 가능한 영역이있는 팝업 (카트의 스크롤 가능한보기가있는 "쇼핑 카트"팝업)이있을 때 모든 본문 스크롤을 방지하는 방법을 찾고있었습니다.

스크롤하려는 팝업이나 div가있을 때 (전체 페이지 본문을 "오버 스크롤"하지 않음) 바디에서 "noscroll"클래스를 전환하기 위해 최소한의 자바 스크립트를 사용하여 훨씬 더 우아한 솔루션을 작성했습니다.

데스크톱 브라우저가 overflow : hidden을 관찰하는 동안-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();
});

이것은 매우 유용하고 최소한의 접근 방식입니다. 위치를 고정으로 설정, top : 0; 왼쪽 : 0; 폭 : 100 %; 내가 놓친 요소였다. 이것은 플라이 아웃 메뉴에도 유용합니다.
bdanin 2015-04-07

3

jquery없이 약간의 해결 방법을 사용했습니다. perfert는 아니지만 잘 작동합니다 (특히 scoll-y에 scroll-x가있는 경우) https://github.com/pinadesign/overscroll/

자유롭게 참여하고 개선하십시오.


1
Jeff와 동일한 문제가 있었고 모든 답변을 시도했으며 귀하의 답변이 작동했습니다. 감사합니다!
Dominik Schreiber

허용 된 대답은 .scrollable이있는 div에 오버플로를 일으킬 수있는 충분한 내용이있을 때만 저에게 효과적이었습니다. 넘치지 않으면 '바운스'효과가 여전히 존재합니다. 그러나 이것은 완벽하게 작동합니다. 감사합니다!
Adam Marshall

1

이 솔루션은 스크롤 가능한 모든 div에 스크롤 가능한 클래스를 넣을 필요가 없으므로 더 일반적입니다. INPUT 요소 contenteditables 및 오버플로 스크롤 또는 자동 인 모든 요소에 대해 스크롤이 허용됩니다.

사용자 지정 선택기를 사용하고 성능을 향상시키기 위해 요소의 검사 결과도 캐시합니다. 매번 동일한 요소를 확인할 필요가 없습니다. 이것은 단지 쓰여졌지만 공유 할 것이라고 생각한 몇 가지 문제가있을 수 있습니다.

$.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();
        }
    });
}

1

모든 "touchmove"이벤트를 비활성화하는 것이 좋은 생각처럼 보일 수 있지만 페이지에서 다른 스크롤 가능 요소가 필요한 즉시 문제가 발생합니다. 또한 특정 요소 (예 : 페이지를 스크롤 할 수 없도록하려는 경우 본문)에서 "touchmove"이벤트 만 비활성화하는 경우 다른 곳에서 활성화하자마자 IOS는 URL이 다음과 같은 경우 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

다음은 zepto 호환 솔루션입니다.

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

0

이것은 나를 위해 작동합니다 (일반 자바 스크립트)

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>

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

0

나는 단순함으로 놀라운 행운을 얻었습니다.

body {
    height: 100vh;
}

팝업 또는 메뉴에 대한 오버 스크롤을 비활성화하는 데 효과적이며 position : fixed를 사용할 때 브라우저 표시 줄이 강제로 표시되지 않습니다. 그러나-고정 높이를 설정하기 전에 스크롤 위치를 저장하고 팝업을 숨길 때 복원해야합니다. 그렇지 않으면 브라우저가 맨 위로 스크롤됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.