jQueryUI sortable and drag speed issue

Sadly... There is no solution to fix that issue.

I first tried using the sort event callback... And the same "skipping" issue occurs on very fast drags.

I wondered why a couple minutes...
Then I taught about what .sortable() could possibly rely on... I came with the only possible "browser" event: mousemove.

While mousemove fires really often... Quite like a "machine gun"... It does not fire fast enought for your expection here.

To verify this, I simply added this mousemove handler at the end of your unchanged Fiddle:

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

And it logs exactly the same numbers you have in your div.console.

enter image description here

So the issue is not due to .sortable() for sure.


EDIT
All I said above is still true... But since you already have the "skip detection", why not use it to fill the gaps?

You had oldInfo, which is the last .index() before this new change.
And you have the new .index()...

Depending if the movement is up or down, you need one or the other for loop to fill the gaps in the numbering. That also is you trigger point for what you have to do with those numbers...

;)

Here is the changed part:

// 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>';
  };
}


It deserves a snippet... For posterity. (Run in ful page mode.)

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>';
      };
    }
    else{info=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


Some of the observations from the previous answer are correct, but here is my grain of salt. Btw, I do believe this is "fixable"..

There is in my opinion a very simple solution. Every time a "skip" occurs the sortable will not tell you all the intermediate steps, so..why don't you construct them?..

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;
          }
            }
            else{info=ui.item.text();};
            $('.console').html(info);
            oldInfo = ui.item.text();
    }
});

As you can see here, if a skip happens I will just add to the info var all the steps missed by the change event...however, and sadly, this still does not work..the reason is simple..the reentry of the change event.

Now, I didn't had the time to code a working solution, however this is my idea. You must rebuild the list with the missing intermediate values every time a change event ends. That way you can have a continual sequence. So, from your original code, just add to the stop event a method that based on the sequence created by change fills up the intermediate values and voila, you have your sequence.

Let me know if this satisfy your requirements.