0

I have a page containing multiple tabs per region.

enter image description here

Each row in the table has a class with each region that its impacted by.

<tr class="apac emea americas">...</tr> <tr class="apac emea">...</tr>

When a tab is clicked, it filters out the table and removes anything where the condition is not met.

$('#' + tab).find("#trainingEvents .results tr:not(.Americas.EMEA.APAC)").remove(); <- This is the ALL tab

Each of the tabs is pretty easy to understand except for "Multiple" which is what my question relates to.

The condition needs to be, remove rows that do not contain 2 of the 3 possible regions. For example:

<tr class="amea apac"></tr> = True

<tr class="apac">...</tr> = False, Remove it

How can I accomplish this filter? Just needs to meet any 2 combinations of the 3 possible options

SBB
  • 8,560
  • 30
  • 108
  • 223
  • Can you give enough of your HTML, CSS and jQuery (see: "[MCVE](http://stackoverflow.com/help/mcve)") that we can accurately reproduce this situation? Trying to guess what you want, and what you might have (especially given your [comment to Verhaeren's answer](http://stackoverflow.com/questions/27027004/jquery-filter-row-by-condition#comment42577587_27027259), is a recipe for frustration and not-quite-right answers. – David Thomas Nov 19 '14 at 21:28
  • Sorry, I thought my question was clear. There are 3 possible regions to filter by but there could be more classes on a row than just those 3. So, filtering by just a count of classes wouldn't work. I need to basically say if 2 of these regions in the array are not applied to this row, then remove it. – SBB Nov 19 '14 at 21:30

2 Answers2

3

I'd suggest the following:

// collating the 'regions':
var regions = ['americas', 'emea', 'apac'],
// initialising an array to use, later:
    foundClasses = [];

// iterating over the 'tr' elements, filtering them:
$('tr').filter(function () {
    // using Array.prototype.forEach to filter the classList of the element:
    foundClasses = Array.prototype.filter.call(this.classList, function (c) {
        // 'c' is the current class in the classList we're iterating over,
        // if it's in the array we return that array to the 'foundClasses':
        if (regions.indexOf(c) > -1) {
            return c;
        }
    });
    // we keep the the element in the jQuery collection (of 'tr' elements),
    // if we have only 1 (or less...) classes found:
    return foundClasses.length < 2;
// removing those 'tr' elements:
}).remove();

var regions = ['americas', 'emea', 'apac'],
    foundClasses = [];

$('tr').filter(function () {
  foundClasses = Array.prototype.filter.call(this.classList, function (c) {
    if (regions.indexOf(c) > -1) {
      return c;
    }
  });
  return foundClasses.length < 2;
}).remove();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
    <tbody>
        <tr class="americas emea">
            <td>americas emea</td>
        </tr>
        <tr class="apac">
            <td>apac</td>
        </tr>
        <tr class="emea">
            <td>emea</td>
        </tr>
        <tr class="americas">
            <td>americas</td>
        </tr>
        <tr class="apac emea">
            <td>apac emea</td>
        </tr>
    </tbody>
</table>

To account for those browsers without access to Array.prototype.filter(), and possibly element.classList:

var regions = ['americas', 'emea', 'apac'],
  classes,
  foundClasses = [];

    $('tr').filter(function() {
      // creating an array by splitting the className property by white-space:
      classes = this.className.split(/\s+/);
      // crudely emptying the initialised array:
      foundClasses = [];
      // iterating over the array of classes using a for-loop:
      for (var i = 0, len = classes.length; i < len; i++) {
        // if the current element in the classes array is in the
        // foundClasses array:
        if (regions.indexOf(classes[i]) > -1) {
          // we push the current class into the foundClasses array:
          foundClasses.push(classes[i]);
        }
      }
      // as above:
      return foundClasses.length < 2;
    }).remove();

var regions = ['americas', 'emea', 'apac'],
  classes,
  foundClasses = [];

$('tr').filter(function() {
  classes = this.className.split(/\s+/);
  foundClasses = []; // crudely emptying the array
  for (var i = 0, len = classes.length; i < len; i++) {
    if (regions.indexOf(classes[i]) > -1) {
      foundClasses.push(classes[i]);
    }
  }
  return foundClasses.length < 2;
}).remove();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <tbody>
    <tr class="americas emea">
      <td>americas emea</td>
    </tr>
    <tr class="apac">
      <td>apac</td>
    </tr>
    <tr class="emea">
      <td>emea</td>
    </tr>
    <tr class="americas">
      <td>americas</td>
    </tr>
    <tr class="apac emea">
      <td>apac emea</td>
    </tr>
  </tbody>
</table>

References:

David Thomas
  • 249,100
  • 51
  • 377
  • 410
  • Amazing, thank you! Apologies if the question was confusing but you nailed it. – SBB Nov 19 '14 at 21:39
  • Is it possible to say where its `== 2` vs `<2` ? Trying to remove rows that do not contain exactly 2 of any combination of the classes. – SBB Nov 19 '14 at 21:43
  • 1
    There's no need to apologise, but the more help (and insight, context and convenience) you can provide the more likely you are to get better answers more quickly. I would have answered pretty much the same as Verhaeren based on your question, had I not had opportunity to read the comment to his answer first. You want to get rid of all `` elements without exactly two region-classes? If so, simply : `return foundClasses.length !== 2;` – David Thomas Nov 19 '14 at 21:44
  • Thanks, this is all working as expected except for one minor detail (which I didnt mention) but hopefully its fixable. I need this to work in IE8 and I am getting an error about this line ` foundClasses = Array.prototype.filter.call(this.classList, function(c) {` saying `array.prototype.filter is null or not an object` – SBB Nov 20 '14 at 00:23
  • I will sugest you that, in order to execute the less code as possible, you use to accomplish what you want, something different than a class, a custom attribute. The Array.prototype.filter.call by @DavidThomas was beautiful and made me upvote him, but with a good design of your elements you wouldn't need it and also you wouldn't be running with any error in ie8. – Verhaeren Nov 20 '14 at 00:37
  • What would you suggest @Verhaeren? The code works perfectly with what I need, I just happened to be testing in FF and not IE8. Is there another way to pull this off without prototype? – SBB Nov 20 '14 at 14:31
  • I update my answer to meet your requirements. Yet, keep in mind that maybe there is a way to working around the Array.prototype.filter.call error in ie8 (I don't know). Also notice in my answer a second approach that uses a custom attribute to filtering the selection (better code design). – Verhaeren Nov 20 '14 at 17:38
0

You use for that the function "filter": (UPDATE after another requirement to filter it)

$("tr").
filter(function(index){
    var classes = $(this).attr("class").split(" ");
    var regions = "americas,emea,apac,";
    var counter = 0;
    for(var i = 0, j = classes.length;i < j;i++){
        if(regions.indexOf(classes[i] + ",") >= 0){counter++;}
    }
    return counter != 2;
})
.remove();

That code will remove all the rows with less or more than 2 region classes.


FIDDLE HAS BEEN UPDATED TOO: http://jsfiddle.net/fac5tapz/8/


If you were using a custom attribute, it would be possible to spare some code:

<table border="1">
    <tr regions="apac emea"><td>first row do not remove</td></tr>
    <tr regions="apac emea"><td>second row do not remove</td></tr>
    <tr regions="apac"><td>third will be removed</td></tr>
    <tr regions="apac emea americas"><td>fourth will be remove</td></tr>
    <tr regions="apac emea"><td>fifth row do not remove</td></tr>
    <tr regions="apac emea americas"><td>sixth will be removed</td></tr>
    <tr regions="apac"><td>seventh will be removed</td></tr>
    <tr regions="americas emea"><td>eighth row do not remove</td></tr>
</table>

$("tr").
filter(function(index){
    var regions = $(this).attr("regions").split(" ");
    return regions.length != 2;
})
.remove();

Another fiddle for this version: http://jsfiddle.net/dkseknyw/2/

Verhaeren
  • 1,661
  • 9
  • 10
  • instead of doing it against a length, what about an array of accepted regions? These rows contains more classes than just the region data – SBB Nov 19 '14 at 21:24
  • 2
    Be clear in your question with exactly WHAT to filter and we can build a great filtering function to achieve anything :) – Verhaeren Nov 19 '14 at 21:26
  • `.remove()` is probably not the best way, what about using `.hide()`? Once you remove the table rows from the table, you cannot re-add them to the table unless they are stored somewhere. You'll run into a problem if a user toggles between different tabs. – Terry Nov 19 '14 at 21:31
  • I have a hidden table that I pull the data from each time you click a tab. I then remove the rows from that table, not the source – SBB Nov 19 '14 at 21:32
  • Also, if you want to filter base on length of the split class attribute, instead of using `classes.length !=2`, you can simply say `classes.length < 2`, which will target all rows with more than one class. – Terry Nov 19 '14 at 21:32