jQuery Concatenate multiple DOM node references into one list - javascript

Lets say for example I have the following HTML:
<div class="level0">
<div>level 0 #1</div>
<div class="contents">
<div class="level1">
<div>level 1 #1</div>
<div class="contents">
<div class="level2"><div>level 2 #1</div></div>
</div>
</div>
</div>
</div>
<div class="level0">
<div>level 0 #2</div>
<div class="contents">
<div class="level1"><div>level 1 #2</div></div>
<div class="level1"><div>level 1 #3</div></div>
<div class="level1">
<div>level 1 #4</div>
<div class="contents">
<div class="level2"><div>level 2 #2</div></div>
<div class="level2"><div>level 2 #3</div></div>
</div>
</div>
</div>
</div>
<div class="level0"><div>level 0 #3</div></div>
I want to get all of the references to nodes with class "level0", "level1", or "level2".
Then I want to iterate over them starting with the "level2" references, then going to "level1", then "level0".
For instance, the following code would work for what I am trying to do:
$(".level2").each(function(){
console.log($(this).children().first().text());
});
$(".level1").each(function(){
console.log($(this).children().first().text());
});
$(".level0").each(function(){
console.log($(this).children().first().text());
});
That would make the console output be the following:
level 2 #1
level 2 #2
level 2 #3
level 1 #1
level 1 #2
level 1 #3
level 1 #4
level 0 #1
level 0 #2
level 0 #3
As you can see, they are in order based on class, THEN by order in the HTML from top to bottom. This is what I want
However, I want to store this list of elements in this order so I can just use one loop for all of the elements.
The following code is similar to what I want but displays iterates through the rows in the wrong order.
var $rows = $(".level2, .level1, .level0");
$rows.each(function(){
console.log($(this).children().first().text());
});
This iterates through the rows using in-order traversal.
Is there any way to append these rows into a single variable that I can iterate over in the order I wanted way above?

A rough example using a for loop and _map
var allArr = [],
maxLevel = 2;
for (var i = maxLevel; i > -1; i--) {
var arr = $('.level'+ i ).children('div:first-child').map(function () {
return $.trim($(this).text());
}).get();
allArr = allArr.concat(arr);
}
console.log(allArr);
Needs to be refactored though.
Check Fiddle

I just stumbled upon a working answer looking through a plugins source code.
The solution uses jQuery function $.merge().
The following code accomplishes what I want to do:
var $rows = $(".level2");
var $rows = $.merge($rows,$(".level1"));
var $rows = $.merge($rows,$(".level0"));
$rows.each(function(){
console.log($(this).children().first().text());
});
I supposed if you wanted to get a little more tricky, the following code would also work:
var $allRows = $.merge($.merge($(".level2"),$(".level1")),$(".level0"));
$allRows.each(function(){
console.log($(this).children().first().text());
});
Check out this fiddle to see both options working.

Related

JQuery dom treeview hierarchy problems

So, I have this html code.
<div class="test">
A
<div class="test">
B
</div>
</div>
<div class="test">
C
<div class="test">
<div class="test">
D
</div>
E
</div>
</div>
And I am trying to structure the data to something like this using jQuery.
[0] = [0] = <div class="test">A</div>
[1] = <div class="test">B</div>
[1] = [0] = <div class="test">C</div>
[1] = [0] = <div class="test">C</div>
[1] = [1] = <div class="test">E</div>
But the problem I am facing is that I can't make jQuery ignore the first set of DIVS when its finished parsing (A AND B).
This is how far ive come.
getFor($(document).first());
function getFor($prev) {
// look for initial foorloop
var $cur = $prev.find(".class").first();
if (!$cur.length) {
return;
}
// save it
collection.push($cur);
//look for descendants
// has childreN? then create a new array instead?
getFor($cur);
// We found our first hit. go thru nodes
}
console.log(collection);
I know I am on the right path, but without involving .remove() and start from the $(document), I dont know how to get to the second block of .class...
Thanks in advance
/xgx

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/

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 :)

How to sort nested divs depending upon content

I've the following HTML schema
<div id="txm_left_column">
<div class="id">
<h2>Some Name1</h2>
<div>// another list</div>
</div>
<div class="id">
<h2>Some Name2</h2>
<div>// another list</div>
</div>
</div>
The user is able to add items to that list and all Divs should be in alphabetical order by the name in the h2.
I've two options
1) i need to be able to insert a new item in between the 2 divs where the alphabetical order is correct
2) re organise the list entirely.
My approach was option 2, to sort the Divs in alphabetical order by the name in the h2
I came up with the following code to try to order it but this code creates a new List of ordered H2s without the divs. then I tried to do option 1) by using the same function but trying to insert into something like this (upA < upB) ? -1 : (NEW_ITEM_NAME> upB) ? 1 : 0 but that will cause a problem as that doesn't break the sort.
I was wondering 2 things, one is how could i break the sort as return 0 would not break it. or any help on how could i organise my list.
Thanks
jQuery("#txm_left_column > div").children("h2").sort(function(a, b) {
var upA = jQuery(a).text().toUpperCase();
var upB = jQuery(b).text().toUpperCase();
return (upA < upB) ? -1 : (upA > upB) ? 1 : 0;
}).appendTo(selector);
but this code creates a new List of ordered H2s without the divs.
That's because you are sorting h2 elements and not the div elements:
jQuery("#txm_left_column > div").sort(function(a, b) {
var upA = jQuery('> h2', a).text().toUpperCase();
var upB = jQuery('> h2', b).text().toUpperCase();
// ...
}).appendTo(selector);
wanting to comment but have not met the "50 reputation" quota,
the Vohuman answer is problematic when introducing duplicates:
<div id="txm_left_column">
<div class="id">
<h2>80</h2>
<div>// another list for name 80</div>
</div>
<div class="id">
<h2>80</h2>
<div>// another list for name 80</div>
</div>
<div class="id">
<h2>81</h2>
<div>// another list for name 81</div>
</div>
<div class="id">
<h2>100</h2>
<div>// another list for name 100</div>
</div>
<div class="id">
<h2>100</h2>
<div>// another list for name 100</div>
</div>
</div>
-
jQuery("#txm_left_column > div").sort(function(a, b) {
var upA = jQuery('h2', a).text().toUpperCase();
var upB = jQuery('h2', b).text().toUpperCase();
return upA < upB;
}).appendTo('#txm_left_column');
result:
81
// another list for name 81
80
// another list for name 80
80
// another list for name 80
100
// another list for name 100
100
// another list for name 100
see http://jsfiddle.net/std57rm4/
this only happens when a duplicate name or number you are sorting on is introduced
at this moment I don't have a solution, but it should be something along the lines of counting how many times a duplicate has been encountered when sorting and then adding or subtracting that number from the next sort item or items depending on the ascending or descending direction you are sorting in

sorting elements using jquery

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).

Categories

Resources