jQuery Sortable List - 排序时滚动条跳起来

Posted

技术标签:

【中文标题】jQuery Sortable List - 排序时滚动条跳起来【英文标题】:jQuery Sortable List - scroll bar jumps up when sorting 【发布时间】:2010-12-16 15:53:25 【问题描述】:

我创建了一个演示:http://pastebin.me/584b9a86d715c9ba85b7bebf0375e237

当滚动条位于底部并且您拖动项目以对其进行排序时,它会导致滚动条向上跳动。它似乎在 FF、Safari、Chrome 和 IE(至少 IE8)中执行此操作。

在我 Mac 上的 Firefox 中,当滚动条位于底部时它会向上跳,但也会在整个列表中添加一个漂亮的 Flash。它只是在向上滚动时闪烁整个列表。我相信,如果我找出导致向上滚动的原因(如果我能阻止它),闪烁也会停止。

我不喜欢它像 b/c 那样跳起来,我觉得它很刺耳和令人困惑。我知道这有点极端,但如果可以的话,我想解决它。

那么,有什么办法可以防止它向上滚动吗?或者,是什么导致它向上滚动?

谢谢

【问题讨论】:

我在 FF 3.6 中,我没有看到任何真正的问题。如果我抓住底部的并将其向上拖动,则该框不会滚动,直到它到达顶部。 在弄乱javascript之前尝试this solution 【参考方案1】:

问题很简单。

首先让我们设置场景。您有一个可排序元素列表,您的页面高于屏幕,因此它有一个滚动条,您的页面位于最底部,滚动条一直向下。

问题,你去拖动一个元素,这会导致 jQuery 从 dom 中删除它,然后添加一个占位符。让我们放慢速度,它首先删除元素,现在列表更短导致页面更短,导致滚动条更短。然后它添加了占位符,现在页面更长了,现在滚动条更长了(但窗口不会向下滚动,所以看起来滚动条向上跳了)。

我发现解决此问题的最佳方法是确保可排序列表具有静态高度,因此删除元素不会导致它变短。这样做的一种方法是

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

将在创建可排序列表时调用上述内容,将其高度静态设置为当前高度。

如果您在其中一个可排序元素中有图片,除非您在标签中预先定义尺寸,否则上述代码将不起作用。原因是当 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);
        
    );
);

【讨论】:

这段代码也解决了我的问题。谢谢你。但是这个修复还有另一个问题。如果可排序列表具有删除行选项。从列表中删除单行后,由于列表的高度是固定的,因此会留下空间。 还有一个问题。如果可排序列表不可见,则无法修复此问题。例如:可排序列表位于非活动选项卡中。 这里也一样,确保在调整窗口大小时也调整大小,尤其是对于响应式网站。因为内容高度可能会改变。但是 +1 用于处理图像案例。 我写了一个补充答案来处理窗口调整大小的情况。 老大.. 你让我开心!!非常感谢【参考方案2】:

这是解决此问题的另一个简单方法。

    当鼠标进入你的排序图标时,

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

    当鼠标离开你的排序图标时

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

【讨论】:

如果你发现切换 overflow-y 会影响你的容器宽度,试试 ::-webkit-scrollbar width: 0px; 【参考方案3】:

所以,复杂!您所要做的就是将overflow: auto; 添加到父级(ul,#element,whatever_you_call_it),它应该可以工作。这样可以确保即使文档的高度略有变化,在调整大小时,溢出自动将保留元素的高度。

【讨论】:

这个问题我找了很久,你的回答解决了我的问题,谢谢【参考方案4】:

我正在通过 Ajax 将可排序的 div 加载到具有自动高度的包装器中。

我没有尝试链接到可排序的事件,而是在加载和更改列表时将高度设为自动,然后将其设置为静态高度。

Vit 的解决方案类似,但我不想通过在每个 mousedownmouseup 事件上执行此操作来增加不必要的开销。相反,我只是在加载时列表发生变化或添加新元素时执行此操作(我使用slideDown 动画并在完成时将高度设置为静态)。

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

当我重新加载数据时,其他解决方案对我不起作用,因为我只在页面加载期间使它们可排序一次。

我的列表/div 会随着下拉列表而变化,因此我可以选择不同的列表,并且我的包装器会相应地调整大小,而不会出现烦人的跳转。

如果您需要添加元素或重新加载任何内容,只需将这两行代码放入一个函数并调用该函数即可。

希望这对某人有所帮助!

【讨论】:

【参考方案5】:

Carl M. Gregory 完美地解释了这个问题,但我认为他的解决方案过于复杂。

将列表高度设置为固定值可以解决问题,但在创建列表时这样做还为时过早。您可以使用.mousedown() 函数在排序开始之前设置高度。此时任何可能的图像都已加载,因此无需检查它们的大小。

所以跳转不会发生,但现在请考虑一下:您有多个已连接列表,它们是垂直排列的。现在发生了这种情况:

    您从上方列表开始拖动 上方列表现在具有固定高度 将项目移至底部列表 底部列表调整得很好,因为它的高度是auto 虽然上面的列表仍然具有相同的固定高度它应该短一个项目

所以在某些时候,您应该将其高度返回到auto。什么时候?在mouseup 上为时已晚(请参阅https://jsfiddle.net/937vv4tx/1/),但没关系,您几乎可以在跳跃不再构成威胁后立即返回正确的值。使用start() 事件

更新: 但是您仍然希望在 .mouseup() 上将高度设置为 auto,因为如果您只是单击项目而不开始拖动,它仍然会设置固定高度,因为 @987654329 @已经发生了。

$('.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】:

Carl M. Gregory 非常清楚地解释了“为什么会这样”。

但固定高度似乎还不够,因为当您调整窗口大小时,内部内容可能会变得更高。

我希望能做到的事:

    监听可排序的“开始”事件,并固定可排序的高度 容器 监听可排序的“停止”事件,并将高度设置回自动(以确保调整窗口大小时没有问题)

实际发生的情况:跳转发生在我们捕捉到开始事件之前,所以不能使用这个

解决方法: 监听可排序项目上的 jquery mousedown 和 mouseup 事件。这很简单。

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

我建议您仍然考虑 Carl 关于图像加载的观点。他的回答值得一读。

【讨论】:

【参考方案7】:

似乎 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);
    
);

【讨论】:

这感觉有点像 hack,因为我必须设置一个半全局变量,但这是在这个线程上唯一对我有用的东西。我正在使用隐藏内容的横向滚动 div,因此大多数其他解决方案不适用于我。我也在使用 rails-ui gem,所以我无法编辑核心文件。谢谢! 我和你有过类似的情况,想出了这个主意。我记得几天来没有任何运气的情况下查看了大量的解决方案。我很高兴这节省了其他人的时间。【参考方案8】:

这就是我解决问题的方法。

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

【讨论】:

【参考方案9】:

大声笑,看起来这个在 4 年后仍然没有修补。这是ticket。

添加到@Carl M. Gregory 的答案,为了充分利用动态容器而不是将其设置在固定高度,我将编辑 jquery.ui.sortable.js 核心文件。

只需将第 228 行移回即可。this._createPlaceHolder... 在第 208 行之前。this.helper.css("position", "absolute");

感谢 jquery 票务门户上的 Honda。我正在使用 Sortable.js 版本 1.10.3。

【讨论】:

如果有人使用其他版本,甚至是自定义的 jQuery UI 构建(具有不同的行号),只需查找 Sortable 小部件代码并在函数 _mouseStart() 中将行 this._createPlaceHolder... 移到前面线this.helper.css("position", "absolute");. 嗯……虽然没有以前那么高了,但仍然有一个小的跳跃。【参考方案10】:

我在 Chrome 中遇到了这个问题。所有可排序的 div 都有固定的高度。

问题出在这些 div 的 css 属性 position: relative 中。 div从DOM中拖拽移除时,由于相对位置,坐标计算错误。

Inheritabsolute 定位应该用于可排序元素。

【讨论】:

【参考方案11】:

我在使用 div 容器和 div 元素时遇到了同样的问题,并且设置容器高度对我没有帮助,但是在包含的 div 上显式设置 overflow:auto css 属性确实解决了这个问题。不过只在 Chrome 中测试过。

【讨论】:

“你是我的救星,伙计!”它工作得很好。 (也适用于 Firefox) 我添加“溢出:自动;”到我的“ol”容器中,它可以工作。谢谢!! 接受的答案对我不起作用,但确实如此。谢谢。 (是的,2019 年还在使用 jQuery UI)【参考方案12】:

感谢您的出色解决方案 但我建议改用以下内容:

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

【讨论】:

对于 cmets 来说有点晚了,但我也更喜欢这种方法……尤其是当您动态添加可排序元素时。 +1 这是什么?根本不使用resize【参考方案13】:

我也遇到过这个问题。当列表指示页面的长度时,就会发生这种情况。当您开始排序时,列表高度会在一瞬间发生变化,从而导致页面跳转。这是一个奇怪但非常实用的解决方案:

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

【讨论】:

同意。问题发生的原因解释清楚,修复工作完美,快速简单。 @Matt 页面结束,当文档为ready。 这是一个不错的解决方案,但请记住,调整窗口大小时,您的“ul”内部内容可能会变高。在这种情况下,您可能需要监听窗口“调整大小”事件并相应地设置“ul”高度。

以上是关于jQuery Sortable List - 排序时滚动条跳起来的主要内容,如果未能解决你的问题,请参考以下文章

Jquery-ui Sortable with js list maker from array 无法正常工作

jQuery Draggable + Sortable - 如何拒绝放入排序?

jQuery sortable live() 不排序

带有动画的 jQuery 可排序

排序前后的jQuery sortable get .index()?

jQuery UI Droppable and Sortable - 放置在正确的排序位置