【问题标题】:JQuery sortable lists and fixed/locked itemsJQuery 可排序列表和固定/锁定项目
【发布时间】:2011-05-17 00:02:17
【问题描述】:

是否可以锁定 JQuery 可排序列表中的列表项,以使这些项目保留在列表中的特定位置。

例如,

考虑这个带有锁定项目的伪列表...

item A
item B(locked)
item C(locked)
item D
item E
item F
item G(locked)

所以,我希望将项目 B、C 和 G 固定为这样,如果用户将项目 D 拖放到列表的开头,则项目 A“跳过”固定/锁定的项目B 和 C 的结果如下...

item D
item B(locked)
item C(locked)
item A
item E
item F
item G(locked)

我一直在寻找这样的东西没有运气。有可能吗..?

【问题讨论】:

  • Perpaps 取消绑定列表项的所有事件,并仅重新绑定未锁定的事件?
  • 您能否展示您用于处理可排序列表的 jQuery 代码,或者拼凑一个 JS FiddleJS Bin 演示?

标签: jquery jquery-ui-sortable


【解决方案1】:

看看这个:Forcing an item to remain in place in a jQuery UI Sortable list

另外,我在这里用多个固定元素实现了上述解决方案:http://jsfiddle.net/enBnH/12/(已过时,见下文) 我认为这是不言自明的。

编辑:

我已经自动化了生成 lockto 值以及将 ID 添加到具有“固定”类的 lis 的过程(请注意,我必须添加一个 ID,以便我们可以引用它)

在此处查看完整的解决方案:http://jsfiddle.net/enBnH/44/

编辑

好的,在上面出现无数错误之后,我自己重写了该死的东西: http://jsfiddle.net/thomas4g/GPMZZ/15/

注意:以上方法确实有效,但@DarthJDG 的回答对我来说似乎好多了。我要让我的人可能更喜欢我的行为方式(我学会了不要删除东西,只是因为有更好的版本:P)

【讨论】:

  • 感谢您的回答,将检查并报告
  • 我认为这是一个不同的问题。
  • @omnosis 怎么会这样?在我看来是一样的。
  • @omnosis lockto 变量必须与固定元素的 id 匹配。请参阅此工作演示:jsfiddle.net/enBnH/8
  • 这只适用于同时一个lockto#static-1 id 也在污染 id-s。顺便说一句很好的演示。
【解决方案2】:

这是一个希望没有错误的版本,在您拖动时会更新。它会在排序开始时生成项目的当前所需位置,这意味着您应该能够在需要时更改类,刷新小部件的列表项,然后就可以开始了。

它还使用 sortable 的内置 items 属性来防止拖动固定项并解决列表顶部和底部的任何排序问题。

我试图移动固定项目,但这导致了可怕的错误行为,尤其是当组中有多个固定项目时。最终的解决方案是从列表中分离所有固定项,在前面添加一个辅助元素,然后将固定元素重新插入到所需位置,这似乎修复了所有错误。

在此处试用演示: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();
    }
});

【讨论】:

  • @Thomas Shields:不高兴,这太可怕了,赏金仍在等待中。 :)
  • 希望这次我能做到。
  • 不错。 “手感”和操作也比其他方法好。
  • @Alex:感谢您的慷慨赏金。
  • 辅助对象只是在位置 0 插入。事实上,这从来没有必要,因为如果位置 0 的项目不可排序,Sortable 将永远不会移动它。因此,您可以只检查位置 0(或强制检查所需位置是否与当前位置不同)并跳过它。然后你可以删除助手。
【解决方案3】:

我扩展了jQuery.Ui.sortable

概述

jQuery.Ui.sortable 具有fixed 功能的小部件扩展。此功能允许用户修复列表中的元素。
使用.fixedsortable() 构造函数,您可以构造一个.sortable() 类,该类扩展了这些特性。您可以使用原始方法和扩展方法。

代码

https://gist.github.com/3758329#file_fixedsortable.js > fixedsortable.js

示例

http://jsfiddle.net/omnosis/jQkdb/

用法

一般:

要使用,请将fixed 属性添加到可排序列表选项中:

$("#list").fixedsortable({
   fixed: (value)
})

值可以是:

  • 整数 示例:3
  • 数组 整数示例:[1,2,5]
  • 一个html元素或html元素列表
  • css 选择器
  • jquery 对象

HTML:

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> //the jquery 
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js"></script> //the original jquery-ui   
<script type="text/javascript" src="https://raw.github.com/gist/3758329/91749ff63cbc5056264389588a8ab64238484d74/fixedsortable.js"></script> //the extended sortable
...
<ul id="sortable1">
    <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>

<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>

<ul id="sortable3">
    <li>bananas</li>
    <li>oranges</li>
    <li>apples</li>
    <li>pineapples</li>
    <li>grapes</li>
    <li>pears</li>
    <li>mango</li>
</ul>

Javascript

$(function() {
    $("#sortable1").fixedsortable({
        fixed: "> .static"
    });

    $("#sortable2").fixedsortable({
        fixed: $("li[foo]").css("background","red")
    });

    $("#sortable3").fixedsortable({
        fixed: 2
    })
});

注意事项:

如果你坚持使用.sortable 代替.fixedsortable,你可以使用https://gist.github.com/3758329#file_sortable.js 代替jquery.ui 库。这是jQuery.ui 的完全替代品,但我不建议使用它,因为以后会更新。

我已经为此工作了超过 12 个小时 :( 我疯了..

【讨论】:

  • 干得好!很好的答案,很好的布局,小提琴链接,解释!干得好。
  • 干得好!我注意到,如果我正在排序的列表项包含链接(例如,
  • fruit
  • ),则该行为与预期不符。移动非固定列表项可以找到,但“固定”项可以移动到可排序中的新位置。固定项目的原始位置保留了没有新项目排序到其中的行为。查看你的 jsfiddle 的这个分支:jsfiddle.net/jbru/wphPG/1
  • 这个脚本还在某处可用吗?该链接不再指向任何地方。谢谢。
  • 【解决方案4】:

    使用items 参数,您可以像这样实现您想要的:

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

    现在只能对具有.sortable CSS 类的行进行排序。

    如果您只想锁定第一行,您可以这样做:

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

    可能性无穷无尽……

    【讨论】:

    • 这不起作用。 Here's why。抓住“一个”并将其向下拖动。请注意,“二”在“一”上方移动,此时它应保留为列表中的第二项。
    【解决方案5】:

    这是基于@DarthJDG 代码。然而,它并没有检索到所有的 id,并且排序不适用于表格。所以我设法更新了他的解决方案,它适用于列表和表格,并将 id 保存在数组中。

    Javascript:

    var fixed = '.static'; //class which will be locked
    var items = 'li'; //tags that will be sorted
    
    $('ul').sortable({
      cancel: fixed,
      items: items,
      start: function () {
        $(fixed, this).each(function () {
          var $this = $(this);
          $this.data('pos', $this.index());
        });
      },
      change: function () {
        var $sortable = $(this);
        var $statics = $(fixed, this).detach();
        var tagName = $statics.prop('tagName');
        var $helper = $('<'+tagName+'/>').prependTo(this);
        $statics.each(function () {
          var $this = $(this);
          var target = $this.data('pos');
          $this.insertAfter($(items, $sortable).eq(target));
        });
        $helper.remove();
      }
    });
    

    演示:http://plnkr.co/edit/hMeIiRFT97e9FGk7hmbs

    【讨论】:

      【解决方案6】:

      哦不!要点链接已损坏。这是来自https://gist.github.com/peterh-capella/4234752的代码转储

      代码于 2016 年 1 月 6 日访问

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

      为了以防万一

      依赖

      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

      脚本

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

      【讨论】:

        【解决方案7】:

        也许这会对某人有所帮助:使用“禁用”和“启用”方法。例子 HTML:

        <ul class="sortable">
          <li>You can move me</li>
          <li data-state="lifeless">You can't move me.</li>
        </ul>
        

        脚本:

        $('#sortable').sortable();
        $('#sortable').mousedown(function() {
          if($(this).data('state')=='lifeless') $('#sortable').sortable('disable');
          else $('#sortable').sortable('enable');
        });
        

        此处的示例:https://jsfiddle.net/ozsvar/0ggqtva5/2/

        【讨论】:

        • 几乎但不是一个真正可用的答案,因为使用非禁用项目仍然可以重新排序......
        【解决方案8】:

        连接的可排序项和固定项

        当我们有几个连接的可排序对象时,我遇到了这个问题。 @sarunast 和 @DarthJDG 建议的代码在将项目从一个列表拖到另一个列表时存在错误行为。 因此,我对其进行了一些修改,现在您可以从不同的列表中拖动项目,并在两个列表中保存位置。

        javascript:

        let connected = '.soratble';
        let fixed = '.static';
        let newParentContainer;
        
        //wrap the code suggested by @sarunast and @DarthJDG into the function
        //code was modified a little
        function sortingAroundFixedPositions(container) {
          let sortable = $(container);
          let statics = $(fixed, container).detach();
          let tagName = statics.prop('tagName');
          let helper = $('<' + tagName + '/>').prependTo(container);
          statics.each(function() {
            let target = this.dataset.pos;
            let targetPosition = $(tagName, sortable).eq(target);
            if (targetPosition.length === 0) {
              targetPosition = $(tagName, sortable).eq(target - 1)
            }
            $(this).insertAfter(targetPosition);
          });
          helper.remove();
        }
        
        $('ul').sortable({
          connectWith: connected,
          cancel: fixed,
          start: function() {
            $(fixed, connected).each(function() {
              this.dataset.pos = $(this).index();
            });
          },
          change: function(e, ui) {
            sortingAroundFixedPositions(this);
            if (ui.sender) {
              newParentContainer = this;
            }
            if (newParentContainer) {
              sortingAroundFixedPositions(newParentContainer);
            }
          },
          update: function(e, ui) {
            newParentContainer = undefined;
          }
        });
        

        演示:http://plnkr.co/edit/blmv4ZjaWJFcjvO2zQH0

        【讨论】:

          【解决方案9】:

          只需使用“包含/排除”项目选择器。 这是链接:https://jqueryui.com/sortable/#items

          【讨论】:

            【解决方案10】:

            有一个更好的方法来解决这个问题。 您需要使用网格而不是列表,然后您可以通过使用 css 声明元素应在何处排列来固定元素的位置:

            .fixed-element {
               grid-column-start: 1;
               grid-column-end: 1;
               grid-row-start: 1;
               grid-row-end: 1;
            }
            

            【讨论】:

              猜你喜欢
              相关资源
              最近更新 更多
              热门标签