javascript looping through li attributes and changing values per item - javascript

I want to loop through a bunch of <li> elements and update the value held in it, and show this on the document.
The reason i'm doing this is because I want to sort a list of elements in an order. I do this using Sortable in JQuery.
<li> 1 </li>
<li> 2 </li>
<li> 3 </li>
<li> 4 </li>
<li> 5 </li>
the order may become:
<li> 3 </li>
<li> 1 </li>
<li> 4 </li>
<li> 2 </li>
<li> 5 </li>
Then clicking a button i would like my JS function to change the value of the li items back to 1,2,3,4,5. Its worth noting i am not looking for a solution to revert back to how the list was before.
Thanks.

This should work (as of jQuery 1.4):
$('.your_list li').text(function (index) {
return index + 1;
});

I think you would rather reorder the list items than their contents. This way, you don't lose any of the li's attributes/classes/... And you can keep the DOM's elements intact, only change the order of some ul's children.
I found a nice little snippet do do it:
http://www.onemoretake.com/2009/02/25/sorting-elements-with-jquery/
The only thing you still need to do is remember the initial order
function mysortA( a, b ) {
var compA = $(a).text().toUpperCase();
var compB = $(b).text().toUpperCase();
return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
}
function mysortB( a, b ) {
return a.originalindex < b.originalindex ? -1
: a.originalindex > b.originalindex ? 1
: 0;
}
You have to initialize:
var ul = $('ul');
var listitems = ul.children('li').get();
// remember original index
listitems.each( function(index, li){ li.originalindex=index; } );
Then you can sort one way:
// sort them using your sort function
listitems.sort( mysort )
// rebuild the list container
listitems.each( function(idx, itm) { mylist.append(itm); });
And sort back:
var ul=$('ul');
ul.children('li').get().sort(mysortB).each(function(i,li){ ul.append(li); });
Note: untested - grab the idea.

Related

Apply multiple client-side filters with jQuery

I need some help with jQuery filtering and selection.
I've got a list of elements
<ul>
<li data-cat="1 4 6" data-level="1 2" data-price="500">Element 1</li>
<li data-cat="1 2 5" data-level="1 3" data-price="300">Element 2</li>
<li data-cat="2 4 6" data-level="3" data-price="450">Element 3</li>
</ul>
which I must filter client-side (show/hide).
Category filter is a radiobutton (single selection), price filter is a jQuery UI slider, level filter is a set of checkboxes (multiple selection). These 3 controls affect the following variabiles:
var filterCategory = 0;
var filterPriceFrom = 0; var filterPriceTo = 0;
var filterLevel = [];
On them changing I call the following listener, which applies the filters (leve filter not implemented yet).
var list = $('ul > li');
list.hide();
if (filterCategory > 0){
list = $('ul > li[data-cat~=\"' + filterCategory + '\"]');
}
list = list.filter(function() {
var value = parseInt($(this).data('price'), 10);
return value >= filterPriceFrom && value <= filterPriceTo;
});
list.show();
Now I have to add level filtering, which is a set of checkboxes (multiple selection). I think I have 2 options
1: add a block like category filter. The problem with this solution is that my filtered result are within "list" variabile. I would like to use the power of jQuery data() selector. How can I take "list" variabile (and not the entire DOM) in input and apply filters again?
$(list > data-level[...]) /* ??? */
2: Put everything inside list.filter() block. Again: how can I keep using jQuery data() selector?
list = list.filter(function() {
/* check if $(this) has filterCategory within data-cat attribute */
/* check if $(this) has at least one of filterLevel elements within data-level attribute */
var value = parseInt($(this).data('price'), 10);
return value >= filterPriceFrom && value <= filterPriceTo;
});
Thank you.

Jquery to copy first link to second ul and then change class of first link

Short question:
http://jsfiddle.net/wF4FH/2/
What I want is for Page1 to be right above Page2 and Page10 above Page 20 before I change the classes. This should work for any number of elements.
The code provided gives an "Uncaught TypeError: Object # has no method 'append' ".
Long question:
I'm having problem finding the correct way to insert an li element based on the first link. The problem is I cant use id's on my markup so I have to "walk through" each class and check for names. I might just make this a lot more complicated than it is because my first two solutions didn't work the way I thought they would.
html
<ul class="nav">
<li class="active">
Start
</li>
<li class="has-child">
page1
<ul class="">
<li>
page2
</li>
</ul>
</li>
<li class="has-child">
page10
<ul class="">
<li>
page20
</li>
<li>
page30
</li>
</ul>
</li>
javascript
//Copy first link to child ul li
var pageLinks = $("li.has-child > a:first-child");
if (pageLinks != null) {
//var dropdownMenus = $("li.dropdown > a:first-child");
for (var i = 0; i < pageLinks.length; i++) {
for (var x = 0; x < pageLinks.length; x++) {
if (pageLinks[i].innerHTML === pageLinks[x].innerHTML) {
pageLinks[x].childNodes.append(pageLinks[i]);
}
}
}
}
//Change css classes
$("li.has-child").attr('class', 'dropdown');
$(".dropdown ul").addClass("dropdown-menu");
$(".dropdown a").attr("href", "#").addClass("dropdown-toggle").attr('data-toggle', 'dropdown');
strong text
What I want is for Page1 to be right above Page2 and Page10 above Page 20 before I change the classes. This should work for any number of elements.
When they are copied to the inner ul I change the top level menu item to a different class to work as a clickable dropdown men item.
The code provided gives an "Uncaught TypeError: Object # has no method 'append' ".
It is the navigation of a cms I cant change the markup on.
try this:
$links = $('li.has-child').children('a:first-child');
if($links.length > 0){
$links.each(function(i,link){
$(link).next().prepend($('<li></li>').append($(link)))
})
}
http://jsfiddle.net/wF4FH/6/
You need .clone() method to copy elements..
UPDATED
$links = $('li.has-child').children('a:first-child');
if($links.length > 0){
$links.each(function(i,link){
$(link).next().prepend($('<li></li>').append($(link).clone()))
})
}
http://jsfiddle.net/wF4FH/7/
When you have a jQuery object and you access it by numeric index, you're left with an HTML element. So $('body')[0] == document.body. This means that when you access pageLinks[x], you're really getting a raw element. This means that you want pageLinks[x].appendChild(pageLinks[i]);, not pageLinks[x].childNodes.append(pageLinks[i]);

Retaining state of navigation problems

Hi I am trying to retain the state of a navigation accros multiple pages pages.I have managed to get the indexes of each element I want to retain , the problem is setting them.For some reason only some of the elements seem to be set.
I have debugged the code and it seems as if only one element gets taken into consideration sometimes.I can not figure out what am I doing wrong here.Here is my code:
<ul id="ProductNav">
<li>
<h2>#category.Key.ToUpper()</h2>
<ul>
<li>
<img src="#Url.Content("~/Content/Images/arrow.gif")" class="arrow"/>
#Html.ActionLink(subcategory,"Index" , "Products" , new { subcat = subcategory} , null)
</li>
</ul>
</li>
</ul>
......
var menuState = JSON.parse(sessionStorage["navigation"]);
for (var i = 0; i < menuState.length; i++) {
var menuIndex = menuState[i].eq;
$("ul#ProductNav li").eq(menuIndex).children("ul").css("display", "block");
}
menuState will we an object containing an array of indexes
From what I understand so far the problem begins here:
$("ul#ProductNav li").eq(menuIndex).children("ul").css("display", "block");

Ordering a list of elements without losing the event handler

I have this list :
<ul>
<li id="6">
list 6: somethings
</li>
<li id="2">
list 2: somethings
</li>
<li id="4">
list 4: somethings
</li>
<li id="5">
list 5: somethings
</li>
<li id="0">
list 0: somethings
</li>
</ul>
and I'd like (with Javascript/jQuery) order these elements by the id (ASC) keeping the event handler for each element.
Is it possible? How can I do it?
You could just assign the ID's into an array and use sort():
var a = [];
$("ul li").attr('id',function(i,e){
a.push(e);
});
$.each(a.sort(),function(i,e){
$("#"+e).appendTo('ul');
});
You are never removing them from the list, just moving them around. Click handler stays intact:
http://jsfiddle.net/niklasvh/nVLqR/
This should work fine. Using detach preserves any events associated with the element. You then use a custom sort function to compare the id attributes of each li element.
var ul = $("ul");
var li = ul.children("li");
li.detach().sort(function(a, b) {
var compA = $(a).prop("id");
var compB = $(b).prop("id");
return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
});
ul.append(li);
See an example fiddle here. A click event is attached to the li with ID "6". After the list has been reordered, that click event is still handled.
I think to order them you'll had to remove and add them back into the DOM... and therefore you'll certainly lose the event handler. Are you in control of the handler, can you rebind or use live() instead?
The alternative would be to absolutely position the li elements and use the css position properties (top, right, bottom, left) to move them around, this will keep them in the same order in the DOM, but render them in your desired order.t
This example work for me:
var mylist = $('.item').detach().sort(function (a, b) {
return $(a).find(selector).html() > $(b).find(selector).html();
});
$("#container").html(mylist);
or if you want to sort with other informations:
var mylist = $('.item').detach().sort(function (a, b) {
return $(a).find(selector).attr('data-title')() > $(b).find(selector).attr('data-title');
});
$("#container").html(mylist);

using '.each' method: how do I get the indexes of multiple ordered lists to each begin at [0]?

I've got multiple divs, each with an ordered list (various lengths). I'm using jquery to add a class to each list item according to its index (for the purpose of columnizing portions of each list). What I have so far ...
<script type="text/javascript">
/* Objective: columnize list items from a single ul or ol in a pre-determined number of columns
1. get the index of each list item
2. assign column class according to li's index
*/
$(document).ready(function() {
$('ol li').each(function(index){
// assign class according to li's index ... index = li number -1: 1-6 = 0-5; 7-12 = 6-11, etc.
if ( index <= 5 ) {
$(this).addClass('column-1');
}
if ( index > 5 && index < 12 ) {
$(this).addClass('column-2');
}
if ( index > 11 ) {
$(this).addClass('column-3');
}
// add another class to the first list item in each column
$('ol li').filter(function(index) {
return index != 0 && index % 6 == 0;
}).addClass('reset');
}); // closes li .each func
}); // closes doc.ready.func
</script>
... succeeds if there's only one list; when there are additional lists, the last column class ('column-3') is added to all remaining list items on the page. In other words, the script is presently indexing continuously through all subsequent lists/list items, rather than being re-set to [0] for each ordered list.
Can someone please show me the proper method/syntax to correct/amend this, so that the script addresses/indexes each ordered list anew?
many thanks in advance.
shecky
p.s. the markup is pretty straight-up:
<div class="tertiary">
<h1>header</h1>
<ol>
<li>a link</li>
<li>a link</li>
<li>a link</li>
</ol>
</div><!-- END div class="tertiary" -->
This will iterate over each OL, but once at a time:
// loop over each <ol>
$('ol').each(function(olIndex){
// loop over each <li> within the given <ol> ("this")
$(this).find('li').each(function(liIndex){
// do your <li> thing here with `liIndex` as your counter
});
});
As for all that stuff in the middle, you might be able to improve it with some nicer selectors:
$('ol').each(function(){
$(this).find('li')
.filter(':lt(6)').addClass('column-1') // <li> 1-5
.filter(':first').addClass('reset').end().end() // <li> 1
.filter(':gt(5):lt(12)').addClass('column-2') // <li> 6-11
.filter(':first').addClass('reset').end().end() // <li> 6
.filter(':gt(11)').addClass('column-3') // <li> 12+
.filter(':first').addClass('reset'); // <li> 12
});
Of course if we're making columns here, maybe we should be getting these counts dynamically?
$('ol').each(function(){
var $lis = $(this).find('li');
var len = $lis.size();
var colLen = Math.ceil(count / 3);
// and so on with the filter stuff with
});
$('ol').each(function(){
$(this).find('li').each(function(index){
// assign class according to li's index ... index = li number -1: 1-6 = 0-5; 7-12 = 6-11, etc.
if ( index <= 5 ) {
$(this).addClass('column-1');
}
if ( index > 5 && index < 12 ) {
$(this).addClass('column-2');
}
if ( index > 11 ) {
$(this).addClass('column-3');
}
}).filter(function(index) {
return index != 0 && index % 6 == 0;
}).addClass('reset'); // Closes li each and filter
}); // closes ol each

Categories

Resources