I have a simple HTML Form with one column having a select menu with 2 options: Moderate or High. I need to find all rows where the cell = "Moderate" and count the total of another cell in the same row, and repeat this for all rows where the cell = "High". Here's a JS Fiddle showing a sample completed table:
http://jsfiddle.net/8hn2H/
In this example the result for Total Moderate would be 1.6 and the result for Total High would be 8.7 (it's totalling the value in the Average Hours/Week column). The number of rows will not be fixed in advance - there could be 1 or more.
Here's the actual html for the form:
<div class="span8"><br>
<table id="activities" class="table table-bordered table-striped">
<thead>
<tr>
<th>Activity</th>
<th>Risk</th>
<th>Hours/Week</th>
<th>Weeks/Year</th>
<th>Average Hours/Week</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><input name="Activity" id="Activity" type="text" value=""/></td>
<td><div class="controls">
<select id="Risk">
<option></option>
<option>Moderate</option>
<option>High</option>
</select>
</div></td>
<td><input class="span1" id="A1" type="text" value=""/></td>
<td><input class="span1" id="B1" type="text" value=""/></td>
<td><input class="span1" id="C1" type="text" value=""/></td>
<td><button class="btn">Delete Activity</button></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td>Total Moderate</td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td>Total High</td>
<td></td>
</tr>
</tbody>
</table>
</div>
var moderateTotal = 0;
var highTotal = 0;
$("#activities").find("tr").each(function() {
var tr = $(this);
var level = tr.find("td:nth-child(2)").html();
var value = tr.find("td:nth-child(5)").html();
switch(level)
{
case "Moderate":
moderateTotal += value*1;
break;
case "High":
highTotal += value*1;
break;
}
});
$("#activities tr:last td:last").prev().html("High Total: " + highTotal);
$("#activities tr:last").prev().find("td:last").prev().html("Moderate Total: " + moderateTotal);
I use jQuery's .find() and .each() function to iterate over each row. I use find again to find the level and value (2nd and 5th columns) and a switch statement to add the value to the totals. I need to do value*1 to convert the string to an integer so it will add. Default behavior here is concatenate.
The last two lines set the value of the table cells. It'd be a lot easier if you could just give those a specific ID so you can just do $("moderateTotal").html() but since that wasn't given I used some jQuery selectors to do it.
Demo
There are a number of ways to attack this, but I'd strongly recommend that you consider jQuery DataTables. It has built in functionality for summing fields, sorting, filtering, etc and is MUCH easier than having to home-brew something. Plus, it looks far better than just a bare table with full Themeroller support.
Here's a simple example with a footer callback to do sums: http://datatables.net/release-datatables/examples/advanced_init/footer_callback.html
Because all of the data in the table is fully available to you via API, it's easy to do math on it. You --could-- simply filter the table based on the the moderate or high flag and show only that data, allowing the user to modify that view on the fly.
And, if your data ever gets really long, it has paging and ajax loading options to improve the speed on the user's level.
Related
I have a show/hide function on a web page where I want to improve the layout, but when I added a new table to put in headers for the data, the show/hide stopped working.
A snippet of the original code is:
<table id="cart-list">
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-12">
<tr class="cl-group">
<td class="cl-sh" title="Show [+] or Hide [-] this lists' products"> </td>
<td colspan="2">Cart List One:
<table id="cl-chg-1" class="cl-other cl-chg">
<tr>
<td>List Name:</td>
<td><input type="text" name="group_name[1]" value="Cart List One" maxlength="255" /></td>
</tr>
<tr>
<td>List Comments:</td>
<td><input type="text" name="group_comments[1]" maxlength="255" /></td>
</tr>
<tr>
<td><button type="button" onclick="cancelGroupEdit();">Cancel</button></td>
<td><button type="submit" name="change" value="1">Go</button></td>
</tr>
</table>
</td>
</tr>
</div>
<tr>
<td>Stuff Here</td>
</tr>
<tr>
<td>Stuff Here</td>
</tr>
</table>
And the js used is
var groups = document.getElementsByClassName("cl-sh");
if (groups) {
for (var i = 0; i < groups.length; i++) {
groups[i].onclick = function() {
this.classList.toggle('cl-a');
var productsDisplay = '';
if (this.classList.contains('cl-a')) {
productsDisplay = 'none';
}
var nextRow = this.parentNode.nextElementSibling;
while (nextRow) {
if (nextRow.classList.contains('cl-group')) {
break;
}
nextRow.style.display = productsDisplay;
nextRow = nextRow.nextElementSibling;
}
}
}
}
A fiddle can be seen here: https://jsfiddle.net/65j1nvpq/1/
The broken function with the additional table code added can be seen here: https://jsfiddle.net/cmgqeLf7/
I don't really understand why having an additional table is breaking the function.
Any suggestions on a fix for this?
What does your JS function do when you click on the +/- cell?
Firstly, it toggles whether the clicked cell contains the cl-a class. Then, it runs through all remaining rows in the same table, showing or hiding them as appropriate, until it finds a row with the class cl-group, or hits the end of the rows in that table.
Note the word same in the last paragraph.
In your first fiddle, you have one table. In your second fiddle, you have two, and the rows you wish to toggle the display of are not in the same table as the toggle button. This is why they are not showing/hiding when you click the toggle button.
I'm not sure exactly what your requirements for showing/hiding rows are, so I'm not going to propose a fix. However, understanding what the problem is may help you to find a fix for yourself.
I am using JS/Jquery to dynamically add rows to by Table. Here is a template that I found online:
<table id="tblCustomers" cellpadding="0" cellspacing="0" border="1">
<thead>
<tr>
<th>Name</th>
<th>Country</th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td><input type="text" id="txtName" /></td>
<td><input type="text" id="txtCountry" /></td>
<td><input type="button" onclick="Add()" value="Add" /></td>
</tr>
</tfoot>
</table>
From what I gather, in order to ensure that the actual cell content values get passed on Post I need to assign them to hidden fields.
How do I read the actual contents of the cells.
I tried:
$("#submitForm").click(function(){
alert("submit Clicked");
$('#tblCustomers tfoot td').each(function() {
var cellText = $(this).html();
});
});
however, All I got was this as return value:
Can you let me know what I need to do to get actual value of cell.
I need to make a dynamic table/item list and i would like to send the list of items back to server for processing. Sort of like a purchase list etc..
BTW: the backend is Django 2.0
Thanks for your help.
Tanmay
I have a hard JS/jQuery riddle ! Hard because I couldn't find it on Google nor here, neither now, nor months ago when I was looking for it previously.
A large framework is using checkboxes in a table:
<table class="ListTable">
<tr>
<td><input name="blnChecked[70_20]" type="checkbox" value="1" id="some_unusable_gobbledy_gook" /></td>
<td></td>...
</tr>
<tr>
<td><input name="blnChecked[71_20]" type="checkbox" value="1" id="some_more_unusable_gobbledy_gook" /></td>
<td></td>...
</tr>
<tr>
<td><input name="blnChecked[70_25]" type="checkbox" value="1" id="some_further_unusable_gobbledy_gook" /></td>
<td></td>...
</tr>
</table>
I now need to collect all checkbox name references into an array: 70_20, 71_20 and 70_25 in the above example. Then join them up, and submit them as a URL parameter to a different page (although this joining is not essential to my question).
Question: Using JS/jQuery on the same page, how do I get these references from the name strings in these (checked) checkboxes in an array ?
I prefer not to use regexes (a bit messy, or 'overkill' for such a seeming trivial matter imho), although such a solution is not off my table.
(If someone asks why the table is structured as such: This is not my doing. But I can see that when such a form, in which this table is submitted to a PHP page, the PHP stores all such checkboxes into a single array, which is very nice, and I wanted to achieve a similar effect with JS/jQuery.)
A way to create on client side the array is based on using:
.map()
string .replace()
$('#btn').on('click', function(e) {
var retVal = $('table.ListTable :checkbox[name^="blnChecked["]:checked').map(function(idx, ele) {
//
// if the name value has always the same format...
//
return ele.name.replace('blnChecked[', '').replace(']', '');
//
// or....
//
// return ele.name.split('[').pop().replace(']', '');
// return ele.name.substr(11, 5);
//return ele.name.replace(/blnChecked\[(.*?)\]/g, '$1')
}).get();
var param = $.param({'param': retVal.join(',')});
console.log('Array: ' + retVal);
console.log('URL param: ' + param);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="ListTable">
<tr>
<td><input name="blnChecked[7125_2355]" type="checkbox" value="1" id="some_unusable_gobbledy_gook" /></td>
<td></td>
</tr>
<tr>
<td><input name="blnChecked[71_20]" type="checkbox" value="1" id="some_more_unusable_gobbledy_gook" /></td>
<td></td>
</tr>
<tr>
<td><input name="blnChecked[70_25]" type="checkbox" value="1" id="some_further_unusable_gobbledy_gook" /></td>
<td></td>
</tr>
</table>
<button type="button" id="btn">Click Me</button>
I have a table with columns:
Artist
Song Title
Album
Add to playlist
I have some code that sorts the table rows by artist.
function sortAsc(a, b) {
var aText=$(a).find('.artist').text();
var bText=$(b).find('.artist').text();
return aText==bText?0:aText<bText?-1:1;
}
function sortTheTable(){
$(function() {
elems=$.makeArray($('tr:has(.artist)'));
elems.sort(sortAsc);
$('#songs').append(elems);
});
}
window.onload = function() {
document.getElementById("artistsort").onclick = function() {
sortTheTable();
}
}
And here is some HTML I'm using to test it:
<table class="table" style="table-layout:fixed">
<thead>
<tr>
<th>Artist/Band:</th><th>Song:</th>
<th>Album:</th>
<th style="padding-left:6%">Add to Playlist</th>
</tr>
</thead>
</table>
<table id="songs" class="table table-hover" style="table-layout:fixed">
<form action="">
<tr>
<td class="artist">Capital Cities</td><td class="songtitle">Safe and Sound</td><td>In a Tidal Wave of Mystery</td>
<td style="text-align:center"><input type="checkbox" name="addsong" value=""></td>
</tr>
<tr>
<td class="artist">Capital Cities</td><td class="songtitle">Kangaroo Court</td><td>In a Tidal Wave of Mystery</td>
<td style="text-align:center"><input type="checkbox" name="addsong" value=""></td>
</tr>
<tr>
<td class="artist">Two Door Cinema Club</td><td class="songtitle">Remember My Name</td><td>Beacon</td>
<td style="text-align:center"><input type="checkbox" name="addsong" value=""></td>
</tr>
<tr>
<td class="artist">Anamanaguchi</td><td class="songtitle">Blackout City</td><td>Dawn Metropolis</td>
<td style="text-align:center"><input type="checkbox" name="addsong" value=""></td>
</tr>
</form>
</table>
What ends up happening is that it sorts the rows by artist as intended, but the order of songs by the same artist gets mixed up every time "Artist/Band:" gets clicked.
I don't want a solution that involves some plugin, I can find plenty of those on my own.
I just want to know what is going on here.
welcome to the subtleties of sorting algorithms. As noted in my comments, you're asking for what is known as a stable sorting algorithm. But browser implementations of sort() aren't required to be stable. Some are; some aren't. To solve your problem reliably you'll have to use your own sort algorithm rather than the built-in. More information in this question: Fast stable sorting algorithm implementation in javascript
I have a table that lists a lot of data (each row is an offer, and there can be a couple hundred of them). Each offer has data associated with it: a group ID (multiple offers can be in the same group), a cost, if you must trade other items as a requirement to fulfill offer, etc.
I've made a fiddle to demonstrate:
http://jsfiddle.net/gd5Nd/
and since i need to include code with fiddle links:
<label class='checkbox'>
<input type='checkbox' class='toggle' id='filterToggle' value='true' data-toggle='filtering' />Filtering Options</label>
<fieldset id='filtering'>
<label class='checkbox'>
<input type='checkbox' class='filter' value='1' data-type='req' />Offers with Required Items</label>
<label class='checkbox'>
<input type='checkbox' class='toggle' id='groupToggle' value='true' data-toggle='groups' />Filter By Group</label>
<fieldset id='groups'>
<div class="col-md-3">
<label class='checkbox'>
<input type='checkbox' class='filter' value='11' data-type='group' />Cap Boosters</label>
</div>
<div class="col-md-3">
<label class='checkbox'>
<input type='checkbox' class='filter' value='475' data-type='group' />Datacores</label>
</div>
<div class="col-md-3">
<label class='checkbox'>
<input type='checkbox' class='filter' value='24' data-type='group' />Gunslinger Implants</label>
</div>
</fieldset>
</fieldset>
<table class='table table-striped table-hover table-condensed' id='offerList'>
<tr>
<th>Offer</th>
<th>Items Req</th>
<th>LP Cost</th>
<th>ISK Cost</th>
</tr>
<tr id='offer-1607' data-group='11' data-isk='60000' data-lp='60' data-req='1'>
<td><a href='offer/1607/'>20x Navy Cap Booster 25</a>
</td>
<td>1</td>
<td>60</td>
<td>60,000</td>
</tr>
<tr id='offer-1608' data-group='11' data-isk='125000' data-lp='125' data-req='1'>
<td><a href='offer/1608/'>20x Navy Cap Booster 50</a>
</td>
<td>1</td>
<td>125</td>
<td>125,000</td>
</tr>
<tr id='offer-1609' data-group='11' data-isk='185000' data-lp='185' data-req='1'>
<td><a href='offer/1609/'>20x Navy Cap Booster 75</a>
</td>
<td>1</td>
<td>185</td>
<td>185,000</td>
</tr>
<tr id='offer-1431' data-group='475' data-isk='250000' data-lp='250' data-req='0'>
<td><a href='offer/1431/'>5x Datacore - Amarrian Starship Engineering</a>
</td>
<td>0</td>
<td>250</td>
<td>250,000</td>
</tr>
<tr id='offer-1432' data-group='475' data-isk='250000' data-lp='250' data-req='0'>
<td><a href='offer/1432/'>5x Datacore - High Energy Physics</a>
</td>
<td>0</td>
<td>250</td>
<td>250,000</td>
</tr>
<tr id='offer-1433' data-group='475' data-isk='250000' data-lp='250' data-req='0'>
<td><a href='offer/1433/'>5x Datacore - Laser Physics</a>
</td>
<td>0</td>
<td>250</td>
<td>250,000</td>
</tr>
<tr id='offer-1434' data-group='475' data-isk='250000' data-lp='250' data-req='0'>
<td><a href='offer/1434/'>5x Datacore - Mechanical Engineering</a>
</td>
<td>0</td>
<td>250</td>
<td>250,000</td>
</tr>
<tr id='offer-1435' data-group='475' data-isk='250000' data-lp='250' data-req='0'>
<td><a href='offer/1435/'>5x Datacore - Nanite Engineering</a>
</td>
<td>0</td>
<td>250</td>
<td>250,000</td>
</tr>
<tr id='offer-1464' data-group='11' data-isk='250000' data-lp='250' data-req='1'>
<td><a href='offer/1464/'>20x Navy Cap Booster 100</a>
</td>
<td>1</td>
<td>250</td>
<td>250,000</td>
</tr>
<tr id='offer-249' data-group='24' data-isk='375000' data-lp='375' data-req='0'>
<td><a href='offer/249/'>1x Eifyr and Co. 'Gunslinger' Large Projectile Turret
LP-1001</a>
</td>
<td>0</td>
<td>375</td>
<td>375,000</td>
</tr>
<tr id='offer-252' data-group='24' data-isk='375000' data-lp='375' data-req='0'>
<td><a href='offer/252/'>1x Eifyr and Co. 'Gunslinger' Medium Projectile Turret
MP-801</a>
</td>
<td>0</td>
<td>375</td>
<td>375,000</td>
</tr>
<tr id='offer-255' data-group='24' data-isk='375000' data-lp='375' data-req='0'>
<td><a href='offer/255/'>1x Eifyr and Co. 'Gunslinger' Motion Prediction
MR-701</a>
</td>
<td>0</td>
<td>375</td>
<td>375,000</td>
</tr>
<tr id='offer-258' data-group='24' data-isk='375000' data-lp='375' data-req='0'>
<td><a href='offer/258/'>1x Eifyr and Co. 'Gunslinger' Small Projectile Turret
SP-601</a>
</td>
<td>0</td>
<td>375</td>
<td>375,000</td>
</tr>
<tr id='offer-261' data-group='24' data-isk='375000' data-lp='375' data-req='0'>
<td><a href='offer/261/'>1x Eifyr and Co. 'Gunslinger' Surgical Strike
SS-901</a>
</td>
<td>0</td>
<td>375</td>
<td>375,000</td>
</tr>
</table>
JS:
$(document).ready(function () {
// !- Bind checkboxes to show filters
$('input[type="checkbox"].toggle').bind("change", function () {
if (typeof $(this).data('toggle') != 'undefined') {
if ($(this).is(":checked")) {
$("#" + $(this).data('toggle')).show();
} else {
$("#" + $(this).data('toggle')).hide();
}
}
}).trigger('change');
$('input[type="checkbox"].filter').bind("change", function () {
switch ($(this).data('type')) {
case 'group':
var filtered = $('*[data-group="' + this.value + '"]');
console.log("Affected offers for " + this.value + ": " + filtered.length);
filtered.toggleClass('danger');
break;
case 'req':
var filtered = $('*[data-req="1"]');
filtered.toggleClass('danger');
break;
}
}).trigger('toggle');
});
The problem I'm having is that multiple filters may point to the same data row. For example: I have an offer that is part of group 11 (Cap Boosters), and it also requires items to trade. I have two filters that affect this: Has required items filter, and the group filter.
If I were to activate the group filter for group 11, I highlight the group 11 offers. However, when I activate the filter for offers with required items, I want it to remain highlighted. Instead, it removes the highlight.
This is because I'm toggling the class for the highlight on/off. I've tried show()/hide() which pretty much do the same thing (of course). One thing I tried that didn't work was addClass() and removeClass(). I thought it would append the class, so that if you had two filters that added the class, and then removed one filter, the other filter's class would still be active, but it seems that addClass only adds the class if it's not already present.
I could do some checking, such as if I choose to deactivate the group toggle, it will check to see if the req items toggle is still checked. However, that seems like a hack, and I'm not sure if it will work well (the example I have is a simplified version; in reality, there are about 10 groups and I would like to add more filters, so I'm not sure how feasible it would be to recursively check all filters and if they affect the data row every time one filter is checked/unchecked).
I was curious as to how best solve this problem of multiple filters pointing to the same data row.
Solution
I basically set up, as part of routine JS init, a 'filter container' object initially structured as so: rowID : Array(). Every time I add a filter, the filter will add itself to array for that row (so, having two filters in this example, each row could be filtered with group or req). Every time a filter is applied, we loop through the matching rows, and add/remove the filter from the list. If we're adding a filter, we go ahead and .hide() the element as well. If we remove the filter, we check to see if the array is empty, which means no filters are applied and we then .show() the element.
Sweet. A fellow EVE pilot.
I think your primary problem here is relying too much on simple notions like CSS classes or something to accomplish your goal. It sounds like what you need to do is keep track of all the different market items in a data structure, then go through and "turn on highlight" for the items you want. If it's already highlighted, who cares, otherwise turn it on (and vice versa). This is a strategy I typically use when I have complex hierarchies of collapsible items, or complex groupings such as what you're dealing with.
I think the solution you describe as a "hack" is fine - and is the only way I can think to do it simply. I had an idea involving adding an additional data-attribute for every filter option and then applying the styling via those instead of the class
IE:
<style>
.danger,
[data-req_style=1],
[data-group_style=11],
[data-group_style=475]{
...
}
</style>
Then rather than toggling the class you could safely add remove multiple filters without interfering with others. BUT that becomes really unmaintainable if you have an arbitrary or ever expanding list of filters.
Your "hack" solution is how I would implement it were it my project. Class selectors and determining the status of check boxes are comparably pretty fast so I wouldn't worry too much about performance even with really large data sets.