I have to create a list of items out of which user can choose any one item.
I have this code till now -
var state = 0;
function selectLI(element) {
if (state == 0) {
element.innerHTML = element.innerHTML + "<span class='liTick'>✔</span>";
state = 1;
} else {
var ele = document.getElementsByClassName('checklistLI');
for (var i = 0; i < ele.length; i++) {
var els = ele[i].getElementsByClassName(".liTick");
els.item(0).style.display = "none";
}
}
}
<ul class="checklist">
<li class="checklistLI" onclick="selectLI(this)">item 1</li>
<li class="checklistLI" onclick="selectLI(this)">item 2</li>
</ul>
What the code is supposed to do is first generate a tick and remove it if user chooses another time and generate a tick on that item instead. The first tick generates fine but it keeps giving me an error -
Uncaught TypeError: Cannot read properties of null (reading 'style')
when I try to remove the first tick and generate second one.
I could simply just use radio buttons but I don’t want that kind of UI.
There are two issues making it so your code doesn't hide the tick:
You've included a . in the class name when calling getElementsByClassName, but the class name doesn't actually have a dot in it.
You're looping through all the li elements, but only some of them have a liTick inside them; you're not allowing for the possibility you didn't find one.
The minimal fix is:
var els = ele[i].getElementsByClassName("liTick");
// No `.` here −−−−−−−−−−−−−−−−−−−−−−−−−−^
if (els[0]) { // <== Make sure there is one
els[0].style.display = "none";
}
Or in really up-to-date environments, you could use optional chaining:
var els = ele[i].getElementsByClassName("liTick");
els[0]?.style.display = "none";
But, I wouldn't do that, because it's just hiding the tick. The be consistent, if you're adding a tick, you should remove (not just hide) it.
Separately, you have only one state flag, but there are multiple elements. I'm going to assume they should be independently "tick"-able. For that, we want to base our decision on whether the element already has a tick in it.
Also, avoid using innerHTML (and in particular avoid .innerHTML = .innerHTML + "x"). Instead, just insert what you need.
Here's an example:
function selectLI(element) {
const tick = element.querySelector(".liTick");
if (tick) {
// Remove the tick
tick.remove(); // In modern environments
// Or: tick.parentNode.removeChild(tick); // In older environments
} else {
// Add the tick
element.insertAdjacentHTML(
"beforeend",
"<span class='liTick'>✔</span>"
);
}
}
<ul class="checklist">
<li class="checklistLI" onclick="selectLI(this)">item 1</li>
<li class="checklistLI" onclick="selectLI(this)">item 2</li>
</ul>
Or if you wanted only one item to be allowed to have a tick:
function selectLI(element) {
const tick = element.querySelector(".liTick");
if (tick) {
// Remove the tick
tick.remove(); // In modern environments
// Or: tick.parentNode.removeChild(tick); // In older environments
} else {
// Remove any other tick
const other = document.querySelector(".checklistLI .liTick");
if (other) {
other.remove(); // Or, again, the longer form in old environments
}
// Add the tick to this element
element.insertAdjacentHTML(
"beforeend",
"<span class='liTick'>✔</span>"
);
}
}
<ul class="checklist">
<li class="checklistLI" onclick="selectLI(this)">item 1</li>
<li class="checklistLI" onclick="selectLI(this)">item 2</li>
</ul>
Note that this is not accessible to people using assistive technologies. Consider using a <input type="checkbox"> instead.
As #Andy Holmes suggested in a comment:
This seems a lot more complicated for what it's actually doing. Why
don't you just put the ticks there by default, and when clicking on
the relevant li you just toggle a hidden class (which has display: none;) on the tick rather than this seemingly excessive code?
HTML
<ul class="checklist">
<li class="checklistLI" onclick="selectLI(this)">item 1 <span class='liTick hidden'>✔</span></li>
<li class="checklistLI" onclick="selectLI(this)">item 2 <span class='liTick hidden'>✔</span></li>
<li class="checklistLI" onclick="selectLI(this)">item 3 <span class='liTick hidden'>✔</span></li>
</ul>
CSS
.hidden {
display: none;
}
JS
function selectLI(element) {
document.querySelectorAll(".checklistLI").forEach(function (el) {
el.getElementsByClassName("liTick").item(0).classList.add("hidden");
});
element.getElementsByClassName("liTick").item(0).classList.remove("hidden");
}
Demo
checklist
You can use this solution, it's working!
<ul class="checklist">
<li class="checklistLI" onclick="selectLI(this, 1)">item 1</li>
<li class="checklistLI" onclick="selectLI(this, 2)">item 2</li>
</ul>
<script>
var state1 = 0;
var state2 = 0;
function selectLI(element, flag) {
if (flag === 1) {
if (state1 === 0) {
element.innerHTML = element.innerHTML + "<span class='liTick'>✔</span>";
state1 = 1;
} else {
element.innerHTML = 'item 1';
state1 = 0;
}
} else {
if (state2 === 0) {
element.innerHTML = element.innerHTML + "<span class='liTick'>✔</span>";
state2 = 1;
} else {
element.innerHTML = 'item 2';
state2 = 0;
}
}
}
</script>
Im using Dragula JS for the drag and drop functionality and I would like to also have the option to move back and forth the elements in my list with the mouse click without loosing the drag and drop functionality.. How can I achieve this?
so I click on element 1 and it moves to the list.
I click it back from that list and it moves back.
That's the idea.
I prepared a fiddle with the basic drag and drop if it helps.
http://jsfiddle.net/vf6dnwxj/10/
my structure in the fiddle above:
<div class="wrapper panel panel-body">
<ul id="left1" class="cont-dragula">
</ul>
<ul id="right1" class="cont-dragula">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3.</li>
<li>Item 4.</li>
<li>Item 5.</li>
<li>Item 6.</li>
</ul>
</div>
JS:
dragula([left1, right1]);
Well dragula doesn't do anything special it just moves items around. So You can simply move them around Yourself:
var leftList = document.querySelector('#left1');
var rightList = document.querySelector('#right1');
var list = document.querySelectorAll('#right1 li, #left1 li');
for (var i = 0; i < list.length; i++) {
list[i].addEventListener('click', function(){
if (this.parentNode.id == 'right1') {
leftList.appendChild(this);
} else {
rightList.appendChild(this);
}
});
}
demo fiddle
If You want dragulas callbacks to fire before manipulating DOM add drake.start(this) and after manipulation drake.end():
drake = dragula([left1, right1]);
drake.on('drop', function(el, target, source, sibling){
console.log(el);
console.log(target);
console.log(source);
console.log(sibling);
});
var leftList = document.querySelector('#left1');
var rightList = document.querySelector('#right1');
var list = document.querySelectorAll('#right1 li, #left1 li');
for (var i = 0; i < list.length; i++) {
list[i].addEventListener('click', function(){
drake.start(this);
if (this.parentNode.id == 'right1') {
leftList.appendChild(this);
} else {
rightList.appendChild(this);
}
drake.end();
});
}
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 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...
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..?
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();
}
});
I extended the jQuery.Ui.sortable:
Overview
jQuery.Ui.sortable widget extension with fixed feature. 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 original methods and the extended as well.
Code
https://gist.github.com/3758329#file_fixedsortable.js > fixedsortable.js
Example
http://jsfiddle.net/omnosis/jQkdb/
Usage
General:
To use, add the fixed property to the sortable list optios:
$("#list").fixedsortable({
fixed: (value)
})
the value can be:
integer example: 3
array of integers example : [1,2,5]
a html element or a list of html elements
a css selector
jquery object
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
})
});
Notes:
If you insist to use the .sortable instead of .fixedsortable you can use this https://gist.github.com/3758329#file_sortable.js instead 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.
i have been working on this more than 12 hours :( i am insane..
Check this out: Forcing an item to remain in place in a jQuery UI Sortable list
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)
See the COMPLETE solution HERE: 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/
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 )
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...
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.
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
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.
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
Just use the "Include/Exclude" items selectors.
Here is the link: https://jqueryui.com/sortable/#items
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>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>
</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;
}
Maybe this will help to someone: use methods "disable" and "enable". Example
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/
There is a slightly better way to solve this.
You need to use a grid instead of a list and then you can fix the position of an element by declaring where the element should be arranged with css:
.fixed-element {
grid-column-start: 1;
grid-column-end: 1;
grid-row-start: 1;
grid-row-end: 1;
}
I have multiple list on a page. An example of a list looks like this:
<ul class="effects-list">
<li data-sorte="2">creative</li>
<li data-sorte="1">euphoric</li>
<li data-sorte="2">uplifted</li>
<li data-sorte="1">energetic</li>
<li data-sorte="0">lazy</li>
<li data-sorte="1">focused</li>
<li data-sorte="2">happy</li>
<li data-sorte="0">talkative</li>
<li data-sorte="0">giggly</li>
<li data-sorte="0">tingly</li>
<li data-sorte="0">hungry</li>
<li data-sorte="0">sleepy</li>
<li data-sorte="0">aroused</li>
</ul>
I have a script that will remove all data-sorte that equals 0. After that is done, it sorts the list from highest to lowest (again by the numbers stored in data-sorte). It then takes the top three options and removes the rest.
Here is the script that does this:
$('*[data-sorte="0"]').remove();
$(".effects-list li").sort(sort_li_effects).appendTo('.effects-list');
function sort_li_effects(a, b){
return ($(a).data('sorte')) < ($(b).data('sorte')) ? 1 : -1;
}
$(".effects-list li").filter( function(k, v) {
if( k < 3 ) {
min = parseInt($(v).data('sorte'));
return false;
} else
return min > parseInt($(v).data('sorte'));
}).remove();
The problem I have is it sorts all of the list based on the first list. My question is how do I modify the script so it sorts all of the list on the page correctly?
Here is a jsFiddle with working code that shows the problem.
EDIT
To clarify a point. Lets say I have the following list:
<ul class="effects-list">
<li data-sorte="2">creative</li>
<li data-sorte="1">euphoric</li>
<li data-sorte="2">uplifted</li>
<li data-sorte="1">energetic</li>
<li data-sorte="0">lazy</li>
<li data-sorte="1">focused</li>
<li data-sorte="1">happy</li>
<li data-sorte="0">talkative</li>
<li data-sorte="0">giggly</li>
<li data-sorte="0">tingly</li>
<li data-sorte="0">hungry</li>
<li data-sorte="0">sleepy</li>
<li data-sorte="0">aroused</li>
</ul>
I would want it to show creative, uplifted, euphoric, energetic, focused and happy as those are the top options by the numbers. euphoric, energetic, focused and happy are all tied at 1 thus I want to show them all. The original script does this.
I slightly modified your script.
$('*[data-sorte="0"]').remove();
$(".effects-list").each(function() {
var $list = $(this),
$items = $list.find('li'),
sortes = [];
$items.detach().sort(sort_li_effects).filter(function(i) {
var sorte = $(this).data('sorte');
if (i < 3) {
sortes.push(sorte);
return true;
}
return sortes.indexOf(sorte) >= 0;
}).appendTo($list);
});
function sort_li_effects(a, b) {
return ($(a).data('sorte')) < ($(b).data('sorte')) ? 1 : -1;
}
http://jsfiddle.net/KK2bV/5/ or http://jsfiddle.net/KK2bV/6/ or http://jsfiddle.net/KK2bV/3/
Major differences:
It goes through li in every list and sorts correctly
It removes all unnecessary items at once using li:gt(2)
You can do this in the following three steps:
$('.effects-list').replaceWith(function() {
// step 1: grab list of items, remove all zero values and sort descending
var $items = $(this).children()
.filter(function() {
return $(this).data('sorte') !== 0;
}).sort(function(a, b) {
var a_value = $(a).data('sorte'),
b_value = $(b).data('sorte');
if (a_value != b_value) {
return a_value > b_value ? -1 : 1;
}
return 0;
}),
current,
levels = 0,
index = 0;
// step 2: work out the top three
while (index < $items.length && levels < 3) {
var value = $items.eq(index).data('sorte');
if (current === null || current != value) {
current = value;
++levels;
}
++index;
}
// step 3: replace the contents
return $items.slice(0, index);
});
See also: .replaceWith()
Demo
I don't like jQuery, however I can provide a javascript solution. I can recommend that you structure your HTML so that each UL has an ID that is enumerable. So:
<ul class="effects-list" id="list1"> ... </ul>
<ul class="...-list" id="list2">...<ul>
And so on. Then use a for loop to go through all the lists like this. Assume that there are 10 lists:
for (var i = 0; i < 10; i++) {
var list = document.getElementById("list"+i);
// Then run your jQuery code on the list 10 times.
}
Does that help?
This question already has answers here:
JQuery: Remove duplicate elements?
(8 answers)
Closed 9 years ago.
I have a code like below format. I don't want to display contents twice. For instance text 'Issue 1' is repeaded 4 times in first ul tag. I want it to be shown once using javascript/jquery. Thanks.
<h3>Year 2010</h3>
<ul>
<li>Issue 2</li>
<li>Issue 1</li>
<li>Issue 2</li>
<li>Issue 1</li>
<li>Issue 1</li>
<li>Issue 1</li>
</ul>
<h3>Year 2011</h3>
<ul>
<li>Issue 2</li>
<li>Issue 1</li>
<li>Issue 2</li>
<li>Issue 2</li>
</ul>
Try using this:
$('ul').each(function(){
var issues = [];
$(this).find('li a').each(function(){
var text = $(this).text();
if($.inArray(text, issues) > -1){
$(this).parent().remove();
}else{
issues.push(text);
}
});
});
It loops over each ul and then loops through each li a within them. It stores the text of each ul's a elements in an array (issues).
For each li a it checks whether the text has already been added to the array (i.e. it's a duplicate). If it has then it deletes the parent li, if it hasn't then it adds it to the array.
Here it is working: http://jsfiddle.net/3kr2m/1/
Here is a native JavaScript solution using slice to convert NodeList to Array and forEach to loop (using forEach directly on the NodeList seemed to have some problems), followed by a removeChild on nodes similar to what we have seen previously.
Array.prototype.slice.call(document.getElementsByTagName('ul')) // <ul>s to Array
.forEach( // loop over them
function (ul) {
var text = {}; // record what we have seen
Array.prototype.slice.call(ul.childNodes) // <li>s to Array
.forEach( // loop over these
function (li) {
if (li.nodeType !== 1 || li.tagName !== 'LI')
return; // only care about <li>s
if (text[li.textContent])
ul.removeChild(li); // seen it, remove
text[li.textContent] = 1; // mark seen
}
);
}
);
Demo fiddle
Of course, you could use for or while loops instead, which would be a little faster but requires you to keep track of more variables..
var ul, li, i, j, text;
ul = document.getElementsByTagName('ul');
for (i = 0; i < ul.length; ++i) { // loop over <ul>
text = {}; // reset seen for each <ul>
li = ul[i].childNodes;
for (j = 0; j < li.length; ++j) { // loop over <ul> children
if (li[j].nodeType === 1 && li[j].tagName === 'LI') { // only <li>
if (text[li[j].textContent]) ul[i].removeChild(li[j]); // remove seen
text[li[j].textContent] = 1; // mark seen
}
}
}
Demo of this