JQuery sortable lists and fixed/locked items

前端 未结 9 1508
故里飘歌
故里飘歌 2020-11-28 06:07

Is it possible to lock list items in JQuery sortable list in a way that those items will stay in that particular place in the list.

For example,

consider th

相关标签:
9条回答
  • 2020-11-28 06:59

    Here's a hopefully bug-free version, updating as you drag. It's generating the current desired positions of the items when sorting starts, which means you should be able to change the classes whenever you need, refresh the widget's list items and you're good to go.

    It also uses the sortable's built-in items property to prevent dragging the fixed items and to sort out any sorting problems at the top and the bottom of the list.

    I tried to move the fixed items around, but that resulted in horribly buggy behaviour, especially when there are multiple fixed items in groups. The final solution detaches all fixed items from the list, adds a helper element to the front, then re-inserts the fixed elements to their desired position, which seems to fix all bugs.

    Try the demo here: http://jsfiddle.net/PQrqS/1/

    HTML:

    <ul id="sortable">
        <li>oranges</li>
        <li class="static">apples</li>
        <li>bananas</li>
        <li>pineapples</li>
        <li>grapes</li>
        <li class="static">pears</li>
        <li>mango</li>
    </ul>
    

    CSS:

    .static { color:red; }
    
    li { background-color:whitesmoke; border:1px solid silver; width:100px; padding:2px; margin:2px; }
    

    Javascript:

    $('#sortable').sortable({
        items: ':not(.static)',
        start: function(){
            $('.static', this).each(function(){
                var $this = $(this);
                $this.data('pos', $this.index());
            });
        },
        change: function(){
            $sortable = $(this);
            $statics = $('.static', this).detach();
            $helper = $('<li></li>').prependTo(this);
            $statics.each(function(){
                var $this = $(this);
                var target = $this.data('pos');
    
                $this.insertAfter($('li', $sortable).eq(target));
            });
            $helper.remove();
        }
    });
    
    0 讨论(0)
  • 2020-11-28 07:01

    Using the items parameter you can achieve what you want like this:

    $("#mytable tbody").sortable({items: 'tr.sortable'});
    

    Only rows with a .sortable CSS class can be sorted now.

    If you want to lock only the 1st row you can do this:

    $("#mytable tbody").sortable({items: 'tr:not(:first)'});
    

    The possibilities are endless...

    0 讨论(0)
  • oh no! gist link is broken. here is code dump from https://gist.github.com/peterh-capella/4234752

    code accessed Jan 6, 2016

    //this code is created to fix this problem: http://stackoverflow.com/questions/4299241/
    
    (function( $, undefined ) {
    
    $.widget("ui.fixedsortable", $.ui.sortable, {
    
        options: $.extend({},$.ui.sortable.prototype.options,{fixed:[]}),
    
        _create: function() {
          var o = this.options;
          this.containerCache = {};
          this.element.addClass("ui-sortable");
    
          //Get the items
          $.ui.sortable.prototype.refresh.apply(this,arguments);
    
          if( typeof this.options.fixed == "number") {
            var num = this.options.fixed
            this.options.fixed = [num];
          }
          else if( typeof this.options.fixed == "string" || typeof this.options.fixed == "object") {
            if(this.options.fixed.constructor != Array) {
              var selec = this.options.fixed;
              var temparr = [];
              var temp = $(this.element[0]).find(selec);
              var x = this;
    
    
              temp.each(function() {
                var i;
                for(i=0;i<x.items.length && x.items[i].item.get(0) != this;++i) {}
                if(i<x.items.length) temparr.push(i);
              });
              this.options.fixed = temparr;
            }
          }   
    
    
          //Let's determine if the items are being displayed horizontally
          this.floating = this.items.length ? o.axis === 'x' || (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false;
    
          //Let's determine the parent's offset
          this.offset = this.element.offset();
    
          //Initialize mouse events for interaction
          $.ui.sortable.prototype._mouseInit.apply(this,arguments);
        },
    
        _mouseCapture: function( event ) { 
    
          this._fixPrev = this._returnItems();
          return $.ui.sortable.prototype._mouseCapture.apply(this,arguments);
        },
    
        _mouseStart: function( event ) { 
    
          for(var i=0;i<this.options.fixed.length;++i) {
            var num = this.options.fixed[i];
            var elem = this.items[num];
            if(event.target == elem.item.get(0)) return false;
          }
    
          return $.ui.sortable.prototype._mouseStart.apply(this,arguments);
        },
    
        _rearrange: function(event, i, a, hardRefresh) {
    
          a ? a[0].appendChild(this.placeholder[0]) : 
          i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling));
    
          this._refix(i);
    
    
    
          //Various things done here to improve the performance:
          // 1. we create a setTimeout, that calls refreshPositions
          // 2. on the instance, we have a counter variable, that get's higher after every append
          // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
          // 4. this lets only the last addition to the timeout stack through
    
    
    
          this.counter = this.counter ? ++this.counter : 1;
          var self = this, counter = this.counter;
    
    
          window.setTimeout(function() {
            if(counter == self.counter) self.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
          },0);
    
        },
    
        _refix: function(a) {
          var prev = this._fixPrev;
          var curr = this._returnItems();
    
          var Fixcodes = this.options.fixed;
    
          var NoFixed = [];
          var Fixed = [];
          var Mixed = []
          var post = [];
    
    
          for(var i=0;i<Fixcodes.length;++i) {
            var fix_index = Fixcodes[i];
            var fix_item  = prev[fix_index];
            var j = 0;
    
            for(j=0;j<curr.length && curr[j].item.get(0) != fix_item.item.get(0);++j) {}
    
            curr.splice(j,1);
    
            Fixed.push(fix_item);
          }
    
          for(var i=0;i<curr.length;++i) {
            if(curr[i].item.get(0) != this.currentItem.get(0)) {
              NoFixed.push(curr[i]);
            }
          }
    
          var fix_count = 0;
          var nofix_count = 0;
    
          for(var i=0;i<Fixed.length + NoFixed.length;++i) {
            if(Fixcodes.indexOf(i) >= 0) {
              Mixed.push(Fixed[fix_count++]);
            }
            else {
              Mixed.push(NoFixed[nofix_count++]);
            }
          }
    
          var parent = this.currentItem.get(0).parentNode;    
          var allchild = parent.children;
    
          for(var i=0;i<Mixed.length;++i) {
            parent.removeChild(Mixed[i].item.get(0));
            parent.appendChild(Mixed[i].item.get(0));
          }
        },
    
        _returnItems: function(event) {
    
          this.containers = [this];
          var items = [];
          var self = this;
          var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
          var connectWith = $.ui.sortable.prototype._connectWith.apply;
    
          if(connectWith) {
            for (var i = connectWith.length - 1; i >= 0; i--){
              var cur = $(connectWith[i]);
              for (var j = cur.length - 1; j >= 0; j--){
                var inst = $.data(cur[j], 'sortable');
                if(inst && inst != this && !inst.options.disabled) {
                  queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
                  this.containers.push(inst);
                }
              };
            };
          }
    
          for (var i = queries.length - 1; i >= 0; i--) {
            var targetData = queries[i][1];
            var _queries = queries[i][0];
    
            for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
              var item = $(_queries[j]);
    
              item.data('sortable-item', targetData); // Data for target checking (mouse manager)
    
              items.push({
                item: item,
                instance: targetData,
                width: 0, height: 0,
                left: 0, top: 0
              });
            };
          };
    
          return items;
        },
    
    
        value: function(input) {
            //console.log("test");
            $.ui.sortable.prototype.value.apply(this,arguments);
        }
    });
    
    })(jQuery);
    

    And dumping rest of his answer, just in case

    dependencies

    https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js

    Script

    function randomColor() { //for a little fun ;)
       var r = (Math.floor(Math.random()*256));
       var g = (Math.floor(Math.random()*256));
       var b = (Math.floor(Math.random()*256));
    
       return "#" + r.toString(16) + g.toString(16) + b.toString(16)
    }
    
    $(function() {
        $("#sortable1").fixedsortable({
            fixed: "> .static", //you can use css selector
            sort: function() {  //you can add events as well, without getting confused. for example:
                $(".static").css("background",randomColor())  //change the fixed items background
            },
            change: function(event,ui) {
                $(ui.item[0]).css("border","2px solid "+randomColor())  //change the captured border color
            },
            stop: function(event,ui) {
                $(ui.item[0]).css("border","2px solid #777"); //change the back the css modifications
                $("#sortable1 > li.static").css("background","#aaa");
            }
        });
    
        $("#sortable2").fixedsortable({  //you can use jQuery object as selector
            fixed: $("li[foo]").css("background","red")
        });
    
        $("#sortable3").fixedsortable({
            fixed: [2,4], //you can use array of zero base indexes as selector
            update: function(event, ui) {
                alert($(this).fixedsortable('toArray'))   //the fixedsortable('toArray') also works
            }
        })
    
        $("#sortable4").fixedsortable({
            fixed: 5  //you can fix a single item with a simple integer
        })
    });
    

    HTML

     <body>
        <div style="width:120px;float:left;">
        <ul id="sortable1">
            <li><a href="#">oranges</a></li>
            <li class="static"><a href="#">apples</a></li>
            <li><a href="#">bananas</a></li>
            <li><a href="#">pineapples</a></li>
            <li><a href="#">grapes</a></li>
            <li class="static"><a href="#">pears</a></li>
            <li><a href="#">mango</a></li>
        </ul>
    
        <ul id="sortable2">
            <li>bananas</li>
            <li foo="asd">oranges</li>
            <li foo="dsa">apples</li>
            <li>pineapples</li>
            <li>grapes</li>
            <li>pears</li>
            <li>mango</li>
        </ul>
        </div>
        <div style="width:120px;float:left;">
        <ul id="sortable3">
            <li id="fru_1">bananas</li>
            <li id="fru_2">oranges</li>
            <li id="fru_3" style="background:#f4f">apples</li>
            <li id="fru_4">pineapples</li>
            <li id="fru_5" style="background:#faaba9">grapes</li>
            <li id="fru_6">pears</li>
            <li id="fru_7">mango</li>
        </ul>
    
    
        <ul id="sortable4">
            <li>bananas</li>
            <li>oranges</li>
            <li>apples</li>
            <li>pineapples</li>
            <li>grapes</li>
            <li style="background:#dada00">pears</li>
            <li>mango</li>
        </ul>
       </div>
    </body>
    

    CSS

    ul {margin:10px;}
    ul#sortable1 > li, ul#sortable2 > li, ul#sortable3 > li, ul#sortable4 > li {
        display:block;
        width:100px;
        height:15px;
        padding: 3px;
        background: #aaa;
        border: 2px solid #777;
        margin: 1px;
    }
    ul#sortable1 > li.static {
        opacity:0.5;
    }
    
    0 讨论(0)
提交回复
热议问题