HTML5 기록 popstate에서 브라우저 스크롤 방지


83

popstate 이벤트가 발생할 때 문서를 스크롤하는 기본 동작을 방지 할 수 있습니까?

우리 사이트는 jQuery 애니메이션 스크롤링과 History.js를 사용하며 상태 변경은 사용자를 pushstate 또는 popstate를 통해 페이지의 다른 영역으로 스크롤해야합니다. 문제는 popstate 이벤트가 발생하면 브라우저가 이전 상태의 스크롤 위치를 자동으로 복원한다는 것입니다.

문서의 너비와 높이를 100 %로 설정 한 컨테이너 요소를 사용하고 해당 컨테이너 내부의 콘텐츠를 스크롤 해 보았습니다. 제가 발견 한 문제는 문서를 스크롤하는 것만 큼 매끄럽지 않은 것 같습니다. 특히 상자 그림자 및 그라디언트와 같은 많은 css3를 사용하는 경우.

또한 사용자가 스크롤을 시작하는 동안 문서의 스크롤 위치를 저장하고 브라우저가 페이지를 스크롤 한 후 (popstate에서) 복원 해 보았습니다. 이것은 Firefox 12에서 잘 작동하지만 Chrome 19에서는 페이지 스크롤 및 복원으로 인해 깜박임이 있습니다. 나는 이것이 스크롤과 스크롤 이벤트가 시작되는 (스크롤 위치가 복원되는) 사이의 지연과 관련이 있다고 가정합니다.

Firefox는 popstate가 실행되기 전에 페이지를 스크롤하고 스크롤 이벤트를 실행하고 Chrome은 먼저 popstate를 실행 한 다음 문서를 스크롤합니다.

기록 API를 사용하는 모든 사이트는 위와 유사한 솔루션을 사용하거나 사용자가 뒤로 / 앞으로 이동할 때 스크롤 위치 변경을 무시합니다 (예 : GitHub).

popstate 이벤트에서 문서가 전혀 스크롤되지 않도록 할 수 있습니까?


스크롤 위치를 설정 한 후 페이지 리플 로우를 강제하기 위해 요소 차원을 읽는 데 성공할 수 있습니다. 예를 들어 document.body.clientHeight모든 브라우저에서 작동하는 것을 보지 못했습니다.
Sean Hogan

문서의 스크롤 기능을 조작했다면, 여기서부터 시작하겠습니다. 여기에 해결책이 있습니다 : stackoverflow.com/questions/7035331/…
Jeff Wooden

답변:


66
if ('scrollRestoration' in history) {
  history.scrollRestoration = 'manual';
}

( 2015 년 9 월 2 일 Google 에서 발표 )

브라우저 지원 :

Chrome : 지원 (46 이후)

Firefox : 지원됨 (46 이후)

IE / Edge : 지원되지 않음, 지원 요청 (댓글에 링크 남기기)

Opera : 지원됨 (33 이후)

Safari : 지원됨

자세한 내용 은 MDN의 브라우저 호환성을 참조하세요 .


1
더 짧게 : history.scrollRestoration && history.scrollRestoration = 'manual';또는 심지어var sr = 'scrollRestoration'; history[sr] && history[sr] = 'manual';
Bharata

31

이것은 지금까지 1 년 넘게 mozilla 개발자 코어 에서 보고 된 문제 였습니다. 불행히도 티켓은 실제로 진행되지 않았습니다. Chrome도 동일하다고 생각 onpopstate합니다. 기본 브라우저 동작이기 때문에 js를 통해 스크롤 위치 를 다룰 수있는 신뢰할 수있는 방법이 없습니다 .

하지만 HTML5 히스토리 사양 을 살펴보면 미래에 대한 희망이 있습니다. HTML5 히스토리 사양 은 스크롤 위치가 상태 객체에 표시되기를 명시 적으로 바라는 것입니다.

기록 객체는 브라우징 컨텍스트의 세션 기록을 세션 기록 항목의 단순 목록으로 나타냅니다. 각 세션 기록 항목은 URL과 선택적으로 상태 개체로 구성되며 제목, 문서 개체, 양식 데이터, 스크롤 위치 및 이와 관련된 기타 정보를 추가로 가질 수 있습니다.

위에서 언급 한 모질라 티켓에 대한 의견을 읽고 경우에, 그리고, 가까운 미래의 스크롤 위치에서 더 이상 복구되지 않을 수 있습니다 몇 가지 표시를 제공 onpopstate적어도 사용하는 사람들을 pushState.

불행히도 그때까지는 스크롤 위치가 pushState사용 되면 저장 replaceState되며 스크롤 위치를 대체하지 않습니다. 그렇지 않으면 상당히 쉬울 replaceState것이며 사용자가 페이지를 스크롤 할 때마다 현재 스크롤 위치를 설정하는 데 사용할 수 있습니다 (주의 깊은 스크롤 처리기 사용).

또한 안타깝게도 HTML5 사양은 popstate이벤트가 정확히 언제 시작되어야하는지 지정하지 않고 다음과 같이 표시됩니다.«세션 기록 항목으로 이동할 때 특정 경우에 시작됨», 이전인지 이후인지 명확하게 표시하지 않습니다. 항상 이전이라면 popstate 이후에 발생하는 스크롤 이벤트를 처리하는 솔루션이 가능할 것입니다.

스크롤 이벤트를 취소 하시겠습니까?

또한 스크롤 이벤트가 취소 가능한 경우에도 쉬울 것입니다. 만약 그렇다면, 시리즈의 첫 번째 스크롤 이벤트를 취소 할 수 있습니다 (사용자 스크롤 이벤트는 레밍과 같으며 수십 개에 해당하는 반면 히스토리 재배치에 의해 발생한 스크롤 이벤트는 단일 이벤트입니다).

지금은 해결책이 없습니다

내가보기에 지금 당장 권장하는 유일한 방법은 HTML5 사양이 완전히 구현 될 때까지 기다렸다가이 경우 브라우저 동작으로 롤링하는 것입니다. 즉, 브라우저에서 할 수있을 때 스크롤 애니메이션을 적용하는 것입니다. , 기록 이벤트가있을 때 브라우저가 페이지의 위치를 ​​변경하도록합니다. 위치 측면에서 영향을 미칠 수있는 유일한 방법 pushState은 페이지가 돌아갈 수있는 좋은 위치에있을 때 사용한다는 것 입니다. 다른 솔루션은 버그가 있거나 너무 브라우저에 특화되어 있거나 둘 다있을 수 있습니다.


이제 히스토리 스펙의 문구가 약간 다릅니다. 또한 아래로 스크롤하여 pushState의 문서를 보면 스크롤 위치 추가에 대한 언급이 없습니다. 사양은 세션 기록 항목에 저장된 스크롤 위치가 포함될 수 있다고 제안하지만 웹 개발자가 설정할 수 있음을 의미하지는 않습니다.
Walex 2014 년

4

여기서 스니핑하는 끔찍한 브라우저를 사용해야 할 것입니다. Firefox의 경우 스크롤 위치를 저장하고 복원하는 솔루션을 사용합니다.

귀하의 설명을 기반으로 좋은 Webkit 솔루션이 있다고 생각했지만 Chrome 21에서 시도했지만 Chrome이 먼저 스크롤 한 다음 popstate 이벤트를 실행 한 다음 스크롤 이벤트를 실행하는 것 같습니다. 그러나 참고로 여기에 내가 생각해 낸 것이 있습니다.

function noScrollOnce(event) {
    event.preventDefault();
    document.removeEventListener('scroll', noScrollOnce);
}
window.onpopstate = function () {
    document.addEventListener('scroll', noScrollOnce);
};​

absolute위치 요소 를 이동하여 페이지를 스크롤하는 척하는 등의 흑마 법도 화면 다시 그리기 속도에 의해 배제됩니다.

그래서 저는 99 %의 대답은 당신이 할 수 없다는 것이라고 확신합니다. 그리고 당신은 질문에서 언급 한 타협 중 하나를 사용해야 할 것입니다. 두 브라우저 모두 JavaScript가 그것에 대해 알기 전에 스크롤하므로 JavaScript는 이벤트 후에 만 ​​반응 할 수 있습니다. 유일한 차이점은 Firefox는 Javascript가 실행될 때까지 화면을 그리지 않는다는 것입니다. 이것이 바로 Firefox에 실행 가능한 솔루션이 있지만 WebKit에는없는 이유입니다.


3

이제 할 수 있습니다

history.scrollRestoration = 'manual';

브라우저 스크롤을 방지해야합니다. 이것은 현재 Chrome 46 이상에서만 작동하지만 Firefox도 지원할 계획 인 것 같습니다.


2

해결책은 페이지의 스크롤 위치 position: fixedtop동일하게 사용 하고 지정하는 것 입니다.

다음은 그 예입니다.

$(window).on('popstate', function()
{
   $('.yourWrapAroundAllContent').css({
      position: 'fixed',
      top: -window.scrollY
   });

   requestAnimationFrame(function()
   {
      $('.yourWrapAroundAllContent').css({
         position: 'static',
         top: 0
      });          
   });
});

예, 대신 깜박이는 스크롤바가 표시되지만 덜 사악합니다.


1

다음 수정 사항은 모든 브라우저에서 작동합니다.

언로드 이벤트에서 스크롤 위치를 0으로 설정할 수 있습니다. 이 이벤트에 대한 내용은 https://developer.mozilla.org/en-US/docs/Web/Events/unload 에서 읽을 수 있습니다 . 기본적으로 언로드 이벤트는 페이지를 떠나기 직전에 발생합니다.

언로드시 scrollPosition을 0으로 설정하면 pushState가 설정된 페이지에서 나갈 때 scrollPosition이 0으로 설정됩니다. 새로 고치거나 뒤로 눌러이 페이지로 돌아 오면 자동 스크롤되지 않습니다.

//Listen for unload event. This is triggered when leaving the page.
//Reference: https://developer.mozilla.org/en-US/docs/Web/Events/unload
window.addEventListener('unload', function(e) {
    //set scroll position to the top of the page.
    window.scrollTo(0, 0);
});

1

scrollRestoration을 수동으로 설정하면 작동하지 않습니다. 여기 내 해결책이 있습니다.

window.addEventListener('popstate', function(e) {
    var scrollTop = document.body.scrollTop;
    window.addEventListener('scroll', function(e) {
        document.body.scrollTop = scrollTop;
    });
});

0

페이지 상단 어딘가에 'span'요소를 만들고로드시 여기에 초점을 설정합니다. 브라우저는 포커스가있는 요소로 스크롤됩니다. 나는 이것이 해결 방법이며 'span'에 대한 초점이 모든 브라우저 (uhmmm .. Safari)에서 작동하지 않는다는 것을 이해합니다. 도움이 되었기를 바랍니다.


0

다음은 poststate가 실행될 때 스크롤 위치가 특정 요소에 초점을 맞추도록하는 사이트에서 구현 한 것입니다 (뒤로 버튼).

$(document).ready(function () {

     if (window.history.pushState) {
        //if push supported - push current page onto stack:
        window.history.pushState(null, document.title, document.location.href);
    }

    //add handler:
    $(window).on('popstate', PopStateHandler);
}

//fires when the back button is pressed:
function PopStateHandler(e) {
    emnt= $('#elementID');
    window.scrollTo(0, emnt.position().top);
    alert('scrolling to position');
}

firefox에서 테스트 및 작동합니다.

Chrome은 위치로 스크롤 한 다음 원래 위치로 다시 위치를 변경합니다.


0

다른 사람들이 말했듯이, 그것을 할 실제 방법은 없으며 추악한 해키 방법입니다. 스크롤 이벤트 리스너를 제거해도 효과가 없었으므로 다음과 같은 추악한 방법이 있습니다.

/// GLOBAL VARS ////////////////////////
var saveScrollPos = false;
var scrollPosSaved = window.pageYOffset;
////////////////////////////////////////

jQuery(document).ready(function($){

    //// Go back with keyboard shortcuts ////
    $(window).keydown(function(e){
        var key = e.keyCode || e.charCode;

        if((e.altKey && key == 37) || (e.altKey && key == 39) || key == 8)
        saveScrollPos = false;
    });
    /////////////////////////////////////////

    //// Go back with back button ////
    $("html").bind("mouseout",  function(){ saveScrollPos = false; });
    //////////////////////////////////

    $("html").bind("mousemove", function(){ saveScrollPos = true; });

    $(window).scroll(function(){
        if(saveScrollPos)
        scrollPosSaved = window.pageYOffset;
        else
        window.scrollTo(0, scrollPosSaved);
    });

});

Chrome, FF 및 IE에서 작동합니다 (IE에서 처음으로 돌아갈 때 깜박임). 개선 제안을 환영합니다! 도움이 되었기를 바랍니다.


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