CSS scroll 막기 - CSS scroll maggi

CSS scroll 막기 - CSS scroll maggi
모바일 레이어 팝업 부모창 스크롤 막기

CSS 속성 중에 overflow 는 컨텐츠의 크기가 요소의 크기보다 커서 넘칠 때 어떻게 보여줄지 지정하는 속성이다.

이를 이용하여 body 태그에 width: 100%; height: 100%; overflow: hidden을 주게 되면 내부 스크롤은 살리면서 외부 스크롤 기능은 막을 수 있다.

그러나 iOS 환경에서는 body 태그에 overflow:hidden 속성이 반영되지 않았다.

모바일은 항상 iOS가 속 썩이는 듯 -,-

대신 touch-action:none 속성을 넣어 터치 이벤트를 비활성화했다.

코드 자체는 간단해서, 자주 쓰는 레이어 팝업과 같이 만들어봤다.

HTML

<a href="#" class="btn">1번 버튼</a>
<a href="#" class="btn">2번 버튼</a>
<a href="#" class="btn">3번 버튼</a>
<a href="#" class="btn">4번 버튼</a>
<a href="#" class="btn">5번 버튼</a>

<div class="pop_wrap">
    <div class="bg"></div>
    <div class="popup">
        <a href="#" class="close"><img src="images/close.png" alt="닫기"></a>
        <div class="popup_con">
            컨텐츠<br>
            컨텐츠<br>
            컨텐츠<br>
            컨텐츠<br>
            컨텐츠<br>
            컨텐츠<br>
            컨텐츠<br>
            컨텐츠<br>
            컨텐츠<br>
            컨텐츠<br>
            컨텐츠<br>
            컨텐츠
        </div>
    </div>
</div>

CSS

/* 스크롤 막기 CSS */
.notScroll {overflow: hidden;width: 100%;height: 100vh;touch-action:none;}
/* 팝업 CSS */
.btn {display: flex;justify-content: center;align-items: center;width: 300px;height: 300px;margin: 0 auto 30px;background: #ade9ff;font-size: 16px;text-decoration: none;color: #000;}
.pop_wrap {display: none;position: fixed;top: 0;left: 0;width: 100%;height: 100vh;z-index: 3;}
.pop_wrap .bg {position: fixed;top: 0;left: 0;width: 100%;height: 100vh;background: rgba(0,0,0,0.8);z-index: 1;}
.popup {position: absolute;top: 50%;left: 50%;transform: translate(-50%,-50%);width: 280px;height: 280px;max-width:90vw;max-height: 90vh;background: #fff;z-index: 2;}
.popup .close {position: absolute;top: 15px;right: 15px;width: 17px;height: 17px;}
.popup .close img {width: 100%;height: 100%;}
.popup .popup_con {overflow: auto;padding: 15px;width: 100%;height: 100%;font-size: 16px;line-height: 24px;color: #000;}

레이어 팝업과 활성화시켜줄 버튼을 만들었다.

모바일의 상 · 하단 바의 영역까지 포함하기 위해 height 값을 vh 단위로 바꿨다.

JS

$(document).ready(function(){
    popup();
})

function popup(){
    //팝업 열기
    $('.btn').on('click',function(e){
        e.preventDefault();
        $('.pop_wrap').fadeIn(300);
        $("body").addClass('notScroll');
    })

    //팝업 닫기
    $('.pop_wrap .bg, .pop_wrap .close').on('click',function(e){
        e.preventDefault();
        $('.pop_wrap').fadeOut(100);
        $("body").removeClass('notScroll');
    })
}

팝업을 열 때 body 태그에 notScroll 클래스를 추가하여 외부 스크롤을 막는 CSS 속성을 추가했다.

제일 간단한 방법이지만, iOS 12 (iPhone 6) 이하 버전에서 touch-action:none; 속성이 반영되지 않는 단점이 있다.

대체 방법으로 현재 notScroll 클래스에 position:fixed 속성을 추가하여 화면 영역의 위치를 고정시키는 방법이 있는데, 이게 또 최신 버전에서는 하단 바 영역이 포함되지 않는 버그가 있었다.

CSS scroll 막기 - CSS scroll maggi
iOS 15버전 fixed 영역 버그

iOS 버전 검사를 통해 예외 처리를 해주면 될 것 같은데, iOS 12 버전 점유율이 낮은 편이기도 하고 테스트 기기도 없어서.... 나중에 테스트를 하게 된다면 추가해 봐야겠다.

[ DEMO ]

브라우저에 스크롤이 생겼지만, 스크롤을 움직이지 못하게 하고 싶다면, 아래의 방법을 쓰면 된다.

css로 하는 방법

<style>
body{
	position: fixed; 
	overflow-y: scroll;
	width: 100%;
}
</style>

jquery로 하는 방법

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
$(window).scroll(function( ){  
	$(window).scrollTop(0);
	$(window).scrollLeft(0);
});
</script>

단, jquery로 할 경우에는 스크롤이 된후에 다시 원래대로 되돌아 오는거라서, 완벽한 고정이라고 보기 힘들다.

넘치는 내용에 대한 scroll은 overflow: scroll로 만들 수 있습니다.
scroll 안에 scroll이 있는 경우가 있습니다.

브라우저에서는 기본적으로 scroll chaining이 발생합니다.
내부 스크롤이 끝나면 바깥쪽 혹은 같은 레벨에 있는 Element에서 스크롤이 계속됩니다.
이런 현상을 막기 위한것이 overscroll-behavior입니다.

CSS scroll 막기 - CSS scroll maggi

속성 (3가지)

auto

The default scroll overflow behavior occurs as normal.

scroll chaining이 발생합니다. (브라우저 기본 스크롤 동작입니다.)

contain

Default scroll overflow behavior is observed inside the element this value is set on (e.g. "bounce" effects or refreshes), but no scroll chaining occurs to neighboring scrolling areas, e.g. underlying elements will not scroll.

sibling element에 대해서는 scroll chain이 발생하지 않습니다.
대신 child element에 대해서는 scroll chain이 발생합니다.

none

No scroll chaining occurs to neighboring scrolling areas, and default scroll overflow behavior is prevented.

child Element에도 scroll chain이 발생하지 않습니다.

Example (contain과 none 비교)

아래 예제를 통해서 contain과 none을 비교해볼 수 있습니다.
(예제링크)

outer의 child(inner)가 있는 경우

첫번째 예제에서는 inner에 none을 붙였습니다. inner box의 스크롤이 끝나도 outer의 스크롤은 발생하지 않습니다.

html

<div className="wrapper">
  <div className="outer">
    <div className="inner">
      <div className="start">Start(inner)</div>
      <div className="content"></div>
      <div className="end">End(inner)</div>
    </div>
    <div className="start">Start(outer)</div>
    <div className="content"></div>
    <div className="end">End(outer)</div>
  </div>
</div>

css

.inner {
  overscroll-behavior: none;
}

outer과 inner가 sibling(같은 레벨)인 경우

이때는 contain으로도 가능합니다. (contain은 같은 레벨 element의 scroll을 막습니다.)
또는 none도 사용할 수 있습니다.

html

<div className="wrapper">
  <div className="outer--contain">
    <div className="start">Start(outer)</div>
    <div className="content"></div>
    <div className="end">End(outer)</div>
  </div>
  <div className="inner--contain">
    <div className="start">Start(inner)</div>
    <div className="content"></div>
    <div className="end">End(inner)</div>
  </div>
</div>

css

.inner {
  overscroll-behavior: contain;
}