jQueryUI 可排序和拖动速度问题

Posted

技术标签:

【中文标题】jQueryUI 可排序和拖动速度问题【英文标题】:jQueryUI sortable and drag speed issue 【发布时间】:2019-08-05 23:57:37 【问题描述】:

我有一个使用 jQueryUI 可排序的列表。 根据插件的“更改”事件,我需要在拖动时跟踪元素的每一步。

但它只能在缓慢到正常的拖动中可靠地工作。在这些速度下,没有任何错误。但是如果用户拖动元素的速度相对较快(列表越大越频繁,因为鼠标有更多的空间来获得速度),“更改”事件会丢失一些步骤,因此一些有用的信息会随之丢失在控制台中没有抛出错误的方式。

显示问题:

https://jsfiddle.net/yhp3m6L8/2/

在这个 jsFiddle 中,您可以拖动一个元素并查看它的索引变化作为一个示例。在列表下方,拖动时,您将有一个控制台模拟跟踪 black 中的索引。

如果拖得相当快,您会看到其中一些索引变为红色。这就是问题。这意味着他们之前的位置在更改事件期间被可排序脚本忽略,原因我只是不明白。连续性被打破了,这种连续性对我的需求来说是最重要的。 速度是唯一破坏连续性的东西。但是无法跟上相当快的鼠标拖动速度的“更改”事件似乎很奇怪。

脚本:

其中一半是关于跟踪索引,因为 Sortable 有一种特殊的引用索引的方式,它取决于方向(上/下),但也取决于元素相对于其初始位置的当前位置(之前/之后) .因此,从视觉上理解这些索引所需的代码量最少。使用该脚本,该元素会以一种有序的方式获得直观的视觉索引,这是您希望看到的。

然而,这个脚本是问题的展示,可能不是问题(见下面的附注)。

脚本的另一半只是模拟一种用于调试目的的控制台。

我的猜测:

我可能错了,但我最终认为这是“更改”事件的跟踪问题,或者拖动的元素没有跟上鼠标光标(感觉它并不总是在相对较高的光标下速度)。 除非有我不知道的 Sortable 选项可供使用...

我认为这是其中一种情况,因为无论我尝试在“更改”事件中使用什么代码,我最终都会遇到这个差距问题。

HTML:

<html>
    <body>
        <div class="panel">
            <ul class="panel_list">
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="ticked"></li>
                <li class="unticked"></li>
                <li class="unticked"></li>
                <li class="unticked"></li>
            </ul>
        </div>
        <div class='console'></div>
    </body>
</html>

CSS:

.console
    width:100%;


.panel ul
    list-style-type:none;
    width:30%;


.panel li
    height:29px;
    border-bottom:1px solid #454d5a;
    text-align: left;
    vertical-align: middle;
    line-height:29px;
    padding-left:8px;
    background: #666;


.panel_highlight
    height:29px;
    background:#1c2128 !important;


.unticked
    color:#7c7a7d;  
    background:#222 !important;

jQuery:

// jQuery: 3.3.1

// jQueryUI: 1.11.4 (downgraded from 1.12.1, because of documented 
// performance issues but...with no effect)

 

var origValue;
var oldInfo;
var c=0;
var x=0;

// LIST RELATED

//Just to have a visual on the indexes in the list. 
$('li').each(function()
    $(this).data('idx',c++);
    $(this).text(c);
)
c=0;

$( ".panel_list" ).sortable(
    placeholder: 'panel_highlight',
    items      : '>li.ticked',
    cancel     : '>li.unticked',
    tolerance  : 'pointer',
    axis       : 'y',
    opacity    :  0.9,
    start: function(event, ui)

    // LIST RELATED

        origValue = ui.item.data('idx');

    // CONSOLE RELATED (initialise variables)

        $('.console').html('');oldInfo='';
    ,
    change: function(event, ui)

    // LIST RELATED

        var idx = ui.placeholder.prevAll().filter(':not(.ui-sortable-helper)').length;
        var a=ui.placeholder.index();
        var b=a+1;

        //detect the direction of the dragging
        if(idx - $(this).data('idx')==1)
        //downward dragging

            //if the element is below its initial position (or x=1)
            if((a<=origValue) || (x==1))ui.item.text(b);x=0;
            //if the element is still above its initial position
            else                        ui.item.text(a);;
        
        else
        //upward dragging

            //if the element is still below its initial position
            if(a<=origValue)            ui.item.text(b);x=1;
            //if the element is above its initial position
            else                        ui.item.text(a);;
        ;
        $(this).data('idx', idx);

       // Update the visual on the indexes in the list. 
        $('li').each(function()
            if(ui.item.index() !=$(this).index())           
                $(this).data('idx',c++);
                $(this).text(c);
            
        )
        c=0;

    // CONSOLE RELATED (show indexes progression and gaps)

        var info=$('.console').html();
        if(oldInfo !='')
            var valAbs= Math.abs( parseInt(ui.item.text()) - parseInt(oldInfo));
            if(valAbs==1)info=info+' + '+ui.item.text();
            elseinfo=info+' + <span style="color:red">'+ui.item.text()+'</span>';;
        
        elseinfo=ui.item.text();;
        $('.console').html(info);
        oldInfo = ui.item.text();
    
);

旁注:

“更改”事件中的所有代码都是为您量身定制的,以便您查看问题,可能不是问题本身。我让你来评判它,但提这个才公平。

我在更改部分的实际脚本是不同的。它触发了某些表列的重新排序,这就是我第一次检测到问题的方式,因为表重新排序在高速下会出现故障。所以这里的虚拟脚本只是我将其缩小到最低限度,同时向您展示直观的方式。

关键是要评估它是否是可以解决的性能问题,是否缺少我应该添加的可排序选项,可以修复的拖动/光标延迟,或者是否有任何技巧可以以某种方式解决此跟踪问题。

我认为这是一个公平的警告,可以避免您调试一个只是展示的虚拟脚本的麻烦。但是考虑到我只是一个新手,我可能错了,请按您的意愿行事。

在任何情况下,我们都非常感谢您的帮助或意见。 提前谢谢你。

编辑:这是我真实的(缩小的)脚本,提供“dataTable.colReorder.move”事件。它涉及更多,因为它需要知道着陆索引(a/b)以及被拖动元素的当前索引(a/b/c/d)。并且 sortable 有自己的情境方式来索引它。

$( ".panel_list" ).sortable(
    placeholder: 'panel_highlight',
    items      : '>li.ticked',
    cancel     : '>li.unticked',
    start: function(event, ui)
        lastValue='';
        origValue=ui.item.index();
        $(this).data('idx', ui.item.index());
    ,
    change: function(event, ui)
        var x;
        var idx = ui.placeholder.prevAll().filter(':not(.ui-sortable-helper)').length;
        var a= ui.placeholder.index();
        var b=a+1;var c=a+2;var d=a-1;

        if(idx - $(this).data('idx')==1)
        //downward
            if((origValue>=a) || (x==1))dataTable.colReorder.move(a,b);lastValue=b;x=0
            else                        dataTable.colReorder.move(d,a);lastValue=a;
        
        else
        //upward
            if(origValue>=a)            dataTable.colReorder.move(c,b);lastValue=b;x=1
            else                        dataTable.colReorder.move(b,a);lastValue=a;
           

        $(this).data('idx', idx);       
    
);

【问题讨论】:

这种快速变化是通过人工输入还是通过读取 API 结果完成的?这看起来很像足球联赛结果表 好老人类拖拉.. 为什么有人想拖得这么快?你真的需要记录所有上下移动直到到达“停车”吗?还是单程(向上或向下)就足够了? 其实并不快。它相对较快。尝试使用四项列表,您将无法走得那么快。尝试列出 30 个项目,从一端到另一端,然后你就拥有了。您在两个坐标之间获得的空间越大,您获得的速度就越快。这是正常的。当像任何人一样使用系统时,有一个很长的列表,四分之一次,我处于亏损状态。所以,这根本不是一个假设的问题。而且那个速度真的没有超出范围。问题“列表越大,发生的频率越高,因为鼠标有更多的空间来获得速度”。 我昨天已经看过这个了,但是就像 Louys 一样想不出任何解决方案。因此我在之前的评论中提出了问题:你真的需要记录所有的上下移动吗?因为如果不是,我们可以计算所有移动,直到到达“停车”=您停下来的地方 【参考方案1】:

很遗憾...没有解决方案来解决这个问题。

我第一次尝试使用sort event callback...,同样的“跳过”问题出现在非常快速的拖动中。

我想知道为什么几分钟... 然后我教了.sortable() 可能依赖的东西……我带来了唯一可能的“浏览器”事件:mousemove

虽然mousemove 经常开火......就像一把“机关枪”......它的开火速度不够快,无法满足您的预期。

为了验证这一点,我只是在未更改的 Fiddle 末尾添加了这个 mousemove 处理程序:

$( ".panel_list" ).on("mousemove",function(e)
  console.log($(e.target).index());
);

记录的数字与您在div.console 中的完全相同

所以问题肯定不是.sortable()造成的。


编辑 我上面所说的仍然是正确的......但是既然你已经有了“跳过检测”,为什么不使用它来填补空白呢?

您拥有oldInfo,这是新change之前的最后一个.index()。 你有新的.index()...

根据移动是向上还是向下,您需要一个或另一个 for 循环来填补编号中的空白。 这也是你处理这些数字的触发点...

;)

这里是修改的部分:

// CONSOLE RELATED

info=$('.console').html();
if(oldInfo !='')

  var valReal = parseInt(ui.item.text()) - parseInt(oldInfo);
  var valOld = parseInt(oldInfo);

  var valAbs= Math.abs( parseInt(ui.item.text()) - parseInt(oldInfo));
  if(valAbs==1)info=info+' + '+ui.item.text();
  else

    // ADDING THE SKIPPED ONES!
    // Upward (in blue)
    if(valReal>0)
      for(i=valOld+1;i<valOld+valReal;i++)
        info=info+' + <span style="color:blue">'+( i )+'</span>';
      

    // Downward (in green)
    else
      for(i=valOld-1;i>valOld+valReal;i--)
        info=info+' + <span style="color:green">'+( i )+'</span>';
      
    

    // The one caugth after a gap (in red)
    info=info+' + <span style="color:red">'+ui.item.text()+'</span>';
  ;


它值得一个sn-p......为了后代。 (以全页模式运行。)

var origValue;
var oldInfo;
var info;
var c=0;
var x=0;

// LIST RELATED

//With or without this, the problem will occur.
$('li').each(function()
  $(this).data('idx',c++);
  $(this).text(c);
)
c=0;

$( ".panel_list" ).sortable(
  placeholder: 'panel_highlight',
  tolerance  : 'pointer',
  axis       : 'y',
  opacity    : 0.9,
  items      : '>li.ticked',
  cancel     : '>li.unticked',
  start: function(event, ui)

    // LIST RELATED

    origValue = ui.item.data('idx');

    // CONSOLE RELATED

    $('.console').html(''); oldInfo=''; info='';
  ,
  change: function(event, ui)

    // LIST RELATED

    var idx = ui.placeholder.prevAll().filter(':not(.ui-sortable-helper)').length;
    var a=ui.placeholder.index();
    var b=a+1;
    if(idx - $(this).data('idx')==1)
    //downward dragging
      if((a<=origValue) || (x==1))ui.item.text(b);x=0;
      else					  				 ui.item.text(a);;
    
    else
    //upward dragging
      if(a<=origValue)  			 ui.item.text(b);x=1;
      else					    			 ui.item.text(a);;
    ;
    $(this).data('idx', idx);

    //With or without this, the problem will occur.
    $('li').each(function()
      if(ui.item.index() !=$(this).index())
        $(this).data('idx',c++);
        $(this).text(c);
      
    )
    c=0;

    // CONSOLE RELATED

    info=$('.console').html();
    if(oldInfo !='')

      var valReal = parseInt(ui.item.text()) - parseInt(oldInfo);
      var valOld = parseInt(oldInfo);

      var valAbs= Math.abs( parseInt(ui.item.text()) - parseInt(oldInfo));
      if(valAbs==1)info=info+' + '+ui.item.text();
      else

				// ADDING THE SKIPPED ONES!
        if(valReal>0)
          for(i=valOld+1;i<valOld+valReal;i++)
            info=info+' + <span style="color:blue">'+( i )+'</span>';
          
        else
          for(i=valOld-1;i>valOld+valReal;i--)
            info=info+' + <span style="color:green">'+( i )+'</span>';
          
        
        
        // The one caugth after a gap
        info=info+' + <span style="color:red">'+ui.item.text()+'</span>';
      ;
    
    elseinfo=ui.item.text();;
    $('.console').html(info);
    oldInfo = ui.item.text();
  
);
.console
  width:100%;


.panel ul
	list-style-type:none;
  width:30%;


.panel li
	height:29px;
	border-bottom:1px solid #454d5a;
  text-align: left;
  vertical-align: middle;
	line-height:29px;
	padding-left:8px;
	background: #666;



.panel_highlight
    height:29px;
    background:#1c2128 !important;


.unticked
	color:#7c7a7d;	
	background:#222 !important;



.ui-sortable-helper
  background-color:red !important;
<link href="https://www.lexgotham.com/test5/styles/reset.css" rel="stylesheet"/>
<link href="https://www.lexgotham.com/test5/scripts/jQuery/jquery-ui-1.12.1/jquery-ui.css" rel="stylesheet"/>
<script src="https://www.lexgotham.com/test5/scripts/jQuery/jquery.3.3.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
<html>
<body>
  <div class="panel">
    <ul class="panel_list">
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="ticked"></li>
      <li class="unticked"></li>
      <li class="unticked"></li>
      <li class="unticked"></li>
    </ul>
  </div>
  <div class='console'></div>
</body>
</html>

Fiddle

【讨论】:

我做了一个演示,其中包含一个与可排序列表相关联的数据。我有很多错误!但我让它工作here。你欠我几杯啤酒! ;)【参考方案2】:

上一个答案中的一些观察结果是正确的,但这是我的一粒盐。顺便说一句,我确实相信这是“可修复的”..

我认为有一个非常简单的解决方案。每次发生“跳过”时,sortable 都不会告诉你所有的中间步骤,所以..你为什么不构造它们呢?..

var origValue;
var oldInfo;
var info;
var c=0;
var x=0;

// LIST RELATED

//With or without this, the problem will occur.
$('li').each(function()
    $(this).data('idx',c++);
    $(this).text(c);
)
c=0;

$( ".panel_list" ).sortable(
    placeholder: 'panel_highlight',
    tolerance  : 'pointer',
    axis       : 'y',
    opacity    : 0.9,
    items      : '>li.ticked',
    cancel     : '>li.unticked',
    start: function(event, ui)

    // LIST RELATED

                origValue = ui.item.data('idx');

        // CONSOLE RELATED

            $('.console').html(''); oldInfo=''; info='';
    ,
    change: function(event, ui)

    // LIST RELATED

            var idx = ui.placeholder.prevAll().filter(':not(.ui-sortable-helper)').length;
            var a=ui.placeholder.index();
            var b=a+1;
            if(idx - $(this).data('idx')==1)
            //downward dragging
            if((a<=origValue) || (x==1))ui.item.text(b);x=0;
                    else                                     ui.item.text(a);;
            
            else
            //upward dragging
            if(a<=origValue)             ui.item.text(b);x=1;
            else                                     ui.item.text(a);;
            ;
            $(this).data('idx', idx);

            //With or without this, the problem will occur.
            $('li').each(function()
          if(ui.item.index() !=$(this).index())
               $(this).data('idx',c++);
               $(this).text(c);
              
        )
            c=0;

        // CONSOLE RELATED

            info=$('.console').html();
            if(oldInfo !='')
          oIv = parseInt(oldInfo);
          uiV = parseInt(ui.item.text());
          stepIn = (uiV > oIv ? 1 : -1);
          switch (stepIn) 
          case 1:
          for (i=oIv+1;i<uiV;i++) info=info+' + <span style="color:green">'+i+'</span>';
          break;
          case -1:
          for (i=uiV+1;i<=oIv;i++) info=info+' + <span style="color:red">'+i+'</span>';
          break;
          
            
            elseinfo=ui.item.text();;
            $('.console').html(info);
            oldInfo = ui.item.text();
    
);

正如您在此处看到的,如果发生跳过,我只会将 change 事件错过的所有步骤添加到 info var 中……但是,遗憾的是,这仍然不起作用……原因很简单……更改事件的重新进入。

现在,我没有时间编写一个可行的解决方案,但这是我的想法。每次更改事件结束时,您都必须使用缺少的中间值重建列表。这样你就可以有一个连续的序列。因此,从您的原始代码中,只需向停止事件添加一个方法,该方法基于由更改创建的序列填充中间值,瞧,您就有了序列。

如果这满足您的要求,请告诉我。

【讨论】:

感谢您的意见。如果我错了,请纠正我,但我认为这无济于事。请记住,我需要实时更改。那么在一个元素被跳过的那一刻,系统如何知道它是向下跳过还是向后跳过?它必须知道列表的下一个元素才能评估它。如果是这样,它就不再是实时的了,因为您需要等待下一个更改事件才能知道如何处理前一个更改事件。如果我的评论没有回答您的建议,请详细说明(并原谅我)。 没什么可原谅的。解决方案很简单...您可以将其放在更改事件中以保持实时...基本上,当您进行交互时,您将拥有一个值数组,有或没有“跳过”......现在......基于该值,您始终可以生成没有跳过的值列表...例如:假设您有 1,2,3,7,3,4,5,6,2...,所以基本上您可以旅行通过数组并根据差异填充间隙并将更正后的值存储在新数组中。所以,当您到达第一个 3 时,您可以看到下一个值为 7,因此您将结果添加到缺失值, 4,5,6, --> 我会考虑一下,看看我能想出什么。如果我这样做了,我会告诉你的。 然后,7..next 你会发现另一个 3,所以现在缺失的值将是 6,5,4..get it?...在填写完空白后,你会显示纠正阵列,你就完成了。同样,您可以在更改事件中执行此操作以使其“实时”.. 不确定我能胜任这项任务,但我会根据您的意见尽力而为。非常感谢您的关心。您的答案是最接近解决方案的答案,而 LP Bessette 的答案对这个问题最有启发性。我希望在你的帮助下我能想出一些东西。如果是这样,我会尽快回复您。

以上是关于jQueryUI 可排序和拖动速度问题的主要内容,如果未能解决你的问题,请参考以下文章

jQuery UI 可拖动滚动速度的单位是啥?

Jquery UI - 在容器/父 div 内可拖动和排序 [重复]

Jquery ui - 可排序:在可排序元素中按图标“句柄”拖动

jquery ui可拖动可排序元素到iframe中

在 JQuery UI 上做一个可拖动的 connectToSortable 多个可排序

可拖动和可排序的 Portlet // jQuery