Проблема с прокруткой в ​​опере

Обновление: я оставил следующий код javascript, чтобы показать, как проблема развивалась с течением времени, но теперь оказалось, что это НЕ актуально, поскольку javascript не является проблемой. Пожалуйста, взгляните на код html/css ниже.

Для обзорной страницы с большим меню я реализовал следующую функцию (большую часть которого я украл отсюда):

function isScrolledIntoView(elem)
{
    var docViewTop = $(window).scrollTop();
    var docViewBottom = docViewTop + $(window).height();

    var elemTop = $(elem).offset().top;
    var elemBottom = elemTop + $(elem).height();

    alert(elemBottom);

    return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom));
}

$(document).ready(function(){
 var overview = $('#overview');
 var active = $('#active-item');
 if (!isScrolledIntoView(active))
 {
  $('#overview li.active-parent').each(function(index, value){
   if (!isScrolledIntoView(active)) overview.scrollTo(value);
  });
 }
 if (!isScrolledIntoView(active)) overview.scrollTo(active);
});

Идея состоит в том, что после загрузки каждой страницы меню, содержащее его, прокручивается до позиции, где виден текущий активный элемент. Предпочтительно, прокручивая до его первого родителя (элементы меню находятся в дереве), в противном случае до самого элемента.

Теперь это прекрасно работает в Firefox и Chrome (и никто из разработчиков Apple не жаловался мне), но Opera делает действительно странную вещь: прокручивается вниз до нужного элемента, затем очень короткая пауза перед прокруткой до конца снова

У кого-нибудь есть идеи

  1. Что происходит, и
  2. Как я могу это остановить?

Спасибо,


Обновление: я тестирую версию 10.63 на Linux (Fedora)


Обновление: похоже, я искал совершенно не в том направлении. Проблема связана с CSS и может быть воспроизведена с помощью следующего кода:

<html>

<head>

<title>Opera scroll test</title>

  <style>
    .main:after
    {
      content: 'abc';
    }

    :focus
    {
      padding: 0px;
    }

    #overview
    {
      display: block;
      float: left;
      width: 219px;
      height: 500px;
      overflow: auto;

    }
  </style>

</head>

<body>

  <div id="main" class="main">

    <div id="overview">
      <ul>
        <?
        for($i = 1; $i < 100; $i++)
          echo '<li>'.$i.'</li>';
        ?>

      </ul>
    </div>
    <div>

      <p>123</p>

    </div>

  </div>

</body>
</html>

Теперь, если вы прокрутите панель навигации вниз и переместите указатель мыши вправо (над панелью содержимого), прокрутка панели навигации будет сброшена.

Извините, что потратил время на охоту за javascript :(

Если есть гуру css, которые знают, как это исправить, или кто может просто объяснить, что происходит, я был бы очень признателен.


Обновление: протестировал приведенный выше код в Windows на Opera 10.63. Такое же странное поведение происходит.


Обновление: сообщил об этом в Opera как об ошибке. Интересно, что будет...


person Michael Clerx    schedule 26.10.2010    source источник
comment
Можете ли вы объяснить, почему вы не используете для этого собственный API scrollIntoView()?   -  person Pointy    schedule 28.10.2010
comment
Впервые слышу об этом... Сейчас попробовал, в опере такое же поведение   -  person Michael Clerx    schedule 28.10.2010
comment
ОПЕРНАЯ ВЕРСИЯ ОТСУТСТВУЕТ. jsFiddle был бы полезен. И совет: попробуйте простой oneliner с scrollTo(), просто чтобы посмотреть, работает ли он.   -  person naugtur    schedule 29.10.2010
comment
Кажется, это происходит во многих случаях: проблема не сильно связана с точным кодом, опубликованным выше.   -  person Michael Clerx    schedule 29.10.2010
comment
почему бы вам не попробовать протестировать разные версии Opera / OS...? ваш код выглядит нормально.   -  person sdadffdfd    schedule 02.11.2010
comment
Пробовал на винде, там тоже самое...   -  person Michael Clerx    schedule 02.11.2010
comment
Привет, Майкл, как сказал Стивен де Салас, проблема может быть связана с наличием другого фрагмента кода в вашем исходном коде, который может мешать! Можете ли вы подтвердить эту теорию, проверив ее, изолировав свой код и посмотрев, работает ли она!?   -  person Luca Filosofi    schedule 03.11.2010
comment
Привет, aSeptik, пожалуйста, взгляните на бесплатную версию javascript, только css для той же ошибки в моем обновленном посте. Я добавил этот код 2 дня назад, но теперь я понимаю, что сообщение не очень ясно. попробую обновить. Спасибо!   -  person Michael Clerx    schedule 03.11.2010


Ответы (4)


Не уверен, почему, но проблема, похоже, напрямую связана с настройкой .main:after content CSS. Вы можете использовать jQuery? Если да, то если вы закомментируете или удалите параметр CSS .main:after и замените его следующим скриптом в теге <head>, вы получите тот же визуальный результат без странной проблемы с прокруткой:

<script type="text/javascript">
  $(document).ready(function(){
    $(".main").append("abc");
  });
</script>
person 300 baud    schedule 03.11.2010
comment
Спасибо! Я предложу это нашему парню css :) - person Michael Clerx; 03.11.2010
comment
Если вы прокрутите обзорный div, а затем переместите курсор вправо за пределами этого div, но оставите курсор внутри основного div (например, над содержимым 123 или abc), тогда странной прокрутки вверх не произойдет. Это происходит только тогда, когда вы перемещаете курсор за пределы основного div. Это странное поведение не происходит, если содержимое abc не применяется через ключевое слово содержимого CSS. Поэтому мне кажется, что проблема в том, как Opera обрабатывает ключевое слово контента CSS. - person 300 baud; 04.11.2010
comment
Странный. Ну, я сообщил об этом в Opera как об ошибке. Большое спасибо за ваше время, извините, я был слишком поздно, чтобы дать вам награду. Я надеялся, что кто-то, кто имел дело с этой проблемой раньше, все же появится :) - person Michael Clerx; 05.11.2010

У меня есть представление о том, что происходит, однако я попробовал ваш код и пока не могу воспроизвести проблему. Хром и Опера ведут себя одинаково.

Это HTML, который я пробовал:

<div id="overview" style="height: 300px;overflow-y: scroll;">
 <ul>   
  <li class="item" style="height: 400px;">item 1</li>
  <li class="item" style="height: 400px;">item 2 </li>
  <li class="active-parent" style="height: 400px;">active-parent<br/><br/>
    <ul>
      <li id="active-item" style="height: 300px;">active-item</li>
    </ul>
  </li>
 </ul>
</div>

Что касается того, почему это не работает в Opera: какую версию Opera вы используете? Я попробовал 10.63 (последнюю), и с ней все в порядке.

Поскольку я не могу воспроизвести проблему, я собираюсь сделать слепой снимок:

Попробуйте ввести небольшую задержку, чтобы этот код выполнялся после всего остального в очереди:

$(document).ready(function() {
  window.setTimeout(function() {
    // Your code goes here
  }, 500);
});

ПОСЛЕДНЕЕ ДОБАВЛЕНИЕ:

В ответ на ваше заявление: «Я действительно хотел бы понять, что здесь происходит. Почему задержка? И должна ли задержка быть больше на более медленных компьютерах?»:

Если установка задержки устраняет вашу проблему, то причина ясна: есть какой-то другой фрагмент кода, мешающий функции «прокрутки вверх», которая выполнялась после нее. Установив задержку в 500 мс, вы гарантируете, что ваш код запускается после каждого другого фрагмента Javascript, который выполняется при загрузке документа. Это может быть либо что-то, что вы явно добавили в очередь функций jQuery, когда документ загружает (используя $(document).ready(function(){) или неявно добавляется Opera в виде «виджета» (установлены ли у вас какие-либо инструменты перевода, панели инструментов Google и т. д.?).

Что касается того, должна ли задержка быть больше для более медленных компьютеров, я лично так не думаю, ключ в том, что задержка заставляет ваш код выполняться ПОСЛЕ КАЖДОГО ДРУГОГО СКРИПТА, запускаемого событием document.onload. Поскольку я подозреваю, что все, что мешает, выполняется в прямой последовательности после загрузки документа, вам действительно не нужна очень большая задержка, даже 50 мс может потребоваться, ключ в том, что задержка заставляет ваш код идти в конец очереди.

Надеюсь, это поможет прояснить ситуацию.

person Steven de Salas    schedule 29.10.2010
comment
Спасибо, Стивен, я тоже использую версию 10.63. - person Michael Clerx; 30.10.2010
comment
Спасибо за все ваши усилия, Стивен! - person Michael Clerx; 01.11.2010

Как упомянул Стивен де Салас, добавление задержки помогает -> $(window).scrollTop() возвращает 0, если не задерживается!!

Я немного поиграл и придумал такой пример:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Scrolling Test</title>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js" type="text/javascript"></script>
<style type="text/css">
.parent { margin-bottom:100px; border:1px solid red; }
</style>

<script type="text/javascript">

(function ($){
    $.fn.extend({
        viewport : function(){
            if (this.length > 0) {

                var element = $(this.get(0)),
                pad = $.fn.viewport.PADDING,
                vp = {
                    height : element.height() + 2 * pad,
                    top : element.offset().top - pad,
                    docHeight : $(window).height(),
                    docTop : $(window).scrollTop(),
                    completelyInViewport : false
                };
                vp.bottom = vp.top + vp.height + pad;
                vp.docBottom = vp.docTop + vp.docHeight;
                vp.fitsInViewport = vp.height <= vp.docHeight;

                if (vp.top > vp.docTop && vp.bottom < vp.docBottom) {
                    vp.completelyInViewport = true;
                }

                return vp;
            }

            return null;
        }
    });

    $.fn.extend($.fn.viewport, {
        PADDING: 10, // ADJUST TO YOUR NEEDS
        LARGE_PARENT_BEHAVIOR: "bottom" // if parent list is bigger than viewport and 
                                        // the active item is not in the first page of 
                                        // the parents list, where should it be shown
                                        // possible: "bottom", "middle" or "top"
    });

    $.extend({
        ensureViewport: function(element, parent) {
            var e_vp = element.viewport(),
            p_vp = parent.viewport();

            if (null == e_vp || null == p_vp) {
                return;
            }

            if (!p_vp.completelyInViewport) {
                if (p_vp.fitsInViewport || e_vp.bottom - p_vp.top <= e_vp.docHeight) {
                    doScroll(p_vp.top);
                } else {
                    switch($.fn.viewport.LARGE_PARENT_BEHAVIOR) {
                        case "top":
                            doScroll(e_vp.top);
                            break;
                        case "middle":
                            doScroll(e_vp.top - (e_vp.docHeight - e_vp.height)/2);
                            break;
                        case "bottom":
                        default:
                            doScroll(e_vp.bottom - e_vp.docHeight);
                            break;
                    }
                }
            }

            function doScroll(y){
                window.scrollTo(0, y);
                // you could implement instead some sort of smooth scroling mechanism here
                // e.g. http://github.com/kswedberg/jquery-smooth-scroll
            }
        }
    });

    $(function(){
        window.setTimeout(function(){
            var item = $("li.active-item");
            if (item.size() > 0) {
                $.ensureViewport(item, item.closest("li.parent"));
            }
        }, 0);
    });
})(jQuery);

</script> 

</head>
<body>

<div id="overview">
 <ul>
  <li class="parent">
   parent 1
   <ul class="item"><li>item 1</li><li>item 2</li></ul>
  </li>
  <li class="parent">
   parent 2
   <ul class="item"><li>item 1</li><li>item 2</li></ul>
  </li>
  <li class="parent">
   parent 3
   <ul class="item"><li>item 1</li><li>item 2</li></ul>
  </li>
  <li class="parent">
   parent 4
   <ul class="item"><li>item 1</li><li class="active-item">item 2</li></ul>
  </li>
  <li class="parent">
   parent 5
   <ul class="item"><li>item 1</li><li>item 2</li></ul>
  </li>
  <li class="parent">
   parent 6
   <ul class="item"><li>item 1</li><li>item 2</li></ul>
  </li>
  <li class="parent">
   parent 7
   <ul class="item"><li>item 1</li><li>item 2</li></ul>
  </li>
 </ul>
</div>

</body>
</html>

Протестировал на Опере 10.63, там тоже работает.

Ваше здоровье

person roberkules    schedule 31.10.2010
comment
Спасибо за ответ! Хотя я действительно хотел бы понять, что здесь происходит. Почему задержка? И должна ли задержка быть больше на более медленных компьютерах? - person Michael Clerx; 31.10.2010

Это всего лишь теория, но она может помочь вам начать.

Когда вы нажимаете назад в браузере, он часто возвращает вас к тому месту на странице, где вы в последний раз просматривали.

Возможно ли, что опера пытается довести вас до той точки, которая мешает вашему коду?

Интересно, если вы очистите свою историю / кеш, убьете процесс оперы и начнете с нового процесса. Имеет ли это какое-то значение.

person Alex KeySmith    schedule 01.11.2010
comment
Спасибо! Хорошая идея, но безрезультатно :) - person Michael Clerx; 01.11.2010