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