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

저는 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의 시작 또는 끝을지나 스크롤해도 뷰포트는 계속 이동합니다. 나는 그것을 비활성화하는 방법을 찾고 싶습니다.



답변

이렇게하면 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();
    }
});


답변

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


답변

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

$(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" />


답변

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

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


답변

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


답변

스크롤 할 수있는 요소가 위로 스크롤 할 때 이미 맨 위로 스크롤되었는지, 아래로 스크롤 할 때 맨 아래로 스크롤되었는지 확인한 다음 전체 페이지 이동을 중지하는 기본 동작을 방지합니다.

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


답변

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

스크롤하려는 팝업이나 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();
});