Подтвердить что ты не робот

Сортируемый список jQuery - полоса прокрутки вставляется при сортировке

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

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

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

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

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

Спасибо

4b9b3361

Ответ 1

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

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

Проблема, вы идете, чтобы перетащить элемент, который заставляет 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);
        }
    });
});

Ответ 2

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

Ответ 3

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

$('ul').height($('ul').height());

Ответ 4

Lol, похоже, что это еще не исправлено 4 года спустя. Здесь ticket.

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

Просто переместите заднюю линию 228. this._createPlaceHolder... перед строкой 208. this.helper.css("position", "absolute");

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

Ответ 5

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

Установка фиксированного значения высоты списка устраняет проблему, но делать это в момент создания списка слишком рано. Вы можете использовать .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');
    }
});

Этот код прекрасно работает для меня.

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

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

Желательно:

  • Слушайте сортируемое событие "start" и фиксируйте высоту сортируемого Контейнер
  • Слушайте сортируемое событие "stop" и установите высоту в auto (чтобы убедиться, что при изменении размера окна нет проблем)

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

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

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

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

Ответ 9

Похоже, что 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 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);
    }
});

Ответ 10

У меня была эта проблема в Chrome. И все сортируемые divs имели фиксированную высоту.

Проблема заключалась в свойстве css position: relative этих div. Когда div был вытащен и удален из DOM, его координаты были рассчитаны неправильно из-за относительного положения.

Inherit или absolute позиционирование должно использоваться для сортируемых элементов.

Ответ 11

Вот как я решаю проблему.

   $(".groups").sortable({
    ...
    helper: function(event, ui){
      lastScrollPosition =  $("body").scrollTop();
    },
    start: function(e, ui){
        // avoid scroll jump
        $("body").scrollTop(lastScrollPosition);
    },
   });

Ответ 12

Я загружал свои сортируемые divs VIA Ajax в обертку с автоматической высотой.

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

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

$("#wrapper").load("list.php",function() {
    $("#wrapper").css({ "height":"auto" });
    $("#wrapper").css({ "height": $("#wrapper").height() });
}

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

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

Если вам нужно добавить элементы или перезагрузить что-нибудь, просто вставьте эти две строки кода в функцию и вызовите функцию.

Надеюсь, это поможет кому-то!

Ответ 13

Вот еще один простой вариант решения этой проблемы.

  1. когда мышь входит в ваш значок сортировки,

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

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