Ngăn chặn cuộn trình duyệt trên cửa sổ bật lên Lịch sử HTML5


83

Có thể ngăn chặn hành vi cuộn tài liệu mặc định khi sự kiện cửa sổ bật lên xảy ra không?

Trang web của chúng tôi sử dụng tính năng cuộn động jQuery và History.js, và các thay đổi trạng thái sẽ cuộn người dùng đến các khu vực khác nhau của trang dù qua pushstate hay popstate. Vấn đề là trình duyệt tự động khôi phục vị trí cuộn của trạng thái trước đó khi một sự kiện cửa sổ bật lên xảy ra.

Tôi đã thử sử dụng phần tử vùng chứa được đặt thành 100% chiều rộng và chiều cao của tài liệu và cuộn nội dung bên trong vùng chứa đó. Vấn đề mà tôi đã tìm thấy là nó dường như không được mượt mà như cuộn tài liệu; đặc biệt nếu sử dụng nhiều css3 như box-shadow và gradient.

Tôi cũng đã thử lưu trữ vị trí cuộn của tài liệu trong khi người dùng bắt đầu cuộn và khôi phục nó sau khi trình duyệt cuộn trang (trên popstate). Điều này hoạt động tốt trong Firefox 12 nhưng trong Chrome 19 có hiện tượng nhấp nháy do trang đang được cuộn và khôi phục. Tôi giả sử điều này là do độ trễ giữa cuộn và sự kiện cuộn được kích hoạt (nơi vị trí cuộn được khôi phục).

Firefox cuộn trang (và kích hoạt sự kiện cuộn) trước khi popstate kích hoạt và Chrome kích hoạt popstate trước rồi cuộn tài liệu.

Tất cả các trang web tôi đã thấy sử dụng API lịch sử hoặc sử dụng giải pháp tương tự như những trang ở trên hoặc chỉ bỏ qua sự thay đổi vị trí cuộn khi người dùng quay lại / chuyển tiếp (ví dụ: GitHub).

Có thể ngăn tài liệu được cuộn ở tất cả các sự kiện cửa sổ bật lên không?


Bạn có thể thành công khi đọc thứ nguyên phần tử (để buộc chỉnh trang lại) sau khi thiết lập vị trí cuộn. ví dụ: document.body.clientHeightnhưng tôi đã không thấy nó hoạt động trong tất cả các trình duyệt.
Sean Hogan

Điều gì sẽ xảy ra nếu bạn thao tác với chức năng cuộn của tài liệu, đó là nơi tôi sẽ bắt đầu ít nhất. Có một giải pháp cho nó ở đây: stackoverflow.com/questions/7035331/…
Jeff Wooden,

Câu trả lời:


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

( Được Google công bố vào ngày 2 tháng 9 năm 2015)

Hỗ trợ trình duyệt:

Chrome: được hỗ trợ (kể từ 46)

Firefox: được hỗ trợ (kể từ 46)

IE / Edge: không được hỗ trợ, yêu cầu hỗ trợ (sau đó để lại liên kết trong bình luận)

Opera: được hỗ trợ (kể từ năm 33)

Safari: được hỗ trợ

Để biết thêm thông tin, hãy xem Khả năng tương thích của trình duyệt trên MDN .


1
Ngắn hơn: history.scrollRestoration && history.scrollRestoration = 'manual';hoặc thậm chívar sr = 'scrollRestoration'; history[sr] && history[sr] = 'manual';
Bharata

31

Đây là vấn đề được báo cáo với lõi nhà phát triển mozilla trong hơn một năm nay . Thật không may, vé đã không thực sự tiến triển. Tôi nghĩ Chrome cũng vậy: Không có cách nào đáng tin cậy để xử lý vị trí cuộn onpopstatequa js, vì đó là hành vi của trình duyệt gốc.

Tuy nhiên, vẫn có hy vọng cho tương lai, nếu bạn nhìn vào thông số lịch sử HTML5 , điều này mong muốn rõ ràng vị trí cuộn được thể hiện trên đối tượng trạng thái:

Các đối tượng lịch sử đại diện cho lịch sử phiên của ngữ cảnh duyệt web của chúng dưới dạng một danh sách phẳng các mục lịch sử phiên. Mỗi mục nhập lịch sử phiên bao gồm một URL và tùy chọn một đối tượng trạng thái và ngoài ra có thể có tiêu đề, đối tượng Tài liệu, dữ liệu biểu mẫu, vị trí cuộn và thông tin khác được liên kết với nó.

Điều này, và nếu bạn đọc các nhận xét về vé mozilla được đề cập ở trên, cho thấy một số dấu hiệu cho thấy có thể trong tương lai gần, vị trí cuộn sẽ không được khôi phục nữa onpopstate, ít nhất là đối với những người đang sử dụng pushState.

Thật không may, cho đến lúc đó, vị trí cuộn sẽ được lưu trữ khi pushStateđược sử dụng, và replaceStatekhông thay thế vị trí cuộn. Nếu không, nó sẽ khá dễ dàng và bạn có thể sử dụng replaceStateđể đặt vị trí Cuộn hiện tại mỗi khi người dùng cuộn trang (với một số trình xử lý trên cuộn thận trọng).

Cũng rất tiếc, thông số kỹ thuật HTML5 không chỉ định thời điểm chính xác popstatesự kiện phải được kích hoạt, nó chỉ cho biết: «được kích hoạt trong một số trường hợp nhất định khi điều hướng đến mục nhập lịch sử phiên», điều này không cho biết rõ ràng là trước hay sau; nếu nó luôn là trước đây, một giải pháp xử lý sự kiện cuộn xảy ra sau cửa sổ bật lên sẽ khả thi.

Hủy sự kiện cuộn?

Hơn nữa, nó cũng sẽ dễ dàng, nếu sự kiện cuộn ở nơi có thể hủy được, nơi thì không. Nếu đúng như vậy, bạn chỉ có thể hủy sự kiện cuộn đầu tiên của một chuỗi (các sự kiện cuộn của người dùng giống như lemmings, chúng có hàng chục, trong khi sự kiện cuộn được kích hoạt bởi định vị lại lịch sử là một sự kiện duy nhất) và bạn sẽ ổn.

Không có giải pháp cho bây giờ

Theo những gì tôi thấy, điều duy nhất tôi khuyên bạn bây giờ là đợi cho Thông số HTML5 được triển khai đầy đủ và cuộn với hành vi của trình duyệt trong trường hợp này, điều đó có nghĩa là: tạo hiệu ứng cuộn khi trình duyệt cho phép bạn làm điều đó và cho phép trình duyệt định vị lại trang khi có sự kiện lịch sử. Điều duy nhất bạn có thể ảnh hưởng đến vị trí là bạn sử dụng pushStatekhi trang được định vị theo cách tốt để quay lại. Bất kỳ giải pháp nào khác đều có lỗi hoặc quá cụ thể đối với trình duyệt hoặc cả hai.


Từ ngữ của thông số lịch sử hơi khác bây giờ. Ngoài ra, nếu bạn cuộn xuống và xem tài liệu của pushState, không có đề cập đến việc thêm vị trí cuộn. Thông số cho thấy mục lịch sử phiên có thể chứa vị trí cuộn đã lưu, nhưng tôi không chắc điều đó có nghĩa là nhà phát triển web sẽ có thể đặt vị trí đó.
Walex

4

Bạn sẽ phải sử dụng một số kiểu đánh hơi trình duyệt khủng khiếp ở đây. Đối với Firefox, tôi sẽ sử dụng giải pháp lưu trữ vị trí cuộn và khôi phục nó.

Tôi nghĩ rằng tôi đã có một giải pháp Webkit tốt dựa trên mô tả của bạn, nhưng tôi vừa thử trong Chrome 21 và có vẻ như Chrome sẽ cuộn trước, sau đó kích hoạt sự kiện popstate, sau đó kích hoạt sự kiện cuộn. Nhưng để tham khảo, đây là những gì tôi nghĩ ra:

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

Ma thuật đen như giả vờ trang đang cuộn bằng cách di chuyển một absolutephần tử đã định vị cũng bị loại trừ bởi tốc độ sơn lại màn hình.

Vì vậy, tôi chắc chắn 99% rằng câu trả lời là bạn không thể, và bạn sẽ phải sử dụng một trong những thỏa hiệp mà bạn đã đề cập trong câu hỏi. Cả hai trình duyệt đều cuộn trước khi JavaScript biết bất cứ điều gì về nó, vì vậy JavaScript chỉ có thể phản ứng sau sự kiện. Sự khác biệt duy nhất là Firefox không vẽ màn hình cho đến khi Javascript được kích hoạt, đó là lý do tại sao có một giải pháp khả thi trong Firefox nhưng không khả thi trong WebKit.


3

Bây giờ bạn có thể làm

history.scrollRestoration = 'manual';

và điều này sẽ ngăn việc cuộn trình duyệt. Tính năng này hiện chỉ hoạt động trong Chrome 46 trở lên, nhưng có vẻ như Firefox cũng đang có kế hoạch hỗ trợ nó


2

Giải pháp là sử dụng position: fixedvà chỉ định topbằng với vị trí cuộn của trang.

Đây là một ví dụ:

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

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

Có, thay vào đó bạn nhận được thanh cuộn nhấp nháy, nhưng nó ít ác hơn.


1

Bản sửa lỗi sau sẽ hoạt động trên tất cả các trình duyệt.

Bạn có thể đặt vị trí cuộn thành 0 trong sự kiện dỡ hàng. Bạn có thể đọc về sự kiện này tại đây: https://developer.mozilla.org/en-US/docs/Web/Events/unload . Về cơ bản, sự kiện dỡ tải sẽ kích hoạt ngay trước khi bạn rời khỏi trang.

Bằng cách đặt scrollPosition thành 0 khi không tải có nghĩa là khi bạn rời khỏi trang với một pushState đã đặt, nó sẽ đặt scrollPosition thành 0. Khi bạn quay lại trang này bằng cách làm mới hoặc nhấn lại, nó sẽ không tự động cuộn.

//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

Đặt scrollRestoration thành thủ công không phù hợp với tôi, đây là giải pháp của tôi.

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

0

Tạo phần tử 'span' ở đâu đó trên đầu trang và đặt tiêu điểm cho phần tử này khi tải. Trình duyệt sẽ cuộn đến phần tử được tiêu điểm. Tôi hiểu rằng đây là một cách giải quyết và việc tập trung vào 'span' không hoạt động trong tất cả các trình duyệt (uhmmm .. Safari). Hi vọng điêu nay co ich.


0

Đây là những gì tôi đã triển khai trên một trang web muốn vị trí cuộn tập trung vào một phần tử cụ thể khi trạng thái đăng được kích hoạt (nút quay lại):

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

Đã thử nghiệm và hoạt động trong firefox.

Chrome sẽ cuộn đến vị trí nhưng sau đó lại đặt lại vị trí ban đầu.


0

Giống như những người khác đã nói, không có cách nào thực sự để làm điều đó, chỉ có cách hacky xấu xí. Việc xóa trình nghe sự kiện cuộn không hoạt động đối với tôi, vì vậy đây là cách xấu xí của tôi để thực hiện điều đó:

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

});

Nó hoạt động trên Chrome, FF và IE (nó sẽ nhấp nháy vào lần đầu tiên bạn quay lại IE). Mọi đề xuất cải tiến đều được hoan nghênh! Hi vọng điêu nay co ich.


-5

Có thể muốn thử điều này?

window.onpopstate = function(event) {
  return false;
};

3
Điều này không hoạt động. Một số sự kiện không thể bị ngăn chặn và popstate là một trong số đó.
Nathan MacInnes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.