Using jQuery appendTo() while sorting html elements - javascript

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

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>');

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 execute partial find on dynamic HTML5 data descriptor?

I have a series of images tagged with HTML5 data descriptor "data-type2=[x]" where x is a number of different elements.
e.g.
<img data-type2="pants" class="element" src="#>
I am trying to pass that data field into a jquery function that finds classes in another div (<div class="outfit-list") that has child divs tagged with classes such as:
<div class="pants-001">
<div class="pants-002">
<div class="shoes-001">
etc.
Here is where I am stumped: how do I write a jquery function that accesses data type2 from the item I click (e.g. data-type2="pants"), finds all other divs under .outfit-list with classes that have, for example, "pants" in their class name "pants-002", and hide them? The function I have below does not work - I suspect that's because it's looking for the full name and not partial.
How do I make it perform a partial search to locate the classes that contain the term from data-type2?
<script type="text/javascript">
$(document).ready(function(){
$('.thumbslist .element').click(function(){
$('.outfit-list').find('.'+$(this).data('type2')).hide();
});
});
</script>
You can use the attribute contains selector, [attribute*="value"].
$('.outfit-list').find('[class*="' + $(this).data('type2') + '"]').hide();
You can use the starts with selector. Something like
$(".thumbslist .element").click(function() {
var type2 = $(this).data("type2");
$(".outfit-list").find("div[class^=" + type2 + "]").hide();
});
This plugin adds support for data selectors: http://plugins.jquery.com/project/dataSelector
First of all, the jQuery .data() method is amazing: http://api.jquery.com/data/
You could do:
$("#img1").data('type', 'pants')
// Or whatever else you need to attach data to. You can do this dynamically too!
t = $("#img1").data('type')
// Recall that data at some point
$("div").each(function() {
pat = new RegExp(t)
if ($(this).attr('class').search(pat) !== -1) {
$(this).hide()
}
});
Or even better in Coffeescript
t = $("#img1").data 'type'
$("div").each ->
if ($(#).attr('class').search new RegExp t) isnt -1 then $(#).hide()
May be with something like in this other question
jQuery selector regular expressions
You could just grab the value of the attribute then use it in an attribute selector: http://jsfiddle.net/n73fC/1/

Sorting in the DOM with adjacent elements

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.

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