Hiding an element with multiple toggles - 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.

Related

Search not giving the exact results, Javascript [duplicate]

This question already has answers here:
Jquery Search - Case insensitive
(3 answers)
Closed 4 years ago.
I have this search function it can search my table but only if I do it exactly the same with data from the table like I have University Gymnasium Building and I must type University for it to work and if I type the 3rd word which is Building it doesn't work it doesn't give result as well as using lowercase no result. Any changes need to be changed in the script?
Here's what I did
building.blade.php
$("body").on("input", "#search-building", function() {
var text_filter = $(this).val();
var tr = $('tr');
$("table").find("tbody").find("tr").hide();
$("table").find("td").show();
$("table").find("tbody").find("tr:contains(" + text_filter + ")").show();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="input-group col-xs-4 col-md-6">
<input type="text" name="search" id="search-building" class="form-control" placeholder="Search..." required>
<span class="input-group-btn"></span>
</div>
<table id="office">
<tbody><tr>
<th>Office</th>
</tr>
<tr>
<td>College of Computer Studies - Deans Office</td>
</tr>
<tr>
<td>Academic Center for Continuing Education and Student Success</td>
</tr>
<tr>
<td>Innovation and Technology Support Office</td>
</tr>
<tr>
<td>Computer Studies Faculty Office</td>
</tr>
<tr>
<td>eLearning Competency and Research Center</td>
</tr>
<tr>
<td>Research and Development Coordinating Office</td>
</tr>
<tr>
<td>Technical Support Group office</td>
</tr>
<tr>
<td>CPE Department Office</td>
</tr>
</tr>
</tbody>
</table>
If you want case-insensitive lookup, don't use :contains as it is case sensitive. You can use filter:
var text_filter = $(this).val();
var rex = new RegExp(text_filter, 'i');
$('table').find('tbody').find('tr').hide();
$('table tbody tr').filter(function () {
return rex.test($(this).text())
}).show();
That tests every line against a Regular Expression with your text and with the case-insensitive flag enabled.

Table not quite being sorted properly

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

JQuery Tablesorter - Filter by clicking link

I'm using #Mottie's excellent fork of Tablesorter and would like to be able to filter table column(s) with external links.
It isn't strictly necessary, but I'd like to make multiple clicks toggle the filter on and off. Alternatively, I could always add an All Records link that resets the column(s).
I don't need to combine filters in a single column. In other words, the data wouldn't be both January and October.
I found a table sort with external link demo, but that one sorts, not filters, and it doesn't toggle.
I also found a table filter with buttons demo which is pretty close. However, as I mentioned, I'd really like links instead, would like to have the links toggle if possible, and don't need the filters to combine.
Thanks in advance.
This was actually a lot easier than I thought. Here's a working demo that came directly from Mottie's demo code above. I replaced the buttons with links, renamed the associated class so it made more sense and replaced the class on the JavaScript function to match the one on the links.
Fair warning: I don't claim to know everything, so my modifications could have very silly errors.
$('.link-filter').click(function() {
var filters = $('table').find('input.tablesorter-filter'),
col = $(this).data('filter-column'),
txt = $(this).data('filter-text');
// filters.val('');
filters.eq(col).val(txt).trigger('search', false);
});
The filters in the various columns combine, but I only need a single column filter at the moment, so that's not really an issue for me.
Country:<br>
All Countries |
Netherlands |
Belgium |
Germany
<br /><br />
<table id="festivaloverzichttable" class="tablesorter">
<thead>
<tr>
<th width="17%" data-placeholder="Search...">Event</th>
<th width="18%" data-placeholder="Search...">Date</th>
<th width="9%" data-placeholder="Search...">Duration</th>
<th width="12%" data-placeholder="Search...">Place</th>
<th width="10%" data-placeholder="Search...">Country</th>
<th data-placeholder="Zoek...">Genre(s)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Event 1</td>
<td data-date="06-02">TBA</td>
<td>2</td>
<td>Oisterwijk</td>
<td>Netherlands</td>
<td>Hardstyle</td>
</tr>
<tr>
<td>Event 2</td>
<td data-date="10-11">11 October t/m 13 October</td>
<td>3</td>
<td>Volkel</td>
<td>Netherlands</td>
<td>Pop, Rock, Urban, Electronic</td>
</tr>
<tr>
<td>Event 3</td>
<td data-date="06-02">TBA</td>
<td>1</td>
<td>Amsterdam</td>
<td>Netherlands</td>
<td>Electronic</td>
</tr>
<tr>
<td>Event 4</td>
<td data-date="09-01">TBA</td>
<td>1</td>
<td>Utrecht</td>
<td>Netherlands</td>
<td>Electronic, Urban</td>
</tr>
<tr>
<td>Event 5</td>
<td data-date="07-06">6 July - 7 July</td>
<td>2</td>
<td>Beek en Donk</td>
<td>Netherlands</td>
<td>Electronic, Hardstyle</td>
</tr>
...
</tbody>
</table>​
Javascript
$("#festivaloverzichttable").tablesorter({
sortList: [[0, 0]],
widgets: ['zebra', 'filter', 'saveSort'],
widgetOptions: {
filter_reset: 'button.reset'
}
});
$('.link-filter').click(function() {
var filters = $('table').find('input.tablesorter-filter'),
col = $(this).data('filter-column'),
txt = $(this).data('filter-text');
// filters.val('');
filters.eq(col).val(txt).trigger('search', false);
});

Checkboxes hide groups of rows from a table

I have a table that I would like to dynamically hide/reveal rows in, based on checkboxes at the top.
I would like to do this without jQuery, just vanilla Javascript.
I have seen numerous methods on this site and others, but always snippets of code,not complete working examples.
I don't want to give each row a separate div name, or whatever, I am assuming there is a way I can do this with a common class for the 3 types of rows I want to hide/reveal.
Can anyone please show me a working example which allows checkboxes to hide/show multiple rows from a table with vanilla Javascript?
Given HTML like the following:
<table>
<thead>
<tr>
<td>
<label>
<input type="checkbox" class="show" value="north" checked />North</label>
</td>
</tr>
<tr>
<td>
<label>
<input type="checkbox" class="show" value="south" checked />South
</label>
</td>
</tr>
<tr>
<td>
<label>
<input type="checkbox" class="show" value="outOfArea" checked />Out of area
</label>
</td>
</tr>
</thead>
<tbody>
<tr class="north">
<td>North One</td>
</tr>
<tr class="north">
<td>North Two</td>
</tr>
<tr class="outOfArea">
<td>Out-of-area One</td>
</tr>
<tr class="south">
<td>South One</td>
</tr>
<tr class="south">
<td>South Two</td>
</tr>
<tr class="north">
<td>North Three</td>
</tr>
<tr class="north">
<td>North Four</td>
</tr>
<tr class="south">
<td>South Three</td>
</tr>
<tr class="outOfArea">
<td>Out-of-area Two</td>
</tr>
</tbody>
</table>
The following jQuery seems to do as you seem to describe:
$('thead input[type=checkbox]').change(function(){
var self = this;
$(self).closest('table').find('tbody tr').filter('.' + self.value).toggle(self.checked);
});
JS Fiddle demo.
As it seems that you'd prefer a plain-JavaScript approach, I'd suggest the following (to work on the same HTML as posted above):
function toggle (e) {
var self = e.target,
toggleClass = '.' + self.value,
toToggle = document.querySelectorAll(toggleClass);
for (var i = 0, len = toToggle.length; i < len; i++) {
toToggle[i].style.display = self.checked ? 'table-row' : 'none';
}
}
var thead = document.querySelector('thead');
thead.addEventListener('change', toggle);
JS Fiddle demo.
References:
jQuery:
Attribute-equals ([attribute="value"]) selector.
change().
closest().
find().
filter().
toggle().
Plain JavaScript:
document.querySelector().
document.querySelectorAll().
EventTarget.addEventListener().
Looks like you are describing something along the lines of this example

Total Value in HTML Form Table from Select Menu

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.

Categories