Анімована прокруткаTop не працює у Firefox


164

Ця функція чудово працює. Він прокручує тіло до зрушення потрібного контейнера

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

Але не у Firefox. Чому?

-EDIT-

Для обробки подвійного тригера у прийнятій відповіді пропоную зупинити елемент перед анімацією:

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

2
Ваш остаточний код є найелегантнішим з усіх речей тут.
Лізардкс

Відповіді:


331

Firefox розміщує перелив у точці html рівні, якщо спеціально не розроблено поведінку.

Щоб змусити його працювати у Firefox, використовуйте

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

Робочий приклад

Рішенням CSS було б встановити такі стилі:

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

Я б припустив, що рішення JS було б найменш інвазивним.


Оновлення

Багато обговорень нижче присвячено тому, що анімація scrollTopдвох елементів спричинить зворотний виклик двічі. Функції виявлення браузера запропоновані та згодом застарілі, а деякі, мабуть, досить надумані.

Якщо зворотний виклик є безвідмовним і не вимагає великих обчислювальних потужностей, його запускання двічі може бути повною проблемою. Якщо декілька викликів зворотного дзвінка справді є проблемою, і якщо ви хочете уникнути виявлення функцій, можливо, більш прямолінійно встановити, що зворотний виклик запускається лише один раз із зворотного дзвінка:

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
Рішення JS має ваду! анімаційна функція виконується двічі, для html- елемента та для елемента body відповідно. Схоже, браузери Webkit використовують body, а решта використовує html . Це виправлення повинно зробити роботу$(jQuery.browser.webkit ? "body": "html").animate(...);
Андрейко

2
Я виявив, що рішення від Andreyco не працює в jQuery <1.4, який я використовував. Я був в змозі виправити це наступним чином : $(jQuery.browser.mozilla ? "html" : "body").animate(...);. Було б чудово використовувати не нюхати при перегляді, але виявляти функції. Не знаєте, як зробити функцію виявлення функцій у CSS
Rustavore

23
Зауважте, jQuery.browserзастарілий і відсутній в останній версії jQuery
spiderplant0

2
4 роки, і ця відповідь все ще виявляється корисною. Дуже дякую!
Метт

1
@DamFa: Головним чином, тому window.scrollщо не анімує . Ви, звичайно, можете згорнути власну річ з інтервалами та scrollBy. Якщо ви хочете додати полегшення до цього, у вас раптом є багато коду для написання досить незначного аспекту вашого продукту.
Девід Хедлунд

19

Виявлення функцій, а потім їх анімація на одному підтримуваному об'єкті було б непогано, але рішення в одному рядку не існує. Тим часом, ось як використати обіцянку робити один зворотний виклик за виконання.

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

ОНОВЛЕННЯ: Ось як можна замість цього використовувати функції виявлення функцій. Цей фрагмент коду потрібно оцінити перед анімаційним дзвінком:

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

Тепер використовуйте var, який ми щойно визначили як селектор для анімації прокрутки сторінки, і використовуйте звичайний синтаксис:

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

Чудово працює в звичайних умовах, дякую! Хоча слід пам’ятати про пару готчей. (1) Якщо в тілі недостатньо вмісту, щоб переповнити вікно, коли тест функцій запускається, вікно не прокручується, і тест повертає хибний результат "body". (2) Якщо тест запускається всередині iframe в iOS, він не працює з тієї ж причини. Iframes в iOS розширюються вертикально (не горизонтально), поки вміст не впишеться всередину, не прокручуючи. (3) Оскільки тест виконується в головному вікні, він запускає обробники прокрутки, які вже є. ... (продовження)
hashchange

... З цих причин пуленепробивальний тест повинен був проходити всередині спеціальної рамки (вирішує (3)) з достатньо великим вмістом (розв'язує (1)), яка перевіряється на горизонтальну прокрутку (вирішує (2)).
хеш-обмін

Мені фактично довелося запустити цю функцію в сценарії очікування, щоб це дало стійкі результати, інакше, здавалося, іноді повертається, htmlа іноді повертається body. Ось мій код після оголошення функції. var scrollTopElement; setTimeout( function() { scrollTopElement = getScrollTopElement(); console.log(scrollTopElement); }, 1000); Дякую за це, проте він вирішив мою проблему.
Тоні М

Неважливо, я зрозумів, що таке власне питання. Якщо ви знаходитесь внизу сторінки вже під час перезавантаження (f5), цей скрипт повернеться htmlзамість того, bodyяк передбачається. Це лише в тому випадку, якщо ви прокручуєте всю сторінку вниз і ви перезавантажуєтесь, інакше це працює.
Тоні М

Це працювало для мене після того, як я додав чек на те, чи відкриється сторінка внизу.
scott

6

Я витратив віки, намагаючись розібратися, чому мій код не працює -

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

Проблема була в моєму css -

body { height: 100%};

Я встановив це autoзамість цього (і мене хвилювало питання, чому це було встановлено 100%в першу чергу). Це зафіксувало це для мене.


2

Ви можете уникнути проблеми, використовуючи плагін - точніше, мій плагін :)

Серйозно, хоча основна проблема вже давно вирішена (різні браузери використовують різні елементи для прокрутки вікон), внизу лінії є досить багато нетривіальних проблем, які можуть вас подолати:

Я, очевидно, упереджений, але jQuery.scrollable - це насправді хороший вибір для вирішення цих проблем. (Насправді я не знаю жодного іншого плагіна, який би їх обробляв.)

Крім того, ви можете розрахувати кінцеву позицію - той , який ви виділите - в куленепробивний чином з в getScrollTargetPosition()функції в цій суттю .

Все це залишило б вас

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

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

    return false;
}

1

Остерігайтеся цього. У мене була така ж проблема, з якою не прокручувались Firefox або Explorer

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

Тому я використовував так, як сказав Девід

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

Чудово це спрацювало, але нова проблема, оскільки є два елементи, тіло та html, функція виконується двічі, це зробити, X виконувати два рази.

намагався лише з 'html', і Firefox та Explorer працюють, але зараз Chrome це не підтримує.

Так потрібні корпус для Chrome і HTML для Firefox та Explorer. Це помилка jQuery? не знаю.

Просто остерігайтеся своєї функції, оскільки вона буде працювати двічі.


Ви знайшли рішення для цього? А оскільки виявлення браузера jquery застаріле, я не знаю, як я можу використовувати функцію виявлення функцій для розмежування хрому та Firefox. У їхній документації не вказано особливість, яка присутня в хромі, а не у firefox чи viceversa. :(
Mandeep Jain

2
як щодо. стоп (правдивий, правдивий) .живий?
Тоні Мішель Каубе

0

Я б рекомендував не покладатися bodyні htmlна більш портативне рішення. Просто додайте в тіло діва, який має на меті містити прокручені елементи, та стилюйте його, як щоб увімкнути повну величину прокрутки:

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

(якщо припустити, що це display:block;і top:0;left:0;є типовими за замовчуванням), то використовуйте $('#my-scroll')для анімації.


0

Це справжня угода. Працює на Chrome і Firefox бездоганно. Навіть сумно, що деякі невігласи мене голосують. Цей код буквально чудово працює, як і у всіх браузерах. Вам потрібно лише додати посилання і ввести href в ідентифікатор елемента, який потрібно прокрутити, і він працює, не вказуючи нічого. Чистий багаторазовий і надійний код.

$(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
Це може бути складним, але це дійсно корисно. Це в основному працює Plug-N-Play у всіх браузерах. Більш просте рішення може не дати вам цього зробити.
drjorgepolanco

0

Я зіткнувся з тією ж проблемою зовсім недавно, і я вирішив її, зробивши це:

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

І youpi !!! він працює у всіх браузерах.

у випадку, якщо позиціонування не є правильним, ви можете відняти значення зі зміщення () .top, виконавши це

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

Це рішення вже згадується в існуючих відповідях, але дякую за те, що поділився
Тоні Мішель Каубе

Цей метод все ще зустрічається з помилками у Firefox, як зазначено у щойно опублікованому питанні: stackoverflow.com/q/58617731/1056713
Chaya Cooper

-3

Для мене проблема полягала в тому, що firefox автоматично перескакував на якір з атрибутом name, таким самим, як ім'я хешу, яке я вклав у URL. Хоча я поставив .preventDefault (), щоб запобігти цьому. Тож після зміни атрибутів імені, firefox не автоматично перескакував на якіри, а виконував анімацію правильно.

@Toni Вибачте, якщо це було не зрозуміло. Справа в тому, що я змінив хеші в URL-адресі, як-от www.someurl.com/#hashname. Тоді я мав, наприклад, якір, <a name="hashname" ...></a>до якого jQuery повинен прокручуватися автоматично. Але це не так, тому що він стрибнув прямо на якір із відповідним атрибутом імені у Firefox без будь-якої анімації прокрутки. Одного разу я змінив атрибут імені на щось відмінне від хеш-імені, наприклад name="hashname-anchor", на прокрутку працював.


-5

Для мене це уникало додавання ідентифікатора в точці анімації:

Уникання:

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

Заздалегідь підготуйте ідентифікатор і зробіть це замість цього:

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

Виправлено у ФФ. (Доповнення CSS не змінили для мене)


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

Сподіваюся, це працює.


4
Як тут допомагає setTimeut?
Тоні Мішель Каубе

2
@ToniMichelCaubet, мабуть, мета - зробити анімацію асинхронною. Але анімації jQuery вже асинхронні, тому це нічого не досягає.
mwcz

Не продумана відповідь на проблему. Не впевнений, що це досягне.
erier
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.