jQuery Sortable List - полоса прокрутки подскакивает при сортировке - PullRequest
26 голосов
/ 14 ноября 2009

Я создал демо: http://pastebin.me/584b9a86d715c9ba85b7bebf0375e237

Когда полоса прокрутки находится внизу и вы перетаскиваете элементы для их сортировки, полоса прокрутки поднимается вверх. Похоже, это делается в FF, Safari, Chrome и IE (по крайней мере, IE8).

В Firefox на моем Mac, он делает прыжок вверх, когда полоса прокрутки находится внизу, но также добавляет приятную вспышку ко всему списку. Он просто высвечивает весь список прямо при прокрутке. Я считаю, что если я выясню, что вызывает прокрутку вверх (и если я смогу остановить ее), то мигание также прекратится.

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

Итак, есть ли способ предотвратить его прокрутку? С другой стороны, что заставляет его прокручиваться вверх?

Спасибо

Ответы [ 13 ]

46 голосов
/ 19 января 2011

Проблема очень проста.

Сначала давайте настроим сценарий. У вас есть список сортируемых элементов, ваша страница выше экрана, поэтому у нее есть полоса прокрутки, ваша страница находится в самом низу, полоса прокрутки полностью вниз.

Проблема, вы идете, чтобы перетащить элемент, который заставляет jQuery удалить его из DOM, а затем добавить в качестве заполнителя. Давайте замедлим его, сначала он удаляет элемент, теперь список короче, а страница короче, а полоса прокрутки короче. Затем он добавляет заполнитель, теперь страница длиннее, теперь полоса прокрутки длиннее (но окно не прокручивается вниз, поэтому полоса прокрутки прыгнула вверх).

Лучший способ противодействовать этому - убедиться, что сортируемый список имеет статическую высоту, поэтому удаление элемента не приводит к его сокращению. Один из способов сделать это будет

create:function(){
    jQuery(this).height(jQuery(this).height());
}

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

Если у вас есть изображения в одном из сортируемых элементов, приведенный выше код не будет работать, если вы предварительно не определите размеры в теге. Причина в том, что когда вызывается и устанавливается высота (), он вычисляется до заполнения изображения. Нет измерения, тогда изображение не имеет места. Как только изображение загрузится, оно займет место.

Вот способ обойти определение размеров. Просто изменяйте высоту списка каждый раз при обнаружении загружаемого изображения.

create:function(){
    var list=this;
    jQuery(list).find('img').load(function(){
        jQuery(list).height(jQuery(list).height());
    });
}

Финальная версия:

jQuery(document).ready(function(){  
    jQuery("div#todoList").sortable({
        handle:'img.moveHandle',
        opacity: 0.6,
        cursor: 'move',
        tolerance: 'pointer',
        forcePlaceholderSize: true,
        update:function(){
            jQuery("body").css('cursor','progress');
            var order = jQuery(this).sortable('serialize');
            jQuery.post("/ajax.php?do=sort&"+order,function(data){jQuery("body").css('cursor','default');});
        },
        create:function(){
            var list=this;
            var resize=function(){
                jQuery(list).css("height","auto");
                jQuery(list).height(jQuery(list).height());
            };
            jQuery(list).height(jQuery(list).height());
            jQuery(list).find('img').load(resize).error(resize);
        }
    });
});
20 голосов
/ 28 марта 2013

У меня была такая же проблема, когда я использовал контейнер div и элементы div, и установка высоты контейнера мне не помогла, но явная установка переполнения: свойство auto css для содержащего div действительно решило проблему. Только проверено в Chrome.

13 голосов
/ 15 ноября 2010

Я тоже столкнулся с этой проблемой. Это происходит, когда список определяет длину вашей страницы. Когда вы начинаете сортировку, высота списка изменяется на доли секунды, вызывая скачок страницы. Вот странное, но очень функциональное решение этой проблемы:

$('ul').height($('ul').height());
5 голосов
/ 15 сентября 2015

Карл М. Грегори прекрасно объясняет проблему, но я думаю, что его решение неоправданно сложно.

Установка высоты списка на фиксированное значение устраняет проблему, но делать это в момент создания списка слишком рано. Вы можете использовать функцию .mousedown(), чтобы установить высоту непосредственно перед началом сортировки. К этому времени любые потенциальные изображения уже загружены, поэтому нет необходимости проверять их размеры.

Таким образом, переход не происходит, но теперь, пожалуйста, учтите это: у вас есть несколько связанных списков, и они расположены вертикально. Теперь это происходит:

  1. Вы начинаете перетаскивать из верхнего списка
  2. Верхний список теперь имеет фиксированную высоту
  3. Переместить элемент в нижний список
  4. Нижний список хорошо настраивается, потому что его высота равна auto
  5. Верхний список по-прежнему имеет такую ​​же фиксированную высоту, хотя он должен быть на один пункт короче

Так что в какой-то момент вы должны вернуть его высоту обратно к auto. Когда? На mouseup слишком поздно (см. https://jsfiddle.net/937vv4tx/1/),, но все в порядке, вы можете вернуть правильное значение почти сразу после того, как прыжок больше не является угрозой. Использование события start()

Обновление: Но вы все равно хотите установить высоту auto на .mouseup(), потому что если вы просто нажмете на элемент и не начнете перетаскивать, он все равно установит фиксированную высоту, потому что .mousedown() уже произошло.

$('.sortable').mousedown(function() {
    // set fixed height to prevent scroll jump
    // when dragging from bottom
    $(this).height($(this).height());
}).mouseup(function () {
    // set height back to auto 
    // when user just clicked on item
    $(this).height('auto');
}).sortable({
    connectWith: '.sortable',
    placeholder: 'placeholder',
    start: function() {
        // dragging is happening
        // and scroll jump was prevented,
        // we can set it back to auto
        $(this).height('auto');
    }
});

Этот код отлично работает для меня.

5 голосов
/ 03 января 2014

Лол, похоже, это все еще не исправлено 4 года спустя. Вот билет .

Если добавить ответ @Carl M. Gregory, чтобы полностью использовать преимущества динамического контейнера вместо установки его на фиксированной высоте, я бы отредактировал файл ядра jquery.ui.sortable.js .

Просто вернитесь назад на 228. this._createPlaceHolder... до строки 208. this.helper.css("position", "absolute");

Благодаря Honda на портале билетов jquery. Использую Sortable.js версии 1.10.3.

3 голосов
/ 10 марта 2017

Итак, комплекс! Все, что вам нужно сделать, это добавить overflow: auto; к родителю (ul, #element, what_you_call_it), и это должно работать. Это гарантирует, что даже если высота документа немного изменится, что при изменении размера авто переполнение сохранит высоту элемента.

3 голосов
/ 17 октября 2011

Спасибо за отличное решение но я рекомендую использовать следующее вместо:

create:function(){
    var list=this;
    resize=function(){
        jQuery(list).css("min-height","0");
        jQuery(list).height(jQuery(list).height());
    };
    jQuery(list).css('min-height', jQuery(list).height());
}
2 голосов
/ 09 сентября 2015

Карл М. Грегори очень четко объясняет «почему это происходит».

Однако фиксации высоты недостаточно, поскольку при изменении размера окна внутреннее содержимое может стать выше.

Что бы я хотел сделать:

  1. Прослушайте сортируемое событие 'start' и установите высоту сортируемого контейнер
  2. Прослушайте сортируемое событие «стоп» и установите для высоты значение auto (чтобы убедиться в отсутствии проблем при изменении размера окна)

Что происходит на практике: Скачок происходит до того, как мы поймаем стартовое событие, поэтому его нельзя использовать

Обходной путь: прослушивание событий jquery mousedown и mouseup для сортируемых элементов. Это очень просто.

$(".sortable_item").mousedown(function(){
    $(".sortable_container").height($(".sortable_container").height());
}).mouseup(function(){
    $(".sortable_container").height('auto');
});

Я предлагаю вам еще учесть мнение Карла о загрузке изображений. Его ответ стоит прочитать.

1 голос
/ 25 августа 2015

Похоже, что jQuery UI 1.9.2 решил проблему.

Если вы не можете изменить библиотеку, есть обходной путь, включающий простую операцию полосы прокрутки. Идея проста:

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

Вот, пожалуйста;

var lastScrollPosition = 0; //variables defined in upper scope
var tempScrollPosition = 0;

window.onscroll = function () { // Up to you requirement you may change it to another elemnet e.g $("#YourPanel").onscroll
    clearTimeout($.data(this, 'scrollTimer'));
    $.data(this, 'scrollTimer', setTimeout(function () {
        tempScrollPosition = lastScrollPosition; // Scrolls don't change from position a to b. They cover some numbers between a and b during the change to create a smooth sliding visual. That's why we pick the previous scroll position with a timer of 250ms.
    }, 250));

    lastScrollPosition = $(document).scrollTop(); // Up to you requirement you may change it to another elemnet e.g $("#YourPanel").onscroll
};

$("#YourSortablePanel").sortable({
    start: function (event, ui) {
        $(document).scrollTop(tempScrollPosition);
    }
});
0 голосов
/ 13 сентября 2018

Вот еще один простой способ решить эту проблему.

  1. при наведении мыши на значок сортировки,

    $("body").css("overflow-y","hidden");
    
  2. когда мышью оставь свой значок сортировки

    $("body").css("overflow-y","scroll");
    
...