Menghidupkan scrollTop tidak berfungsi di firefox


164

Fungsi ini berfungsi dengan baik. Ini menggulung tubuh ke offset wadah yang diinginkan

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

Tetapi tidak di Firefox. Mengapa?

-EDIT-

Untuk menangani de double trigger pada jawaban yang diterima, saya sarankan menghentikan elemen sebelum animasi:

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

2
Kode akhir Anda adalah yang paling elegan dari semua yang ada di sini.
Lizardx

Jawaban:


331

Firefox menempatkan overflow di html level, kecuali jika secara khusus ditata untuk berperilaku berbeda.

Untuk membuatnya berfungsi di Firefox, gunakan

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

Contoh kerja

Solusi CSS adalah mengatur gaya berikut:

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

Saya akan berasumsi bahwa solusi JS akan paling tidak invasif.


Memperbarui

Banyak diskusi di bawah ini berfokus pada fakta bahwa menjiwai scrollTopdua elemen akan menyebabkan panggilan balik dipanggil dua kali. Fitur pendeteksi browser telah disarankan dan kemudian tidak digunakan lagi, dan beberapa bisa dibilang agak dibuat-buat.

Jika panggilan balik idempoten dan tidak memerlukan banyak daya komputasi, memecatnya dua kali mungkin bukan masalah total. Jika beberapa pemanggilan dari callback benar-benar merupakan masalah, dan jika Anda ingin menghindari deteksi fitur, mungkin lebih mudah untuk memastikan bahwa callback hanya dijalankan sekali dari dalam callback:

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
Solusi JS memiliki kelemahan! fungsi animate dieksekusi dua kali, masing-masing untuk elemen html dan untuk elemen tubuh . Sepertinya browser Webkit menggunakan tubuh dan sisanya menggunakan html . Perbaikan ini harus melakukan pekerjaan$(jQuery.browser.webkit ? "body": "html").animate(...);
Andreyco

2
Saya menemukan bahwa solusi dari Andreyco tidak berfungsi di jQuery <1.4 yang saya gunakan. Saya bisa memperbaikinya seperti ini: $(jQuery.browser.mozilla ? "html" : "body").animate(...);. Akan lebih bagus untuk tidak menggunakan pencarian sniffing, tetapi deteksi fitur. Tidak yakin bagaimana melakukan deteksi fitur pada CSS
Rustavore

23
Catatan, jQuery.browsersudah usang dan hilang dari versi jQuery terbaru
spiderplant0

2
4 tahun kemudian, dan jawaban ini masih terbukti bermanfaat. Terima kasih banyak!
Matt

1
@DamFa: Terutama, karena window.scrolltidak bernyawa. Anda tentu saja dapat menggulung barang Anda sendiri dengan interval dan scrollBy. Jika Anda ingin menambahkan pelonggaran untuk itu, Anda tiba-tiba memiliki banyak kode untuk ditulis untuk aspek yang agak kecil dari produk Anda.
David Hedlund

19

Deteksi fitur dan kemudian menghidupkan objek tunggal yang didukung akan menyenangkan, tetapi tidak ada solusi satu baris. Sementara itu, inilah cara untuk menggunakan janji untuk melakukan satu panggilan balik per eksekusi.

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

PEMBARUAN: Inilah cara Anda dapat menggunakan deteksi fitur. Potongan kode ini perlu dievaluasi sebelum panggilan animasi Anda:

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

Sekarang gunakan var yang baru saja kita definisikan sebagai pemilih untuk animasi halaman bergulir, dan gunakan sintaks biasa:

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

Bekerja sangat baik dalam keadaan normal, terima kasih! Ada beberapa gotcha yang harus diperhatikan. (1) Jika tubuh tidak memiliki konten yang cukup untuk meluap jendela ketika tes fitur berjalan, jendela tidak akan menggulir dan tes mengembalikan hasil "tubuh" palsu. (2) Jika tes berjalan di dalam iframe di iOS, itu gagal karena alasan yang sama. Iframe di iOS meluas secara vertikal (tidak horizontal) hingga kontennya masuk tanpa menggulir. (3) Karena tes ini dijalankan di jendela utama, itu memicu penangan gulir yang sudah ada di tempatnya. ... (lanjutan)
hashchange

... Untuk alasan ini, tes anti peluru harus dijalankan di dalam iframe khusus (solves (3)) dengan konten yang cukup besar (solves (1)) yang diuji untuk scroll horizontal (solves (2)).
hashchange

Saya benar-benar harus menjalankan fungsi ini dalam skrip waktu untuk membuat ini memberikan hasil yang konsisten, jika tidak, terkadang kembali htmldan kadang kembali body. Ini kode saya setelah mendeklarasikan fungsinya. var scrollTopElement; setTimeout( function() { scrollTopElement = getScrollTopElement(); console.log(scrollTopElement); }, 1000); Terima kasih untuk ini, ini telah menyelesaikan masalah saya.
Tony M

Sudahlah, saya mencari tahu apa masalah sebenarnya. Jika Anda sudah berada di bagian bawah halaman saat Anda memuat ulang (f5), skrip ini akan kembali, htmlbukan bodyseperti yang seharusnya. Ini hanya jika Anda digulir sepanjang halaman web dan Anda memuat ulang, jika tidak berhasil.
Tony M

Ini bekerja untuk saya setelah saya menambahkan tanda centang untuk apakah halaman terbuka di bagian bawah.
scott

6

Saya menghabiskan waktu lama untuk mencari tahu mengapa kode saya tidak berfungsi -

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

Masalahnya ada di css saya -

body { height: 100%};

Saya mengaturnya sebagai autogantinya (dan dibiarkan khawatir tentang mengapa itu diatur 100%di tempat pertama). Itu memperbaikinya bagi saya.


2

Anda mungkin ingin menghindari masalah dengan menggunakan plugin - lebih khusus lagi, plugin saya :)

Serius, meskipun masalah dasar telah lama diatasi (browser yang berbeda menggunakan elemen yang berbeda untuk pengguliran jendela), ada beberapa masalah non-sepele di telepon yang dapat membuat Anda tersandung:

Saya jelas bias, tapi jQuery.scrollable sebenarnya adalah pilihan yang baik untuk mengatasi masalah ini. (Sebenarnya, saya tidak tahu ada plugin lain yang menangani semuanya.)

Selain itu, Anda dapat menghitung posisi target - salah satu yang Anda gulir ke - dengan cara peluru-bukti dengan yang getScrollTargetPosition()fungsi dalam inti ini .

Semuanya akan meninggalkan Anda

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

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

    return false;
}

1

Waspadai ini. Saya memiliki masalah yang sama, baik dengan Firefox atau Explorer

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

Jadi saya menggunakan seperti yang dikatakan David

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

Hebat itu berhasil, tetapi masalah baru, karena ada dua elemen, tubuh dan html, fungsi dieksekusi dua kali, ini adalah, lakukan X berjalan dua kali.

hanya mencoba dengan 'html', dan Firefox serta Explorer berfungsi, tetapi sekarang Chrome tidak mendukung ini.

Diperlukan tubuh untuk Chrome, dan html untuk Firefox dan Explorer. Apakah ini bug jQuery? tidak tahu

Berhati-hatilah dengan fungsi Anda, karena itu akan berjalan dua kali.


apakah kamu menemukan solusi untuk ini? Dan karena deteksi browser jquery sudah usang, saya tidak tahu bagaimana saya bisa menggunakan deteksi fitur untuk membedakan antara chrome dan firefox. Dokumentasi mereka tidak menentukan fitur yang ada di chrome dan bukan di firefox atau sebaliknya. :(
Mandeep Jain

2
bagaimana dengan .stop (benar, benar) .hidup?
Toni Michel Caubet

0

Saya akan merekomendasikan tidak mengandalkan bodyatau htmlsebagai solusi yang lebih portabel. Cukup tambahkan div di badan yang bertujuan untuk memuat elemen yang digulir dan gaya yang diinginkannya untuk mengaktifkan scrolling ukuran penuh:

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

(dengan asumsi display:block;dan top:0;left:0;default yang sesuai dengan tujuan Anda), lalu gunakan $('#my-scroll')untuk animasi Anda.


0

Ini adalah real deal. Ia bekerja di Chrome dan Firefox dengan sempurna. Sungguh menyedihkan bahwa beberapa orang yang bodoh memilih saya. Kode ini benar-benar berfungsi dengan baik seperti pada semua browser. Anda hanya perlu menambahkan tautan dan meletakkan id elemen yang ingin Anda gulir di href dan itu berfungsi tanpa menentukan apa pun. Kode yang dapat digunakan kembali dan andal murni.

$(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
Mungkin rumit, tetapi ini sangat berguna. Ini pada dasarnya berfungsi Plug-N-Play di semua browser. Solusi yang lebih sederhana mungkin tidak memungkinkan Anda melakukan itu.
drjorgepolanco

0

Saya mengalami masalah yang sama baru-baru ini dan saya menyelesaikannya dengan melakukan ini:

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

Dan youpi !!! ini bekerja di semua browser.

dalam kasus penentuan posisi tidak benar, Anda dapat mengurangi nilai dari offset (). berhenti dengan melakukan ini

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

Solusi ini sudah disebutkan dalam jawaban yang ada tetapi terima kasih telah berbagi
Toni Michel Caubet

Metode ini masih menghadapi kesalahan di Firefox, seperti yang tercantum dalam pertanyaan yang baru saja saya posting: stackoverflow.com/q/58617731/1056713
Chaya Cooper

-3

Bagi saya masalahnya adalah firefox secara otomatis melompat ke jangkar dengan nama-atribut sama dengan nama hash yang saya masukkan ke URL. Meskipun saya meletakkan .preventDefault () untuk mencegahnya. Jadi setelah mengubah atribut nama, firefox tidak secara otomatis melompat ke jangkar, tetapi melakukan animasi dengan benar.

@Toni Maaf jika ini tidak bisa dimengerti. Masalahnya adalah saya mengubah hash di URL seperti www.someurl.com/#hashname. Lalu saya punya misalnya jangkar yang ingin <a name="hashname" ...></a>jQuery gulir ke secara otomatis. Tapi itu bukan karena ia melompat ke jangkar dengan atribut nama yang cocok di Firefox tanpa animasi gulir. Setelah saya mengubah atribut nama menjadi sesuatu yang berbeda dari nama hash, misalnya ke name="hashname-anchor", pengguliran berhasil.


-5

Bagi saya, itu menghindari menambahkan ID pada titik animasi:

Menghindari:

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

Mempersiapkan id sebelumnya dan melakukan ini sebagai gantinya:

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

Diperbaiki dalam FF. (Tambahan css tidak membuat perbedaan bagi saya)


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

Semoga ini berhasil.


4
Bagaimana setTimeut membantu, di sini?
Toni Michel Caubet

2
@ToniMichelCaubet mungkin tujuannya adalah membuat animasi tidak sinkron. Animasi jQuery sudah asinkron, jadi ini tidak menghasilkan apa-apa.
mwcz

Bukan jawaban yang bijaksana untuk masalah ini. Tidak yakin apa yang akan dicapai ini.
erier
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.