Sort Unordered List with Javascript - javascript

I have been looking at the stackoverflow thread:
How may I sort a list alphabetically using jQuery?
but for my scenario, I have the hierachy:
<ul><li>NAME_TO_SORT_ON</li></ul>
Based on this set-up, how can I modify the solution from thread mentioned here to cater for my scenario which has a tag as I would like to sort on all the name found in NAME_TO_SORT_ON?
Thanks.

I would recommend using a jQuery-based solution for this, because once you start getting into multiple DOM levels (e.g. sorting siblings at one level based on the contents of elements at a deeper level) the simple sort mechanism breaks down. It's an extremely rough solution - essentially blowing away the existing HTML and replacing it in raw text mode with other HTML. We can do better by actually shuffling the DOM elements around:
function sort(list, key) {
$($(list).get().reverse()).each(function(outer) {
var sorting = this;
$($(list).get().reverse()).each(function(inner) {
if($(key, this).text().localeCompare($(key, sorting).text()) > 0) {
this.parentNode.insertBefore(sorting.parentNode.removeChild(sorting), this);
}
});
});
}
To use it, we pass in a selector to the list and a selector to use to locate the key we want to sort on:
<ul class="toBeSorted">
<li>sort me</li>
</ul>
<script type="text/javascript">
sort('ul.toBeSorted>li', 'a');
//we want to sort the <li>'s in ul.toBeSorted;
//and we want to use the text of the first and only <a>
//in each item as the sort key
</script>

Related

Create unique buttons dynamically

I'm new to jQuery and am trying to create jQuery UI buttons dynamically and them to a list. I can create one list item but no more are appended after it. What am I doing wrong?
$('#buttonList').append('<li><button>'+ username + '</button>')
.button()
.data('type', userType)
.click(function(e) { alert($(this).data('type')); })
.append('<button>Edit</button></li>');
<div>
<ul id="buttonList">
</ul>
</div>
This only creates one list item with two buttons (although the second button seems to be encased in the first one, but I can probably figure that issue out). How do I get it to create multiple list items with their own unique 'data' values (i.e. I can't do a find() on a particular button class and give it data values as all buttons would then have the same data)?
I suggest to exchange the position of what you are appending and where you are appending to. This way, you retain the appended object, and should be able to work with it as a standard jQuery selector. From your code i commented out the .button() and the .append() lines, because i'm not sure what you want to do with them. Should you need help adding those lines, just drop a comment to my answer ;)
Oh, i almost forgot: i use var i to simulate different contents for username and userType data.
A JSFiddle for you is here: http://jsfiddle.net/cRjh9/1/
Example code (html part):
<div>
<p id="addButton">add button</p>
<ul id="buttonList">
</ul>
</div>
Example code (js part):
var i = 0;
$('#addButton').on('click', function()
{
$('<li><button class="itemButton">'+ 'username' + i + '</button></li>').appendTo('#buttonList')
//.button()
.find('.itemButton')
.data('type', 'userType'+i)
.click(function(e) { alert($(this).data('type'));
})
//.append('<button>Edit</button></li>')
;
i++;
});
You need complete tags when you wrap any html in a method argument. You can't treat the DOM like a text editor and append a start tag, append some more tags and then append the end tag.
Anything insterted into the DOM has to be complete and valid html.
You are also not understanding the context of what is returned from append(). It is not the element(s) within the arguments it is the element collection you are appending to. You are calling button() on the whole <UL>.
I suggest you get a better understanding of jQuery before trying to chain so many methods together
Just a very simplistic approach that you can modify - FIDDLE.
I haven't added the data attributes, nor the click function (I'm not really sure I like the
inline "click" functions - I generally do them in jQuery and try to figure out how to make
the code efficient. Probably not very rational, but I'm often so).
JS
var names = ['Washington', 'Adams', 'Jefferson', 'Lincoln', 'Roosevelt'];
for( r=0; r < names.length; r++ )
{
$('#buttonList').append('<li><button>'+ names[r] + '</button></li>');
}
$('#buttonList').append('<li><button>Edit</button></li>');

Obtaining Upper level li text

So I currently have a list like so on my page
<li class="head">
<b>Introduction</b>
<ul>
<li class="sub">somethingsomething</li>
</ul>
</li>
This list is being used with sortable, so the user can decide on the order, and I am passing this information to a grails controller for use in application logic. So, I am trying to read it in, and place the text contained in the "head" and "sub" classes in 2 different arrays. However, when I use a jquery selector to obtain the head elements, and obtain the text attribute of the element, it contains the inside list as well.
$('#divname').find("ul > li.head").each(function()
{
var current = $(this);
console.log(current.text());
});
results in Introductionsomethingsomething
Is there any way to only obtain the 'Introduction' text from the list, and ignore the text in the nested <ul> and <li.sub>? Due to it being nested, I am unable to figure out how to use jQuery's :not() selector
You can find the b tag using jquery tagname selector.Like this:
var current = $(this).find('b');
console.log(current.text());
Working Demo
May be this is solution:
<script>
$('#divname').find("ul > li.head").each(function()
{
var current = $(this).find("b");
console.log(current.text());
});
</script>

Using jQuery appendTo() while sorting html elements

I need to effectively sort html nodes inside some container. Here's a simplified version of what I did:
<html>
<ul class="navigation">
<li class="first">Main</li>
<li class="second">HTML и CSS tricks</li>
<li class="third">Study</li>
<li class="fourth">HTML reference</li>
</ul>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script>
var rows = $( ".navigation" ).children();
function sortAlpha(a,b){
return a.innerHTML.toLowerCase() > b.innerHTML.toLowerCase() ? 1 : -1;
};
rows.sort(sortAlpha).appendTo('.navigation');
</script>
</html>
Some of the people I work with are suspicious about the line
rows.sort(sortAlpha).appendTo('.navigation');
They ask is it ok to use appendTo this way, maybe there are (or will be in the future) some drawbacks of using this method, how come using appendTo replaces the content of the parent container and why it won't just add the sorted things to the parent container? What if it's a temporary feature of jQuery and it won't work like that in the future?
I need to say that the real case is more complex, e.g. we use the library that allows us to have object-oriented programming at the front-end side, also each element which needs to be sorted is a row of a table that contains some controls that have event bindings.
The idea was to replace the content of the dom node with sorted elements so this library wouldn't notice that. I used the approach described above in the real code and it's bugless at least for now, but the question remains: 1) what are the drawbacks of this approach, 2) what can be a better option?
This is covered in the documentation for .appendTo():
We can also select an element on the page and insert it into another:
$( "h2" ).appendTo( $( ".container" ) );
If an element selected this way is inserted into a single location elsewhere in the DOM, it will be moved into the target (not cloned)...
So, since you're selecting elements that already exist on the page, and calling .appendTo() with a selector that only matches a single element, those selected elements are moved into that new target element, in the order they're in inside of your set of matched elements. It's irrelevant that you're putting them back into the same element.
What are the drawbacks?
None that I can think of. You're using a documented aspect of what the function does. It's unlikely that the way that .appendTo() works is ever going to be changed.
appendTo is supposed to appendTo...I'm surprised it just acts like .html(). To make them happy why dont you just do $('.navigation').empty().append(rows.sort(sortAlpha));
Basically, appendTo() will insert html nodes at the end of the specified element, here : .navigation
Assuming you're using jQuery Sort plugin (http://github.com/jamespadolsey/jQuery-Plugins/tree/master/sort/), if you need to reorder your navigation menu, you could simply do :
$('.navigation li').sort(function(a, b){
return $(a).text() > $(b).text() ? 1 : -1;
});
By the way, if you want to replace the full content of an element, consider html() method, which empty the node and insert the content.
Try this,
$(function() {
$.fn.sortList = function() {
var mylist = $(this);
var listitems = $('li', mylist).get();
listitems.sort(function(a, b) {
var compA = $(a).text().toUpperCase();
var compB = $(b).text().toUpperCase();
return (compA < compB) ? -1 : 1;
});
$.each(listitems, function(i, itm) {
mylist.append(itm);
});
}
$("ul.navigation").sortList();
});
Demo

Delete divs by 2 classes and node attribute

Imagine that we have some divs like that
<div class="row chapter" node="1">Chapter 1</div>
<div class="row section" node="1">Section 1</div>
Ajax accepts array of nodes that must be deleted. I got few questions about this
1) I'm using following function
$.each(result['nodes'], function(column, node)){
$(".row."+column).slideUp("slow").remove();
}
I can't figure out how to delete by 2 classes and node atribute. How to do that?
2) AFAIK $.each function deletes one by one. Is it possible to delete all at once?
3) How to deal with validation issues? I mean,
Since an object only has key-value pairs, in order to handle two classes and one attribute, you'll have to change the structure of your JSON. I suggest changing it to [{"class1":"foo","class2":"bar","node":1}]. With that format, this will work to collect the nodes:
$($.map(result['nodes'], function(o) {
return "."+o.class1+"."+o.class2+"[node="+o.node+"]";
}).join(","))
As Joseph stated, your original code will remove the nodes too soon, so you'll need to put the remove() in a parameter to slideUp():
$($.map(result['nodes'], function(o) {
return "."+o.class1+"."+o.class2+"[node="+o.node+"]";
}).join(",")).slideUp("slow", function() {
$(this).remove()
});
Sample jsFiddle: http://jsfiddle.net/tSu8z/
Note that there's really no "one by one" vs. "all at once" distinction. With the code above, jQuery gets the order to slide the nodes up all at once, but internally it will iterate through the nodes. Likewise, the nodes will most likely all appear to be deleted simultaneously, but jQuery is actually iterating through them. The only requirement for it to look simultaneous is for slideUp to be asynchronous, which it is.
You can collect the selectors using an array, join them and use remove all at once.
var toRemove = [];
$.each(result['nodes'], function(index, value){
toRemove.push('.row.'+index+'[node='+value+']');
}
//$('.row.chapter[node=1], .row.section[node=1]')
$(toRemove.join(',')).slideUp("slow",function(){
$(this).remove()
});

How does one determine the number of a list item in an ordered list using JavaScript?

Given an ordered HTML list, is there any way, given the list element, of determining it's number in JavaScript, besides looking at its location in the list?
For example, suppose I have a list
<ol start="4" type="i">
<li>First</li>
<li>Second</li>
</ol>
which is rendered as
iv. First
v. Second
What is the best way using JavaScript (including jQuery) that, given on of the LI, to find out it's number?
The naive way is to look at the item's index, add the start value, and translate the type. But I am wondering if there's a better way.
an example is to add an index property to the list item:
lets say your list has an id='ordered'
var ol = document.getElementById('ordered');
// select the list items
var lists = ol.getElementsByTagName('li');
// now loop through the items and set a custom property 'index'
var l = lists.length; // total items
for (var i=1;i<=l;i++){
list[i].index = i;
}
now your list item will have an index property that you can access through javascript to determine its position.
<ol id='ordered'>
<li index='1'>First</li>
<li index='2'>Second</li>
</ol>
Looking at http://www.w3.org/TR/html-markup/li.html I'd say using the value member.
note: this is in HTML5. in HTML4.01 both ol.start and li.value were deprecated. This means that this solution will probably work reliably only on browsers with HTML5 support.
The MDC documentation for the <li> element mentions the value attribute, which is supposed to do just that. It was deprecated in HTML 4 but has been reintroduced in HTML 5. If your browser supports it, you should be able to write:
$("li").prop("value"); // jQuery 1.6 and higher
$("li").attr("value"); // jQuery 1.5 and lower
I was, however, unable to use that attribute in Firefox 3.6 (it always returns -1). I created a fiddle if you want to test your browser's support for that feature.
Nice questions :)
I would say, better to inject data into each li elements, you could put some HTML attributes inside the li but I am afraid when you do HTML validation, it will reject it.
So this is the code,
var lis = $("ol li").each(function(i, el)
{
$(this).data("index", i);
});
and when you render your lovely number, do this:
$(this).data("index");
:)

Categories