Animate scrollTop không hoạt động trong firefox


164

Chức năng này hoạt động tốt. Nó cuộn cơ thể đến phần bù chứa mong muốn

function scrolear(destino){
    var stop = $(destino).offset().top;
    var delay = 1000;
    $('body').animate({scrollTop: stop}, delay);
    return false;
}

Nhưng không phải trong Firefox. Tại sao?

-BIÊN TẬP-

Để xử lý kích hoạt de double trong câu trả lời được chấp nhận, tôi khuyên bạn nên dừng phần tử trước khi hoạt ảnh:

$('body,html').stop(true,true).animate({scrollTop: stop}, delay);

2
Mã cuối cùng của bạn là thanh lịch nhất trong tất cả các công cụ ở đây.
Lizardx

Câu trả lời:


331

Firefox đặt tràn ở htmlcấp độ, trừ khi được tạo kiểu đặc biệt để hành xử khác đi.

Để làm cho nó hoạt động trong Firefox, hãy sử dụng

$('body,html').animate( ... );

Ví dụ làm việc

Giải pháp CSS sẽ là thiết lập các kiểu sau:

html { overflow: hidden; height: 100%; }
body { overflow: auto; height: 100%; }

Tôi cho rằng giải pháp JS sẽ ít xâm lấn nhất.


Cập nhật

Rất nhiều cuộc thảo luận dưới đây tập trung vào thực tế là hoạt hình scrollTopcủa hai yếu tố sẽ khiến cuộc gọi lại được gọi hai lần. Các tính năng phát hiện trình duyệt đã được đề xuất và sau đó không được dùng nữa, và một số được cho là khá xa vời.

Nếu cuộc gọi lại là tạm thời và không đòi hỏi nhiều sức mạnh tính toán, việc bắn nó hai lần có thể là một vấn đề hoàn toàn. Nếu nhiều lệnh gọi lại thực sự là một vấn đề và nếu bạn muốn tránh phát hiện tính năng, có thể sẽ đơn giản hơn để thực thi rằng cuộc gọi lại chỉ được chạy một lần từ trong cuộc gọi lại:

function runOnce(fn) { 
    var count = 0; 
    return function() { 
        if(++count == 1)
            fn.apply(this, arguments);
    };
};

$('body, html').animate({ scrollTop: stop }, delay, runOnce(function() {
   console.log('scroll complete');
}));

51
Giải pháp JS có một lỗ hổng! Hàm animate được thực hiện hai lần, cho phần tử html và phần tử cơ thể tương ứng. Có vẻ như các trình duyệt Webkit sử dụng phần thân và phần còn lại sử dụng html . $(jQuery.browser.webkit ? "body": "html").animate(...);
Khắc

2
Tôi thấy rằng giải pháp từ Andreyco không hoạt động trong jQuery <1.4 mà tôi đang sử dụng. Tôi đã có thể sửa nó như thế này : $(jQuery.browser.mozilla ? "html" : "body").animate(...);. Sẽ thật tuyệt nếu không sử dụng trình duyệt đánh hơi, nhưng phát hiện tính năng. Không chắc chắn làm thế nào để phát hiện tính năng trên CSS mặc dù
Rustavore

23
Lưu ý, jQuery.browserkhông được dùng nữa và bị thiếu trong phiên bản jQuery mới nhất
spiderplant0

2
4 năm trôi qua, và câu trả lời này vẫn còn hữu ích. Cảm ơn rất nhiều!
Matt

1
@DamFa: Chủ yếu, vì window.scrollkhông hoạt hình. Tất nhiên bạn có thể cuộn thứ của riêng bạn với các khoảng và scrollBy. Nếu bạn muốn thêm phần dễ dàng vào đó, bạn đột nhiên có rất nhiều mã để viết cho một khía cạnh khá nhỏ của sản phẩm của bạn.
David Hedlund

19

Phát hiện tính năng và sau đó hoạt ảnh trên một đối tượng được hỗ trợ sẽ rất tốt, nhưng không có giải pháp một dòng. Trong khi đó, đây là một cách để sử dụng lời hứa để thực hiện một cuộc gọi lại cho mỗi lần thực hiện.

$('html, body')
    .animate({ scrollTop: 100 })
    .promise()
    .then(function(){
        // callback code here
    })
});

CẬP NHẬT: Đây là cách bạn có thể sử dụng tính năng phát hiện thay thế. Đoạn mã này cần được đánh giá trước cuộc gọi hoạt hình của bạn:

// Note that the DOM needs to be loaded first, 
// or else document.body will be undefined
function getScrollTopElement() {

    // if missing doctype (quirks mode) then will always use 'body'
    if ( document.compatMode !== 'CSS1Compat' ) return 'body';

    // if there's a doctype (and your page should)
    // most browsers will support the scrollTop property on EITHER html OR body
    // we'll have to do a quick test to detect which one...

    var html = document.documentElement;
    var body = document.body;

    // get our starting position. 
    // pageYOffset works for all browsers except IE8 and below
    var startingY = window.pageYOffset || body.scrollTop || html.scrollTop;

    // scroll the window down by 1px (scrollTo works in all browsers)
    var newY = startingY + 1;
    window.scrollTo(0, newY);

    // And check which property changed
    // FF and IE use only html. Safari uses only body.
    // Chrome has values for both, but says 
    // body.scrollTop is deprecated when in Strict mode.,
    // so let's check for html first.
    var element = ( html.scrollTop === newY ) ? 'html' : 'body';

    // now reset back to the starting position
    window.scrollTo(0, startingY);

    return element;
}

// store the element selector name in a global var -
// we'll use this as the selector for our page scrolling animation.
scrollTopElement = getScrollTopElement();

Bây giờ sử dụng var mà chúng ta vừa xác định là bộ chọn cho hoạt hình cuộn trang và sử dụng cú pháp thông thường:

$(scrollTopElement).animate({ scrollTop: 100 }, 500, function() {
    // normal callback
});

Hoạt động tuyệt vời trong hoàn cảnh bình thường, cảm ơn! Tuy nhiên, có một vài vấn đề cần chú ý. (1) Nếu cơ thể không có đủ nội dung để tràn cửa sổ khi kiểm tra tính năng chạy, cửa sổ sẽ không cuộn và kiểm tra trả về kết quả "cơ thể" không có thật. (2) Nếu thử nghiệm chạy bên trong iframe trong iOS, nó sẽ thất bại vì lý do tương tự. Iframes trong iOS mở rộng theo chiều dọc (không phải theo chiều ngang) cho đến khi nội dung vừa với bên trong mà không cần cuộn. (3) Vì thử nghiệm được thực thi trong cửa sổ chính, nó kích hoạt các trình xử lý cuộn đã được đặt sẵn. ... (Tiếp theo)
hashchange

... Vì những lý do này, một bài kiểm tra chống đạn sẽ phải chạy trong iframe chuyên dụng (giải (3)) với nội dung đủ lớn (giải (1)) được kiểm tra cho cuộn ngang (giải (2)).
hashchange

Tôi thực sự đã phải chạy chức năng này trong một kịch bản hết thời gian để làm cho điều này cho kết quả nhất quán, nếu không nó dường như đôi khi trở lại htmlvà đôi khi trở lại body. Đây là mã của tôi sau khi khai báo hàm var scrollTopElement; setTimeout( function() { scrollTopElement = getScrollTopElement(); console.log(scrollTopElement); }, 1000); Cảm ơn bạn vì điều này, nó đã giải quyết vấn đề của tôi.
Tony M

Không sao, tôi đã tìm ra vấn đề thực sự là gì. Nếu bạn đã ở cuối trang khi bạn tải lại (f5), tập lệnh này sẽ trở lại htmlthay vì bodynhư dự kiến. Điều này chỉ khi bạn cuộn toàn bộ trang web xuống và bạn tải lại, nếu không nó sẽ hoạt động.
Tony M

Điều này làm việc cho tôi sau khi tôi thêm một kiểm tra nếu trang mở ra ở dưới cùng.
scott

6

Tôi đã dành nhiều năm cố gắng tìm ra lý do tại sao mã của tôi không hoạt động -

$('body,html').animate({scrollTop: 50}, 500);

Vấn đề là ở css của tôi -

body { height: 100%};

Tôi đặt nó để autothay thế (và đã lo lắng về lý do tại sao nó được đặt 100%ở vị trí đầu tiên). Điều đó đã sửa nó cho tôi.


2

Bạn có thể muốn tránh sự cố bằng cách sử dụng một plugin - cụ thể hơn là plugin của tôi :)

Nghiêm túc, mặc dù vấn đề cơ bản đã được giải quyết từ lâu (các trình duyệt khác nhau sử dụng các yếu tố khác nhau để cuộn cửa sổ), có khá nhiều vấn đề không hề nhỏ có thể khiến bạn gặp rắc rối:

Tôi rõ ràng thiên vị, nhưng jQuery.scrollable thực sự là một lựa chọn tốt để giải quyết những vấn đề này. (Trên thực tế, tôi không biết bất kỳ plugin nào khác xử lý tất cả.)

Ngoài ra, bạn có thể tính toán vị trí mục tiêu - một trong đó bạn di chuyển đến - một cách chống đạn với các getScrollTargetPosition()chức năng trong ý chính này .

Tất cả sẽ để lại cho bạn với

function scrolear ( destino ) {
    var $window = $( window ),
        targetPosition = getScrollTargetPosition ( $( destino ), $window );

    $window.scrollTo( targetPosition, { duration: 1000 } );

    return false;
}

1

Coi chừng việc này. Tôi gặp vấn đề tương tự, cả Firefox và Explorer đều không cuộn

$('body').animate({scrollTop:pos_},1500,function(){do X});

Vì vậy, tôi đã sử dụng như David nói

$('body, html').animate({scrollTop:pos_},1500,function(){do X});

Thật tuyệt khi nó hoạt động, nhưng vấn đề mới, vì có hai phần tử, body và html, hàm được thực thi hai lần, đây là, do X chạy hai lần.

chỉ thử với 'html' và Firefox và Explorer hoạt động, nhưng hiện tại Chrome không hỗ trợ điều này.

Cơ thể cần thiết cho Chrome và html cho Firefox và Explorer. Đây có phải là một lỗi của jQuery không? không biết

Chỉ cần cẩn thận với chức năng của bạn, vì nó sẽ chạy hai lần.


bạn đã tìm thấy một cách giải quyết cho điều này? Và vì phát hiện trình duyệt jquery không được chấp nhận, tôi không biết làm cách nào tôi có thể sử dụng tính năng phát hiện tính năng để phân biệt giữa chrome và firefox. Tài liệu của họ không chỉ định một tính năng có trong chrome và không có trong firefox hoặc viceversa. :(
Mandeep Jain

2
những gì về .stop (đúng, đúng) .animate?
Toni Michel Caubet

0

Tôi muốn giới thiệu không nên dựa vào bodycũng htmlnhư một giải pháp di động hơn. Chỉ cần thêm một div trong phần thân nhằm mục đích chứa các phần tử cuộn và tạo kiểu cho nó để cho phép cuộn kích thước đầy đủ:

#my-scroll {
  position: absolute;
  width: 100%;
  height: 100%;
  overflow: auto;
}

(giả sử rằng display:block;top:0;left:0; là mặc định phù hợp với mục tiêu của bạn), sau đó sử dụng $('#my-scroll')cho hoạt hình của bạn.


0

Đây là thỏa thuận thực sự. Nó hoạt động trên Chrome và Firefox hoàn hảo. Nó thậm chí còn buồn khi một số người không biết gì bỏ phiếu cho tôi xuống. Mã này hoạt động hoàn hảo như trên tất cả các trình duyệt. Bạn chỉ cần thêm một liên kết và đặt id của phần tử bạn muốn cuộn trong href và nó hoạt động mà không chỉ định bất cứ điều gì. Mã tái sử dụng và đáng tin cậy.

$(document).ready(function() {
  function filterPath(string) {
    return string
    .replace(/^\//,'')
    .replace(/(index|default).[a-zA-Z]{3,4}$/,'')
    .replace(/\/$/,'');
  }
  var locationPath = filterPath(location.pathname);
  var scrollElem = scrollableElement('html', 'body');

  $('a[href*=#]').each(function() {
    var thisPath = filterPath(this.pathname) || locationPath;
    if (locationPath == thisPath
    && (location.hostname == this.hostname || !this.hostname)
    && this.hash.replace(/#/,'') ) {
      var $target = $(this.hash), target = this.hash;
      if (target) {
        var targetOffset = $target.offset().top;
        $(this).click(function(event) {
          event.preventDefault();
          $(scrollElem).animate({scrollTop: targetOffset}, 400, function() {
            location.hash = target;
          });
        });
      }
    }
  });

  // use the first element that is "scrollable"
  function scrollableElement(els) {
    for (var i = 0, argLength = arguments.length; i <argLength; i++) {
      var el = arguments[i],
          $scrollElement = $(el);
      if ($scrollElement.scrollTop()> 0) {
        return el;
      } else {
        $scrollElement.scrollTop(1);
        var isScrollable = $scrollElement.scrollTop()> 0;
        $scrollElement.scrollTop(0);
        if (isScrollable) {
          return el;
        }
      }
    }
    return [];
  }
});

1
Nó có thể phức tạp, nhưng nó thực sự hữu ích. Về cơ bản, nó hoạt động Plug-N-Play trong tất cả các trình duyệt. Một giải pháp đơn giản hơn có thể không cho phép bạn làm điều đó.
drjorgepolanco 10/03/2015

0

Gần đây tôi đã gặp phải vấn đề tương tự và tôi đã giải quyết nó bằng cách làm điều này:

$ ('html, body'). animate ({scrollTop: $ ('. class_of_div'). offset () .top}, 'fast'});

Và youpi !!! nó hoạt động trên tất cả các trình duyệt.

trong trường hợp định vị không chính xác, bạn có thể trừ một giá trị từ offset () .top bằng cách thực hiện điều này

$ ('html, body'). animate ({scrollTop: $ ('. class_of_div'). offset () .top-desired_value}, 'fast'});

Giải pháp này đã được đề cập trong các câu trả lời hiện có nhưng cảm ơn bạn đã chia sẻ
Toni Michel Caubet

Phương pháp này vẫn gặp một trục trặc trong Firefox, như đã lưu ý trong câu hỏi tôi vừa đăng: stackoverflow.com/q/58617731/1056713
Chaya Cooper

-3

Đối với tôi, vấn đề là firefox tự động nhảy lên mỏ neo với thuộc tính name giống như tên băm tôi đặt vào URL. Mặc dù tôi đặt .preventDefault () để ngăn chặn điều đó. Vì vậy, sau khi thay đổi thuộc tính tên, firefox không tự động nhảy đến các neo mà thực hiện hoạt ảnh ngay.

@Toni Xin lỗi nếu điều này không thể hiểu được. Vấn đề là tôi đã thay đổi các giá trị băm trong URL như www.someurl.com/#hashname. Sau đó, tôi đã có một ví dụ như neo <a name="hashname" ...></a>mà jQuery sẽ tự động cuộn đến. Nhưng điều đó không xảy ra bởi vì nó đã nhảy thẳng vào mỏ neo với thuộc tính tên phù hợp trong Firefox mà không có bất kỳ hoạt hình cuộn nào. Khi tôi đã thay đổi thuộc tính name thành một cái gì đó khác với tên băm, ví dụ như name="hashname-anchor", việc cuộn đã hoạt động.


-5

Đối với tôi, đó là tránh việc gắn ID vào điểm hoạt hình:

Tránh:

 scrollTop: $('#' + id).offset().top

Chuẩn bị id trước và làm điều này thay vào đó:

 scrollTop: $(id).offset().top

Đã sửa lỗi trong FF. (Việc bổ sung css không tạo ra sự khác biệt đối với tôi)


-8
setTimeout(function(){                               
                   $('html,body').animate({ scrollTop: top }, 400);
                 },0);

Hy vọng điều này làm việc.


4
Làm thế nào để setTimeut giúp đỡ, ở đây?
Toni Michel Caubet

2
@ToniMichelCaubet có lẽ mục tiêu là làm cho hình ảnh động không đồng bộ. Mặc dù vậy, các hình ảnh động jQuery đã không đồng bộ, do đó, điều này không thực hiện được gì.
mwcz

Không phải là một câu trả lời chu đáo cho vấn đề. Không chắc chắn những gì sẽ thực hiện.
erier
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.