sorting elements using jquery - javascript

I have a div, #containerDiv, which contains elements related to users like first name, last name etc. in separate divs. I need to sort the contents of the container div based on the last name, first name etc. values.
On searching google the examples I got all are appending the sorted results and not changing the entire HTML being displayed. They are also not sorting by specific fields (first name, last name).
So please help me in sorting the entire content of #containerDiv based on specific fields and also displaying it.
The Page looks Like something as mentioned Below:
<div id="containerDiv">
<div id="lName_1">dsaf</div><div id="fName_1">grad</div>
<div id="lName_2">sdaf</div><div id="fName_2">radg</div>
<div id="lName_3">asdf</div><div id="fName_3">drag</div>
<div id="lName_4">fasd</div><div id="fName_4">gard</div>
<div id="lName_5">dasf</div><div id="fName_5">grda</div>
<div id="lName_6">asfd</div><div id="fName_6">drga</div>
</div>
On getting sorted by last name div values, the resulted structure of the container div should look like:
<div id="containerDiv">
<div id="lName_3">asdf</div><div id="fName_3">drag</div>
<div id="lName_6">asfd</div><div id="fName_6">drga</div>
<div id="lName_5">dasf</div><div id="fName_5">grda</div>
<div id="lName_1">dsaf</div><div id="fName_1">grad</div>
<div id="lName_4">fasd</div><div id="fName_4">gard</div>
<div id="lName_2">sdaf</div><div id="fName_2">radg</div>
</div>
Now I think you all can help me in a better way.

this is a sample example:
html:
<div id="containerDiv">
<div>2</div>
<div>3</div>
<div>1</div>
</div>
js
$(function() {
var container, divs;
divs = $("#containerDiv>div").clone();
container = $("#containerDiv");
divs.sort(function(divX, divY) {
return divX.innerHTML > divY.innerHTML;
});
container.empty();
divs.appendTo(container);
});
you may set your divs.sort function param depend on your goal.
jsFiddle.
and a jQuery Plugin is suitable

I suggest you read the div values so you get an array of objects (persons for example) or just names and perform a sort operation on that. Than...output the result to the initial div (overwriting the default values).

I have built a jQuery sort function in which you can affect the sort field.
(it rebuilds the html by moving the row to another location).
function sortTableJquery()
{
var tbl =$("#tbl tr");
var store = [];
var sortElementIndex = parseFloat($.data(document.body, "sortElement"));
for (var i = 0, len = $(tbl).length; i < len; i++)
{
var rowDom = $(tbl).eq(i);
var rowData = $.trim($("td",$(rowDom)).eq(sortElementIndex).text());
store.push([rowData, rowDom]);
}
store.sort(function (x, y)
{
if (x[0].toLowerCase() == y[0].toLowerCase()) return 0;
if (x[0].toLowerCase() < y[0].toLowerCase()) return -1 * parseFloat($.data(document.body, "sortDir"));
else return 1 * parseFloat($.data(document.body, "sortDir"));
});
for (var i = 0, len = store.length; i < len; i++)
{
$("#tbl").append(store[i][1]);
}
store = null;
}

Every time I need to sort lists I use ListJs.
It's well documented, has good performance even for large lists and it's very lightweight (7KB, despite being library agnostic).

Related

How can I add the same XML tags multiple times, with different content?

I have some problems with my code. I want to create an XML Document with JQuery / JavaScript. I am now at the point, where I want to create a few Tags and populate them each with the same tags but different content inside the tags.
Here is the code for better understand
function setItems(xmlDoc, channelTag){
const itemList = [];
const itemTitle = xmlDoc.createElement("title");
const itemLink = xmlDoc.createElement("link");
const itemGuid = xmlDoc.createElement("guid");
const itemMediaContent = xmlDoc.createElement("media:content");
const itemMediaDescription = xmlDoc.createElement("media:description");
itemList.push(itemTitle, itemLink, itemGuid, itemMediaContent, itemMediaDescription);
for (var i = 0; i < jsonObj.length; i++){
var item = xmlDoc.createElement("item");
channelTag.appendChild(item);
//Populate the <item> with the tags from "itemList" and content from "jsonObj"
$.each(itemList, function(index) {
$(channelTag).children('item')[i].appendChild(itemList[index]).textContent = jsonObj[0].title;
})
}
}
The Output of the code looks like this:
<item></item>
<item></item>
<item>
<title>Something</title>
<guid>Something</guid>
<link>Something</link>
<media:content>Something</media:description>
<media:description>Something</media:description>
</item>
It always populates the last item-Tag but not the ones above. What I want is that every item-Tag has the same child-Tags (e.g. title, link, guid and so on). Is there something i am missing some unique tags or something like that?
Edited:
Here is some minimal HTML and XML. The values for the function "xmlDoc" and "channelTag" just contains some Document Elements, where my items should be appended, like so:
<rss>
<channel>
<title>SomeTitle</title>
<atom:link href="Link" rel="self" type="application/rss+xml"/>
<link>SomeLink</link>
<description>SomeDesc</description>
<item></item>
<item></item>
<item></item>
</channel>
</rss>
<div class="col-5 col-sm-5 col-lg-3 order-2 count">
<a class="guid1"><img class="card-img image1"></a>
</div>
<div class="col-7 col-sm-7 col-lg-5 order-2">
<div class="card-body">
<a class="guid1">
<h5 class="card-title title1 overflow-title"></h5>
</a>
<p class="card-text body1 text-body overflow-body"></p>
<div class="card-body subtitle">
</div>
</div>
</div>
There are several issues with your code but the area we mostly want to focus on is this:
for (var i = 0; i < jsonObj.length; i++){
var item = xmlDoc.createElement("item");
channelTag.appendChild(item); // you're adding a node here
$.each(itemList, function(index) {
$(channelTag).children('item')[i].appendChild(... // and here
})
}
Instead of appending nodes multiple times per iteration, you should create and populate your node before add it it to channelTag.
Here's a way your could do it:
// use a "$" sign as a variable name prefix, so you know it's a Document Element and not a regular javascript variable
var $item = xmlDoc.createElement("item");
// you don't need jQuery for this iteration
itemList.forEach(function (item, index) {
$item.appendChild(itemList[index]).textContent = jsonObj[0].title;
});
// if "channelTag" is a Document Element, rename it "$channelTag"
$channelTag.appendChild(item);
Couple things about the code above:
you don't need jQuery, use forEach instead
there is no way telling what type is channelTag. If it is a selector (of type string), use $(selector), but you are using the appendChild() method before, suggesting it's actually a Document Element. In that case you don't need to wrap it with $()
I don't have the context needed to test this code, so no guarantee it'll work out of the box. But try and re-read your code and go through it top-to-bottom. For each variable, describe its type and value. I found that to be helpful when I'm lost in code.

jQuery find not working with object array

This code doesn't work
var next = $("#orders").find(".next");
if (next.length == 1) {
var address = $(next[0]).find(".directionsAddress");
var destination = $(address[0]).text();
}
<div id="orders" class="ui-sortable">
<div id="companyAddress" class="noDisplay">101 Billerica Avenue, North Billerica, MA</div>
<div id="companyPhone" class="noDisplay">9788353181</div><div class="next"></div>
<div class="lat">42.616007</div>
<div class="lng">-71.31187</div>
<div id="section1611" class="sectionMargin borderRad">
<div class="directionsAddress noDisplay">92+Swan+Street+Lowell+MA</div>
It is suppose to find one div with a class of "next" that I know exists on the page, then within that one item of the result set array, there will be one div with a class name of directionsAddress.
The "next" array is coming back with a length of 1, so it looks like the problem is with my $(next[0]).find because the address array is coming back as 0 length and I am making a syntax error of some sort that I don't understand.
This should do what you want. You need to find the parent (or alternatively, the sibling of .next) then try to find the applicable .directionsAddress.
var next = $("#orders").find(".next");
if (next.length == 1) {
var destination = $(next).parent().find(".directionsAddress");
alert($(destination).text());
}
Working fiddle: https://jsfiddle.net/00fgpv6L/

Sorting alphabetically in JQuery with two groups

I've got a todo list. Each row has a star icon that you can click, exactly like gmail. The difference here is that if you click a star it should sort to the top (higher priority), but also re-sort within the starred group by ascending alpha. Unstarred items sort below, also sorted by ascending alpha. Everything is working as expected except for the alpha sorting. Below is the sort function where I'm doing that. I've verified that everything works below except the //sort the arrays by alpha bit...
Sort fail:
function sortTasks(currList) {
var starredTasks = [];
var unstarredTasks = [];
//create arrays
$('li.task').each(function(){
if ($(this).children('img.star').attr('src') == "images/star_checked.gif") {
starredTasks.push($(this));
} else {
unstarredTasks.push($(this));
}
});
//sort the arrays by alpha
starredTasks.sort( function(a,b){ ($(a).children('p.task-name').text().toUpperCase() > $(b).children('p.task-name').text().toUpperCase()) ? 1 : -1;});
unstarredTasks.sort( function(a,b){ ($(a).children('p.task-name').text().toUpperCase() > $(b).children('p.task-name').text().toUpperCase()) ? 1 : -1;});
//draw rows starred first, unstarred second
$(currList).empty();
for (i=0; i < starredTasks.length; i++) {
$(currList).append(starredTasks[i]);
}
for (i=0; i < unstarredTasks.length; i++) {
$(currList).append(unstarredTasks[i]);
}
}
This array has been populated with the task rows in the order they were originally drawn. The data renders fine, but basically stays in the same order.
Example task row:
<div id="task-container" class="container">
<form name="enter-task" method="post" action="">
<input id="new-task" name="new-task" type="text" autofocus>
</form>
<h2 id="today">today</h2>
<ul id="today-list">
<li id="457" class="task">
<img class="star" src="images/star_checked.gif">
<p class="task-name" contenteditable>buy milk</p>
<p class="task-date"> - Wednesday</p>
</li>
</ul>
<h2 id="tomorrow">tomorrow</h2>
<ul id="tomorrow-list">
</ul>
<h2 id="future">future</h2>
<ul id="future-list">
</ul>
<h2 id="whenever">whenever</h2>
<ul id="whenever-list">
</ul>
</div>
Each item in the starredTasks array is an entire task row. I'm assuming that $(a) is the same level as $(li)?
and here's the function that triggers the sort:
$('body').on('click', 'img.star', function(){
var thisList = '#' + $(this).parent('li').parent('ul').attr('id');
if ($(this).attr('src') == 'images/star_checked.gif') {
$(this).attr('src', 'images/star_unchecked.gif');
} else {
$(this).attr('src', 'images/star_checked.gif');
}
sortTasks(thisList);
});
Also, I doubt it's worth mentioning, but the data is stored in mySQL and prepopulated via php.
I wasn't sure of a way to use .sort() directly on the $('li') without splitting it into separate arrays...
Anybody see my goof?
I don't see where you're adding the sorted list back into the DOM. If you're not, then that's the problem. Sorting an array of elements doesn't update the DOM at all.
Furthermore, your sorting is very expensive. It's better to map an array of objects that have the elements paired with the actual values to sort.
Finally, you appear to be using the same ID multiple times on a page. That's just wrong. it may work with jQuery's .children(selector) filter, but it's still wrong. You need to change that.
Here I map an array of objects that contain a text property holding the text to sort and a task property that holds the element.
I changed p#task-name to p.task-name, so you should change that to class="task-name" on the elements.
Then I do the sort using .localeCompare(), which returns a numeric value.
Finally, the .forEach() loop appends the elements to the DOM.
var data = starredTasks.map(function(t) {
return { task: t,
text: $(t).children('p.task-name').text().toUpperCase()
};
}).sort(function(obj_a, obj_b) {
obj_a.text.localeCompare(obj_b.text);
}).forEach(function(obj) {
original_container.append(obj.task);
});
This assumes starredTasks is an actual Array. If it's a jQuery object, then do starredTasks.toArray().map(func....
The original_container represents a jQuery object that is the direct parent of the task elements.

Combine divs within a for loop

Is it possible to use a JavaScript for loop to combine a number of div's? I have 16 sets of these I am wanting to put into a for loop. The problem I am having is that its HTML not JavaScript I am trying to do this with. I haven't seen anything so far on how to go about this. Thanks for any help or suggestions.
What the following code does is catch the data pre-defined from a 16X17 table and inserts it into one cell in my document. I then have have other code using the div id's that makes visible just the one I need.
<div id="101" class="hidden"><script>document.write(tab1a1)</script></div>
<div id="102" class="hidden"><script>document.write(tab1a2)</script></div>
<div id="103" class="hidden"><script>document.write(tab1a3)</script></div>
<div id="104" class="hidden"><script>document.write(tab1a4)</script></div>
<div id="105" class="hidden"><script>document.write(tab1a5)</script></div>
<div id="106" class="hidden"><script>document.write(tab1a6)</script></div>
<div id="107" class="hidden"><script>document.write(tab1a7)</script></div>
<div id="108" class="hidden"><script>document.write(tab1a8)</script></div>
<div id="109" class="hidden"><script>document.write(tab1a9)</script></div>
<div id="110" class="hidden"><script>document.write(tab1a10)</script></div>
<div id="111" class="hidden"><script>document.write(tab1a11)</script></div>
<div id="112" class="hidden"><script>document.write(tab1a12)</script></div>
<div id="113" class="hidden"><script>document.write(tab1a13)</script></div>
<div id="114" class="hidden"><script>document.write(tab1a14)</script></div>
<div id="115" class="hidden"><script>document.write(tab1a15)</script></div>
<div id="116" class="hidden"><script>document.write(tab1a16)</script></div>
<div id="117" class="hidden"><script>document.write(tab1a17)</script></div>
Update: Pulling data from table
<!--Start- Takes Assembly number from Data Table--> <!--Change "<Col1;" equala columns-->
for (var x = 1; x<Col1; x++){window["aa"+x] = document.getElementById("part1Table").rows[0].cells[x+1].innerHTML;}
<!--End--- Takes Assembly number from Data Table-->
<!--Start- Takes Assembly Rows from Data Table--> <!--Change "<Row1;" equals rows-->
for (var y = 1; y<Row1+1; y++){window["rows"+y] = document.getElementById("part1Table").rows[y].cells[1].innerHTML;}
<!--End- Takes Assembly Rows from Data Table-->
<!--Start- Takes Part number from Data Table--> <!-- "<Col1;" equals columns----> <!-- If a Column is added to main table add a new line below---->
for (var z1 = 1; z1 <Col1; z1++) {window["tab1a"+z1] = document.getElementById("part1Table").rows[1].cells[z1 +1].innerHTML;}
for (var z2 = 1; z2 <Col1; z2++) {window["tab2a"+z2] = document.getElementById("part1Table").rows[2].cells[z2 +1].innerHTML;}
for (var z3 = 1; z3 <Col1; z3++) {window["tab3a"+z3] = document.getElementById("part1Table").rows[3].cells[z3 +1].innerHTML;}
Try using AngularJS (which is a javascript framework that you can easily add to your html page (see AngularJS.com for a quick intro))
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body ng-app='myApp'>
<div ng-repeat='entry in entries'>
<div id={{entry.id}} class="hidden"><script>document.write({{entry.table}})</script></div>
</div>
<script src='app.js'></script>
</body>
</html>
Then within a controller in a file called app.js you would make a array of objects 1...100 or whatever number you want and pass it to a scope like this
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', function($scope) {
$scope.entries = [{
id: '101', table: 'tab1a1'}, {id: '102', table: 'tab1a2'}]
// extend to your own range
});
having a bunch of sequential variables like tab1a1 is difficult to work with. Can you put those in an array? Then...
var b=document.getElementsByTagName('body')[0];
for(var i=1; i<18; i++){
var d=document.createElement('div'); //make a div element
d.id='10'+i; //assign sequential id
d.innerHTML=tab1a[i]; //put the the array element content corresponding to that number into the new div element
b.appendChild(d); //place new element into the DOM
}
Another option would be to use excel and basically make a column with on the rows 101,102,103... And a similar same column for the tables. Then just add the code lines in string format and bind the colums together with &. Copy everything and paste this in notepad and then in your code!
Ex:
="<div id='" & A1 & "' etc etc
Thank you to everyone who gave suggestions. I spent a few more hours with the code and came up with the following. It works great in testing. I can now add and remove rows and columns from my table without having to change the code.
for (var w = 1; w <Row1; w++)
for (var z1 = 1; z1 <Col1; z1++) {window["tab" + w + "a" + z1] = document.getElementById("part1Table").rows[w].cells[z1 +1].innerHTML;}
This replaces my 17 lines of the following:
for (var z17 = 1; z17 <Col1; z17++) {window["tab17a"+z17] = document.getElementById("part1Table").rows[17].cells[z17 +1].innerHTML;}
This is very good because my new table has 50+ rows.

Table with large number of rows is slow to update

I have a page with a very large table (several thousand rows).
The table shows a subset of data specified by certain filters. I need the table to update whenever one of the filters changes.
Basically, there are a few check boxes and a text box, whenever one of these changes I reload the table to display only those rows which fit the criteria specified by the user.
I'm doing this by clearing the table and reloading the rows that match the filters. This works but has proven to be very slow.
Here is my javascript code:
function reloadTable() {
var tablebody = document.getElementById("tablebody");
while(tablebody.hasChildNodes()) tablebody.removeChild(tablebody.firstChild);
filter = new FilterChecker();
for (var i=0;i<rows.length;i++) {
if (filter.isVisible(rows[i]))
addRowToTable(rows[i]);
}
}
Is there a way to make it faster?
Alright, sorry for the delay, but I got wrapped up in work. I came up with a nice set of logic that illustrates what you need.
FIDDLE
I created some simple html to illustrate the point. It contains two drop downs to mimic the filters and 8 data rows to mimic your data grid.
<div id="body">
<select id="filterA" class="filter" name="states">
<option value="filterACT">Connecticut</option>
<option value = "filterAMA">Mass</option>
</select>
<select id="filterB" class="filter" name="towns">
<option value="filterBBT">Big Town</option>
<option value = "filterBST">Small Town</option>
</select>
<div id="grid">
<div class="row filterACT filterBBT">BigTown CT 1</div>
<div class="row filterACT filterBBT">BigTown CT 2</div>
<div class="row filterACT filterBST">SmallTown CT 1</div>
<div class="row filterACT filterBST">SmallTown CT 2</div>
<div class="row filterAMA filterBBT">BigTown MA 1</div>
<div class="row filterAMA filterBBT">BigTown MA 2</div>
<div class="row filterAMA filterBST">SmallTown MA 1</div>
<div class="row filterAMA filterBST">SmallTown MA 2</div>
</div>
</div>
</br>
<hr/>
<div>LOG</div>
<hr/>
<div id="log"></div>
The log div is simply to show an output, which I think would be helpful. Each row is identified by class 'row' followed by another series of classes. These classes help determine what their filter data is. You would need to set this programmatically when building the grid. Additionally, these class names must match the filter option values. You can see that the first row has classes from the first option in filterA and from the first option in filterB.
Now, the javascript is a bit verbose, but you can refactor to your hearts content. Sometimes I find it easier when things are explicit when you are trying to understand them. Also, admittedly, I would do this with jQuery, so my pure Javascript isn't as sharp.
var elements = document.getElementsByClassName('filter');
writeToLog("Filter elements found: " + elements.length);
for(var e = 0;e < elements.length;e++)
{
elements[e].onchange =function() {
writeToLog('Filter event fired for id:'+this.id);
filterChange();
};
}
First I get all the elements with the filter class, this would be your filters. I then iterate over them and set their onchange event to call the filterChange() method. The writeToLog() method calls are just for output purposes.
function filterChange() {
var filterClasses = [];
for(var i = 0;i<elements.length;i++) {
writeToLog('Pushing ('+elements[i].value+') into filter class variable.');
filterClasses.push(elements[i].value);
}
In the first part of the function I get all the select filter option values and put them into an array.
writeToLog('Filter classes: ' + filterClasses);
var rows = document.getElementsByClassName('row')
writeToLog('Row count: ' + rows.length);
I then get all of the rows, in my grid and start to iterate over them:
for(var j = 0;j<rows.length;j++)
{
writeToLog('Checking row: ' + rows[j].className);
var rowIsHidden = false;
Once I have a row, in the loop, I iterate the filter classes in the array and see if this row's classes have it. If not, I set rowIsHidden to true, otherwise it stays false.
for(var k = 0;k<filterClasses.length;k++)
{
writeToLog('Checking for class: ' + filterClasses[k]);
if(rows[j].className.indexOf(filterClasses[k]) < 0)
{
writeToLog('Class not found, hide this row.');
rowIsHidden = true;
break;
}
}
Before the loop moves to the next row, I set the display style based on the rowIsHidden value.
writeToLog('Row is hidden: ' + rowIsHidden);
rows[j].style.display = rowIsHidden ? 'none' : 'block';
}
}
By all means, this can be cleaned up and certainly optimized, but I think the intent and logic is fairly clear. Hope this helps and feel free to question any of it :)

Categories

Resources