jQuery可排序列表 – 滚动条在排序时跳转

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

当滚动条位于底部并拖动项目以对其进行排序时,会导致滚动条向上跳跃。 它似乎在FF,Safari,Chrome和IE(至少IE8)中这样做。

在我的Mac上的Firefox中,当滚动条位于底部时它会跳起来,但也会为整个列表添加一个漂亮的闪存。 它只是在向上滚动整个列表时闪烁。 我相信,如果我弄清楚是什么导致向上滚动(如果我可以阻止它),闪烁也将停止。

我不喜欢它像那样跳起来b / c我发现它很刺耳和混乱。 我知道这是一个角落的案例,但如果可以,我想解决它。

那么,有没有办法阻止它向上滚动? 或者,是什么导致它向上滚动?

谢谢

问题很简单。

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

问题是,你去拖动一个元素,导致jQuery从dom中删除它然后添加一个占位符。 让它慢下来,它首先删除元素,现在列表更短,导致页面更短,导致滚动条更短。 然后它添加了占位符,现在页面更长,现在srollbar更长(但是窗口没有向下滚动,所以滚动条似乎跳了起来)。

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

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

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

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

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

大声笑,这样看起来仍然没有修补4年后。 这是票 。

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

简单地向后移动第228行this._createPlaceHolder...在第208行之前this.helper.css("position", "absolute");

感谢本田在jquery门票网站上。 我使用的是Sortable.js版本1.10.3。

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

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

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

  1. 您从上面的列表开始拖动
  2. 上面的列表现在有固定的高度
  3. 将项目移动到底部列表
  4. 底部列表很好地调整,因为它的高度是auto
  5. 上面的列表仍然具有相同的固定高度,但它应该是一个较短的项目

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

更新:但你仍然希望在.mouseup()上设置auto高度,因为如果你只是点击项目并且不开始拖动,它仍然设置固定高度,因为.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'); } }); 

这段代码非常适合我。

感谢您提供了出色的解决方案,但我建议您使用以下内容:

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

Carl M. Gregory非常清楚地解释了“为什么会发生这种情况”。

然而,修复高度似乎不够,因为当您调整窗口大小时内部内容会变得更高。

我希望可以做到:

  1. 收听可排序的“开始”事件,并修复可排序容器的高度
  2. 收听可排序的“停止”事件,并将高度设置为自动(以确保在调整窗口大小时没有问题)

在实践中会发生什么:跳转发生在我们捕获开始事件之前,因此无法使用

解决方法:在可排序项上侦听jquery mousedown和mouseup事件。 这很直截了当。

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

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

所以,复杂! 你所要做的就是添加overflow: auto; 到父(ul,#element,whatever_you_call_it),它应该工作。 这样可以确保即使文档稍微改变高度,在resize时,溢出自动也会保留元素的高度。

好像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 eg $("#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 eg $("#YourPanel").onscroll }; $("#YourSortablePanel").sortable({ start: function (event, ui) { $(document).scrollTop(tempScrollPosition); } }); 

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

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

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

这就是我如何解决这个问题。

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

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

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

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

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

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

我的list / div随着下拉列表的变化而变化,因此我可以选择不同的列表,并且我的包装器会相应resize而不会产生烦人的跳转。

如果你需要添加元素或重新加载任何东西,只需将这两行代码抛入一个函数并调用该函数。

希望这有助于某人!

这是解决此问题的另一个简单选择。

  1. 当mouseenter你的排序图标,

     $("body").css("overflow-y","hidden"); 
  2. 当mouseleave你的排序图标

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