Search code examples
javascriptjqueryjquery-ui-sortablesortablejs

SortableJS - can't exclude item from being draggable and sortable


I've already submitted an issue here but just wanted to check if you can solve this faster here.

Trying to exclude an item from being draggable works but not from being sortable -- the problem is that other elements can be placed behind it.

SortableJS Docs don't seem to have exclude.

Here's the expected behavior using jQuery UI:

//jQuery UI
$("#demo .sortable").sortable({
    items: "li:not(.exclude)",
    cancel: "li.exclude"
});
$("#demo .sortable").disableSelection();

$('.add-new').click(function(){
    $(this).prev().clone().insertBefore(this);
});
h1{ font-size: 1em; margin:1em}
.sortable { list-style-type: none; margin: 0; padding: 0; width: 260px; overflow:auto; }
.sortable li { background:lightgray; text-align:center; float:left; width:50px; height:50px; margin:0.2em; padding: 0.2em; cursor:move; }
.sortable li.add-new{cursor:default; color:#959595; opacity:0.8; background: white; outline:2px dotted gray}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js"></script>

<h1>jQuery UI</h1>
<div id="demo">
    <ul class="sortable">
        <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 1</li>
        <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 2</li>
        <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 3</li>
        <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 4</li>
        <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 5</li>
        <li class="ui-state-default add-new exclude"><span class="ui-icon ui-icon-plus"></span>Add New Item</li>
    </ul>
    
</div><!-- End demo -->

Notice that you can never place an item after the last "Add New Item". This is another example using jQuery UI that makes distinction between sortable and drop target and can disable it either one.

What I've tried so far with SortableJS:

// SortableJS

let mySortable = $("#demo2 .sortable").sortable({
  draggable: "li:not(.exclude)",
  animation: 250,
  forceFallback: true,
  removeCloneOnHide: true,
  touchStartThreshold: 10,

  onMove: function(evt) {
    //doesn't work
    //console.log ($(evt.dragged).index(),$('.exclude').index());
    //return $(evt.dragged).is(':last-child') ? false : true; 
  },

  onEnd: function(evt) {
    var itemEl = evt.item; // dragged HTMLElement
    if (!$('.exclude').is(':last-child')) {
      // Somewhat works but is super hacky and visually bad.
      //$('.exclude').appendTo($('.exclude').parent());  
    }
  }
});


$('.add-new').click(function(){
    $(this).prev().clone().insertBefore(this);
});
h1{ font-size: 1em; margin:1em}
.sortable { list-style-type: none; margin: 0; padding: 0; width: 260px; overflow:auto; }
.sortable li { background:lightgray; text-align:center; float:left; width:50px; height:50px; margin:0.2em; padding: 0.2em; cursor:move; }
.sortable li.add-new{cursor:default; color:#959595; opacity:0.8; background: white; outline:2px dotted gray}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-sortablejs@latest/jquery-sortable.js"></script>



<h1>SortableJS</h1>
<div id="demo2">
  <ul class="sortable">
    <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 1</li>
    <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 2</li>
    <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 3</li>
    <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 4</li>
    <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 5</li>
    <li draggable="false" droppable="false" class="ui-state-default add-new exclude"><span class="ui-icon ui-icon-plus"></span>Add New Item</li>
  </ul>

</div>
<!-- End demo -->

I don't want to take the "Add New Item" out of the sortable div or position:absolute it Any idea how to accomplish this?

Thanks!


Solution

  • If you check documentation there is one option filter: ".ignore-elements" you can use this to ignore elements which you do not want to be draggable . Then , check if the related target element doesn't have exclude class depending on this return true or false to cancel event.

    Demo Code :

    let mySortable = $("#demo2 .sortable").sortable({
      animation: 250,
      forceFallback: true,
      removeCloneOnHide: true,
      touchStartThreshold: 10,
      filter: ".exclude", //use this
      onMove: function(evt) {
        return evt.related.className.indexOf('exclude') === -1; //and this
      }
    });
    
    
    $('.add-new').click(function() {
      $(this).prev().clone().insertBefore(this);
    });
    h1 {
      font-size: 1em;
      margin: 1em
    }
    
    .sortable {
      list-style-type: none;
      margin: 0;
      padding: 0;
      width: 260px;
      overflow: auto;
    }
    
    .sortable li {
      background: lightgray;
      text-align: center;
      float: left;
      width: 50px;
      height: 50px;
      margin: 0.2em;
      padding: 0.2em;
      cursor: move;
    }
    
    .sortable li.add-new {
      cursor: default;
      color: #959595;
      opacity: 0.8;
      background: white;
      outline: 2px dotted gray
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jquery-sortablejs@latest/jquery-sortable.js"></script>
    
    
    
    <h1>SortableJS</h1>
    <div id="demo2">
      <ul class="sortable">
        <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 1</li>
        <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 2</li>
        <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 3</li>
        <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 4</li>
        <li class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 5</li>
        <li draggable="false" droppable="false" class="ui-state-default add-new exclude"><span class="ui-icon ui-icon-plus"></span>Add New Item</li>
      </ul>
    
    </div>