JQuery 可排序列表和固定/锁定项目

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4299241/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 17:01:52  来源:igfitidea点击:

JQuery sortable lists and fixed/locked items

jqueryjquery-ui-sortable

提问by laroma

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.

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

For example,

例如,

consider this pseudo list with locked items...

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

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

So, I'd like to have the items B,C and G to be fixed in a way that if user drag and drop item D at the start of the list, the item A "jumps" over fixed/locked items B and C with following results...

因此,我希望以某种方式修复项目 B、C 和 G,如果用户在列表开头拖放项目 D,则项目 A“跳过”固定/锁定的项目 B 和 C有以下结果...

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

I've been searching for something like this without luck. Is it possible..?

我一直在寻找这样的东西而没有运气。是否可以..?

采纳答案by Gergely Fehérvári

I extended the jQuery.Ui.sortable:

我扩展了jQuery.Ui.sortable

Overview

概述

jQuery.Ui.sortablewidget extension with fixedfeature. This feature allows user to fix elements in the list.
With the .fixedsortable()constructor you construct a .sortable()class which extended with the features. You can use the originalmethods and the extendedas well.

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

Code

代码

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

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

Example

例子

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

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

Usage

用法

General:

一般的:

To use, add the fixedproperty to the sortable list optios:

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

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

the value can be:

该值可以是:

  • integerexample: 3
  • arrayof integers example : [1,2,5]
  • a html elementor a list of html elements
  • a css selector
  • jqueryobject
  • 整数示例:3
  • 整数数组示例:[1,2,5]
  • 一个html 元素或一个 html 元素列表
  • 一个CSS 选择器
  • jquery对象

HTML:

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

Javascript

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

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

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

Notes:

笔记:

If you insist to use the .sortableinstead of .fixedsortableyou can use this https://gist.github.com/3758329#file_sortable.jsinstead of the jquery.ui library. This is a complete replacement of the jQuery.ui, but i don't recommend to use this because of later updates.

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

i have been working on this more than 12 hours :( i am insane..

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

回答by DarthJDG

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 itemsproperty to prevent dragging the fixed items and to sort out any sorting problems at the top and the bottom of the list.

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

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/

在此处尝试演示:http: //jsfiddle.net/PQrqS/1/

HTML:

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:

CSS:

.static { color:red; }

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

Javascript:

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

回答by Thomas Shields

Check this out: Forcing an item to remain in place in a jQuery UI Sortable list

看看这个:强制一个项目保持在 jQuery UI 可排序列表中的位置

Also, I've implemented the above solution with multiple fixed elements here: http://jsfiddle.net/enBnH/12/(obsolete, see below) It's rather self-explanatory, i think.

EDIT:

I've automated the process for generating the lockto values as well as adding ID's to those lis with the class "fixed" (note that i have to add an ID so we can reference it)

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

编辑:

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

See the COMPLETE solution HERE: http://jsfiddle.net/enBnH/44/

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

EDIT

编辑

Okay, after a gazillion errors with the above, i just rewrote the dang thing myself: http://jsfiddle.net/thomas4g/GPMZZ/15/

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

NOTE: The above does work, but @DarthJDG's answer seems a lot nicer to me. I'm leaving mine up on the offchance someone might prefer how mine behaves (i've learned not to delete stuff just beceause there's a better version :P )

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

回答by sarunast

This is based on @DarthJDG code. However it wasn't retrieving all the id's and the sorting wasn't working with the table. So I managed to update his solution which works with both list and tables and keeps the id in the array.

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

Javascript:

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

Demo: http://plnkr.co/edit/hMeIiRFT97e9FGk7hmbs

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

回答by Leniel Maccaferri

Using the itemsparameter you can achieve what you want like this:

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

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

Only rows with a .sortableCSS class can be sorted now.

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

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

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

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

The possibilities are endless...

可能性是无止境...

回答by Ilya Shashilov

Connected sortables and fixed items

连接的可排序项和固定项

I ran into the problem when we have several connected sortables. The code suggested by @sarunast and @DarthJDG has erroneous behavior when dragging items from one list to another. Therefore, I have modified it a little, and now you can drag items from different lists with saving positions in both of them.

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

javascript:

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

demo: http://plnkr.co/edit/blmv4ZjaWJFcjvO2zQH0

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

回答by monkeyhouse

oh no! gist link is broken. here is code dump from https://gist.github.com/peterh-capella/4234752

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

code accessed Jan 6, 2016

代码于 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);

And dumping rest of his answer, just in case

并丢弃他的其余答案,以防万一

dependencies

依赖

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

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

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

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

回答by Ozsvar Istvan

Maybe this will help to someone: use methods "disable" and "enable". Example HTML:

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

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

Script:

脚本:

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

Live example here: https://jsfiddle.net/ozsvar/0ggqtva5/2/

现场示例:https: //jsfiddle.net/ozsvar/0ggqtva5/2/