Sorting in the DOM with adjacent elements - javascript

I have the the following DOM structure which I want to sort according to the data-created attribute.
<a id="comment-34" href="#"/>
<li data-created="12342342" />
<a id="comment-35" href="#"/>
<li data-created="89342342" />
<a id="comment-36" href="#"/>
<li data-created="45363342" />
I CANNOT (for various reasons) wrap the <a> and <li> in an outer <div>. I want to do javascript sorting. All the jQuery sorting plugins can do the sorting if I just had the <li>. E.g. using the tinysort jQuery plugin ( http://tinysort.sjeiti.com/ ) I can do
$('li').tsort({order:'desc', attr:'data-created'});
However what happens after the sort is that <a> are no longer associated with their original siblings. I also evaluated https://github.com/jamespadolsey/jQuery-Plugins/tree/master/sortElements/ but it may suffer from the same problem.
Any way to do this? Again, I cannot wrap the <a> and <li> in an outer <div>. I also don't want to dynamically wrap a <div> so that I can use tsort.
Any clean solutions :-) ?

Something like this should work for you:
var elms = [];
$('a').each(function() { //create the array of a and li
var pair = {
aTag: $(this),
liTag: $(this).next("li")
};
elms.push(pair);
});
$("a, li").detach(); //detach them from the dom
elms.sort(function(a, b) {
return a.liTag.data("created") < b.liTag.data("created"); //sort based upon the created data
});
$.each(elms , function(){
$("ul").append(this.aTag).append(this.liTag); //push them back to the dom.
});
Code example on jsfiddle.

You can't really have <li> and <a> elements as siblings in the first place. A list item must be inside a list (<ol>,<ul>), where other elements are not allowed.
Ignoring that, you can simply grab each pair, remove from the DOM, reorder, then put them back. It's quite straight-forward. Example:
var items = [];
$('#sortme a').each(function(){
// grab the element and its next sibling
var self = $(this)
, next = self.next('div');
items.push([
self.remove().get(0),
next.remove().get(0)
]);
});
items.sort(function(a,b){
return a[1].getAttribute('data-created') > b[1].getAttribute('data-created');
});
$.each(items, function(){
$('#sortme').append(this[0], this[1]);
});
Test here: http://jsbin.com/okajo4/edit
Edit: a simpler version :)
var sorted = $('#sortme');
sorted.find('div')
.sort(function(a,b){
return $(a).data('created') > $(b).data('created');
})
.each(function(){
$(this).prev('a').andSelf().appendTo(sorted);
});

Move the a elements inside their respective list items. Then you ought to be able to use the sort you mentioned.
Even if having the elements as siblings is valid, it still strikes me as bad form.

Related

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

jQuery re-order elements by tag

I am currently trying to re-order an unordered list of elements by their rel="1234" tag associated with each using jQuery.
However, my current code is causing the output not to appear and I cannot work out why. Can anyone help?
My jQuery code is:
$(function(){
var elems = $('#test').children('li').remove();
elems.sort(function(a,b){
return parseInt(a.attr('rel')) > parseInt(b.attr('rel'));
});
$('#test').append(elems);
});
With basic mark-up like this:
<ul id="test">
<li rel="4112">blub</li>
<li rel="1422">blaaah</li>
<li rel="6640">hmmmm</li>
<li rel="2221">one more</li>
</ul>
http://jsfiddle.net/BQZHC/
Try this:-
$(function(){
var elems = $('#test').children('li'); // You dont need to remove the elements
elems.sort(function(a,b){
return +$(a).attr('rel') > +$(b).attr('rel'); // use attr on jquery object i.e $(a), $(b)
});
$('#test').append(elems); // This will take care of repositioning the elements.
});
Demo
a and b are DOM elements, not jQuery objects.
Either wrap them with $() to make jQuery objects, or use getAttribute("rel") instead of .attr("rel"). It'ss a little longer, but requires less overhead.
Additionally, assuming you wanted a numeric ordering, I would use subtraction.
return a.getAttribute("rel") - b.getAttribute("rel");
There have been browsers in the past that have had issues with returning a boolean value.
This is what you want
JS:
var elems = $('#test').children('li').remove();
elems.sort(function(a,b){
return parseInt($(a).attr('rel'), 10) > parseInt($(b).attr('rel'), 10);
});
$('#test').html(elems);
Note that the "a" and "b" elements of your sort function shoud be treated as jQuery objects. Also you should specify the radix of the parseInt()
DEMO: http://jsfiddle.net/BQZHC/10/

how to select 2nd <LI> element using javascript?

Using JavaScript, how can I dynamically change one of the list-items below from this:
<ul class="tabbernav">
<li class="tabberactive"><a title="All">All</a></li>
<li class=""><a title="One">One</a></li>
<li class=""><a title="Two">Two</a></li>
<li class=""><a title="Three">Three</a></li>
</ul>
to
<ul class="tabbernav">
<li class="tabberactive"><a title="All">All</a></li>
<li class=""><a title="One">One</a></li>
<li class=""><a title="Two">-----------NEW LIST ITEM CHANGED---------</a></li>
<li class=""><a title="Three">Three</a></li>
</ul>
I guess you could use getElementsByTagName inside of the ul to get all your list items inside an array. Then you can just edit the third element in your array, with index number 2.
var lItems = document.getElementsByTagName("ul").getElementsByTagName("li");
lItems[2].innerHTML = "<a title='Two'>----NEW LIST ITEM CHANGED-----</a>";
That will ofcourse get all ul elements on the page, and might lead to some strange results if you have more than two uls in your document. But you get the idea, right? Just ask some more if you don't understand what I'm trying to say.
Okay, the above code doesn't really work properly. I've modified my code a bit, but that also included a change in your HTML, as i presume you'll only have one ul "tabbernav", thus I changed it from class="tabbernav" to id="tabbernav". This is the code to do what you want.
var ul = document.getElementById("tabbernav");
var liArray = ul.getElementsByTagName("li");
for (var i = 0; i < liArray.length; i++) {
if(liArray[i].childNodes[0].title == "Two") {
liArray[i].innerHTML = "Your desired output";
}
}
Hope that helps you some more :)
I also suggest using jQuery, which makes selections like this trivial. In your case, you can use the :eq psuedo-selector to get the second line element:
$('.tabbernav li:eq(1)')
This selects the DOM element which is the second li (indexes start at 0) in an element with the class tabbernav. It returns a jQuery object which you can chain other methods to. Changing the inner HTML is done with .html('Your html here').
This is how you select the third Li element of your Ul list in pure JavaScript.
document.querySelectorAll("li")[2].innerHTML = "vasile";
Replace "Vasile" with your desired text.
var list = document.getElementsByTagName("li");
list[2].innerHTML = "<a title='Two'>------NEW LIST ITEM CHANGED----</a>";
this will work perfect
I know that this question is old but since it's still open, see how I modified the first answer. I feel someone else might need it.
>var lItems = document.getElementsByTagName("ul")[0];
>>var nth = lItems.getElementsByTagName("li")[2];
>>>nth.innerHTML = "<a title='Two'>----NEW LIST ITEM CHANGED-----> </a>";
So that basically solves it up by specifying the position of the lItems in particular to grab and in this case [0]. The code will not work properly if that position is missing because getElementsByTagName(NAME) returns a collection of html elements bearing that NAME specified. So that if you don't specify which among them all, the code fails.
If you like code reuse, see a function you can use for that. You just need to specify the parent element and its position and the childNode position and you get the same thing.
>var nthChild = function(parent, pos, childPos){
>>parent = document.getElementsByTagName(parent)[pos];
>>>return parent.children[childPos];
>>>>};
//used thus:
>nthChild("ul", 0, 2).innerHTML = "<a title='Two'>----NEW LIST ITEM CHANGED-----> </a>
";
How do you identify which <li> is the one you want to modify?
If you're doing it by index you could do something like this I think:
var list = document.getElementById("listid");
list.childNodes[2].innerHtml = "<a title='Two'>-----------NEW LIST ITEM CHANGED---------</a>";
Look into using a Javascript library such as JQuery. That will make your life a lot easier. Then you can do something like this:
$('li a[title=Two]').text('Changed Text Goes Here');
You'll need to check my syntax (not sure about the text() function), but it's easy enough to look up in JQuery's api docs.

Sort Unordered List with 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>

Categories