Filter table with multiple filters

Actually, I am trying to create a specific each column search. Here I can manage the first column search with data type but using this data type I can't handle other columns search. For these circumstances, without using data type can I manage individual column search?

The procedure will be: If I select = operator and type column any input value and then after a search, the input value will get like one or more row that matches the similar value. In the same way, if I select < operator and this column any input value the less than input value will get if I select > operator and this column any input value the greater than input value will get if I select <= operator and this column any input value the less than or equal input value will get if I select >= operator and this column input value the greater than or equal input value will get.

How to solve this problem?

  const $available_qty = $("#available_quantity");
  const $regular_price = $("#regular_price");
  const $base_price = $("#base_price");
  const $avl_table = $available_qty.closest("table");
  const $rp_table = $regular_price.closest("table");
  const $bp_table = $base_price.closest("table");
  const $aggregate_value = $("#aggregate_condition");

  const aggrFn = {
  "=":  (a,b) => a == b,
  "<":  (a,b) => a  < b,
  ">":  (a,b) => a  > b,
  "<=": (a,b) => a <= b,
  ">=": (a,b) => a >= b,
  };


  $('button.avl').click(function(){
  const ag = $aggregate_value.val();
  const av = $available_qty.val().trim();  
  const $rowsQty = $avl_table.find("[data-qty]")
  $rowsQty.removeClass("u-none");
  if (av === "") return;
  $rowsQty.filter((i, el) => !aggrFn[ag](el.dataset.qty, +av)).addClass("u-none");
  })


  $('button.rp').click(function(){
      const ag = $aggregate_value.val();
      const rp = $regular_price.val().trim();
      const $rowsQty = $rp_table.find("[data-qty]")
      $rowsQty.removeClass("u-none");
      if (rp === "") return;
      $rowsQty.filter((i, el) => !aggrFn[ag](el.dataset.rp, +rp)).addClass("u-none");
  })


  $('button.bp').click(function(){
      const ag = $aggregate_value.val();
      const bp = $base_price.val().trim();
      const $rowsQty = $bp_table.find("[data-qty]")
      $rowsQty.removeClass("u-none");
      if (bp === "") return;
      $rowsQty.filter((i, el) => !aggrFn[ag](el.dataset.bp, +bp)).addClass("u-none");
  })
  table {
        width: 100%;
        border-collapse: collapse;
    }
    table thead th {
        padding: 15px;
    }
    table tbody tr td{
        padding: 10px;
        text-align: center;
    }
    .text-center{
        text-align: center;
    }
    .header-name{
        display: flex;
        justify-content: center;
        align-items: center;
    }
    .u-none{
        display:none;
    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

<table>
    <thead>
        <th class="text-center">
            Available Quantity
            <div class="header-name">
                <div>
                    <select id="aggregate_condition">
                        <option value="=">=</option>
                        <option value="<">&lt;</option>
                        <option value=">">&gt;</option>
                        <option value="<=">≤</option>
                        <option value=">=">≥</option>
                      </select>
                </div>
                <div>
                    <input type="text" id="available_quantity">
                </div>
                <div>
                    <button class="avl" type="submit">Apply</button>
                </div>
            </div>
        </th>
        <th class="text-center">
            Regular Price
            <div class="header-name">
                <div>
                    <select id="aggregate_condition">
                        <option value="=">=</option>
                        <option value="<">&lt;</option>
                        <option value=">">&gt;</option>
                        <option value="<=">≤</option>
                        <option value=">=">≥</option>
                      </select>
                </div>
                <div>
                    <input type="text" id="regular_price">
                </div>
                <div>
                    <button class="rp" type="submit">Apply</button>
                </div>
            </div>
        </th>
        <th class="text-center">
            Base Price
            <div class="header-name">
                <div>
                    <select id="aggregate_condition">
                        <option value="=">=</option>
                        <option value="<">&lt;</option>
                        <option value=">">&gt;</option>
                        <option value="<=">≤</option>
                        <option value=">=">≥</option>
                      </select>
                </div>
                <div>
                    <input type="text" id="base_price">
                </div>
                <div>
                    <button class="bp" type="submit">Apply</button>
                </div>
            </div>
        </th>
    </thead>
    <tbody>
        <tr data-qty="4">
            <td>4</td>
            <td>10</td>
            <td>12</td>
        </tr>
        <tr data-qty="9">
            <td>9</td>
             <td>12</td>
             <td>11</td>
        </tr>
        <tr data-qty="1">
            <td>1</td>
            <td>14</td>
            <td>12</td>
        </tr>
        <tr data-qty="0">
            <td>0</td>
            <td>8</td>
            <td>10</td>
        </tr>
        <tr data-qty="6">
            <td>6</td>
            <td>14</td>
            <td>18</td>
        </tr>
        <tr data-qty="1">
            <td>1</td>
            <td>11</td>
            <td>22</td>
        </tr>
        <tr data-qty="6">
            <td>6</td>
            <td>10</td>
            <td>8</td>
        </tr>
    </tbody>
  </table>

Filter table rows - Multiple filters

The boolean logic to toggle the class "u-none" (to hide a row) is quite similar to this already provided answer, but you need to modify your code to:

  • Create a function filterColumns( $someTableTarget ) that accepts as argument the jQuery wrapped HTMLTableElement to target.
  • Create an Object colFilters (to filter columns),
    where the object key is the column index
    and the value is an object {agg:<select value>, val:<input value>}
  • Loop every "tbody tr" and its td elements that match the column index (key of colFilters) toggle the "u-none" class depending on shouldHide boolean which is deduced by using Array.prototype.some() on the Object.entries() of colFilters

The following example works for any number of <table> elements on the page - and also if some THs do not have filter inputs:

const aggrFn = {
  "=": (a, b) => a == b,
  "<": (a, b) => a < b,
  ">": (a, b) => a > b,
  "<=": (a, b) => a <= b,
  ">=": (a, b) => a >= b,
};

function filterColumns($table) {
  const colFilters = {};
  $table.find("thead .filter").each(function() {
    colFilters[$(this).index()] = {
      agg: $(this).find("select").val(),
      val: $(this).find("input").val(),
    }
  });
  $table.find("tbody tr").each(function() {
    const $tr = $(this);
    const shouldHide = Object.entries(colFilters).some(([k, v]) => {
      const tdVal = $tr.find(`td:eq(${k})`).text();
      return v.val === "" ? false : !aggrFn[v.agg](parseFloat(tdVal), parseFloat(v.val));
    });
    $tr.toggleClass("u-none", shouldHide);
  });
}

$(".filter").on("input", ":input", function(ev) {
  filterColumns($(this).closest("table"));
});
table {
  width: 100%;
  border-collapse: collapse;
}

td {
  text-align: center;
}

.filter>div {
  display: flex;
  justify-content: center;
}

.filter input {
  width: 6em;
}

.u-none {
  display: none;
}
<table>
  <thead>
    <th class="filter">
      Available Quantity
      <div>
        <select>
          <option value="=">=</option>
          <option value="<">&lt;</option>
          <option value=">">&gt;</option>
          <option value="<=">≤</option>
          <option value=">=">≥</option>
        </select>
        <input type="number">
      </div>
    </th>
    <th class="filter">
      Regular Price
      <div>
        <select>
          <option value="=">=</option>
          <option value="<">&lt;</option>
          <option value=">">&gt;</option>
          <option value="<=">≤</option>
          <option value=">=">≥</option>
        </select>
        <input type="number">
      </div>
    </th>
    <th class="filter">
      Base Price
      <div>
        <select>
          <option value="=">=</option>
          <option value="<">&lt;</option>
          <option value=">">&gt;</option>
          <option value="<=">≤</option>
          <option value=">=">≥</option>
        </select>
        <input type="number">
      </div>
    </th>
  </thead>
  <tbody>
    <tr><td>4</td><td>10</td><td>12</td></tr>
    <tr><td>9</td><td>12</td><td>11</td></tr>
    <tr><td>1</td><td>14</td><td>12</td></tr>
    <tr><td>0</td><td>8</td><td>10</td></tr>
    <tr><td>6</td><td>14</td><td>18</td></tr>
    <tr><td>1</td><td>11</td><td>22</td></tr>
    <tr><td>6</td><td>10</td><td>8</td></tr>
  </tbody>
</table>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

so after half a day of playing with jsfiddle i managed to get it working as i wanted, using regexp as @david johnson had menntioned.

https://jsfiddle.net/jokerdan/tqv0ybbz/2/

working html

<div class="status"></div>
<div id="filterdiv">
  <input type="text" class="myinput" id="0"/topic/>

  <select class="mysel" id="1">
    <option value="">all</option>
    <option value="a">a</option>
    <option value="b">b</option>
    <option value="c">c</option>
  </select>

  <select class="mysel" id="2">
    <option value="">all</option>
    <option value="123">123</option>
    <option value="231">231</option>
    <option value="321">321</option>
  </select>

  <input type="checkbox" id="3"> test data :: 1 | 0 | null
</div>
<p class="p a 123" data-name="apple" data-a="a" data-b="123" data-test="1">apple a 123 1</p>
<p class="p b 123" data-name="banana" data-a="b" data-b="123" data-test="0">banana b 123 0</p>
<p class="p c 321" data-name="cherry" data-a="c" data-b="321" data-test="">cherry c 321 x</p>
<p class="p a 321" data-name="date" data-a="a" data-b="321" data-test=""> date a 321 x</p>
<p class="p a 123" data-name="elderberry" data-a="a" data-b="123" data-test="1">elderberry a 123 1</p>
<p class="p c 231" data-name="fig" data-a="c" data-b="231" data-test="1">fig c 231 1</p>

working js

$('#filterdiv').on("change keyup", function() {
  chkbox = { datatest: null };

  if ($('#3').is(':checked')) { chkbox.datatest = "1"; } else { chkbox.datatest = ""; }

  $("p").hide().filter(function() {
    var rtndata = "";

    regexname   = new regexp($('#0').val().trim(), "ig");
    regexa          = new regexp($('#1').val().trim(), "ig");
    regexb          = new regexp($('#2').val().trim(), "ig");
    regextest       = new regexp(chkbox.datatest, "ig")

    rtndata = (
      $(this).attr("data-name").match(regexname) && 
      $(this).attr("data-a").match(regexa) && 
      $(this).attr("data-b").match(regexb) &&
      $(this).attr("data-test").match(regextest)
    );

    //console.log(rtndata);
    return rtndata;
  }).show();
});

see how to find event listeners on a dom node.

in a nutshell, assuming at some point an event handler is attached to your element (eg): $('#foo').click(function() { console.log('clicked!') });

you inspect it like so:

  • jquery 1.3.x

    var clickevents = $('#foo').data("events").click;
    jquery.each(clickevents, function(key, value) {
      console.log(value) // prints "function() { console.log('clicked!') }"
    })
    
  • jquery 1.4.x

    var clickevents = $('#foo').data("events").click;
    jquery.each(clickevents, function(key, handlerobj) {
      console.log(handlerobj.handler) // prints "function() { console.log('clicked!') }"
    })
    

see jquery.fn.data (where jquery stores your handler internally).

  • jquery 1.8.x

    var clickevents = $._data($('#foo')[0], "events").click;
    jquery.each(clickevents, function(key, handlerobj) {
      console.log(handlerobj.handler) // prints "function() { console.log('clicked!') }"
    })
    

okay, i found another way fixing this problem. here is the fix:

please give me feedback! i'm kind of proud on myself ;)

$(document).ready(function() {
    if (jquery.browser.msie) {
            select_init();
    }
});


function select_init () {
    var selects = $('select');
    for (var i = 0; i < selects.length; i++) {
        _resizeselect.init(selects[i]);
    }
}


var _resizeselect = {
    obj : new array(),
    init : function (el) {
        this.obj[el] = new resizeselect (el);
    }
}

function resizeselect (el) {

    this.el = el;
    this.p = el.parentnode;
    this.ht = el.parentnode.offsetheight;
    var obj = this;
    this.set = false;

    el.onmousedown = function () {
        obj.set_select("mousedown");
    }
    el.onblur = function () {
        obj.reset_select("blur");
    }
    el.onchange = function () {
        obj.reset_select("change");
    }

}

resizeselect.prototype.set_select = function (str) {

    if (this.set) {
        this.set = false;
        return;
    }

    this.el.style.width = "auto";
    this.el.style.position = "absolute";
    this.p.style.height = this.ht + "px";
    this.p.style.zindex = 100;
    this.set = true;
    this.el.focus();
}

resizeselect.prototype.reset_select = function (str) {
    this.el.style.width = "";
    this.el.style.position = "";
    this.p.style.height = "";
    this.p.style.zindex = 1;
    this.set = false;
    window.focus();
}

some things to note here:

  1. filters get executed in the following order for an action: globally defined filters -> controller-specific filters -> action-specific filters.
  2. authorization filters -> action filters -> exception filters
  3. now the problem that you seem to mention is related to having multiple filters of the same kind (ex: multiple actionfilterattribute decorated on a controller or an action. this is the case which would not guarantee the order as its based on reflection.). for this case, there is a way to do it in web api using custom implementation of system.web.http.filters.ifilterprovider. i have tried the following and did some testing to verify it. it seems to work fine. you can give it a try and see if it works as you expected.

    // start clean by replacing with filter provider for global configuration.
    // for these globally added filters we need not do any ordering as filters are 
    // executed in the order they are added to the filter collection
    config.services.replace(typeof(ifilterprovider), new system.web.http.filters.configurationfilterprovider());
    
    // custom action filter provider which does ordering
    config.services.add(typeof(ifilterprovider), new orderedfilterprovider());
    

    public class orderedfilterprovider : ifilterprovider
    {
        public ienumerable<filterinfo> getfilters(httpconfiguration configuration, httpactiondescriptor actiondescriptor)
        {
            // controller-specific
            ienumerable<filterinfo> controllerspecificfilters = orderfilters(actiondescriptor.controllerdescriptor.getfilters(), filterscope.controller);
    
            // action-specific
            ienumerable<filterinfo> actionspecificfilters = orderfilters(actiondescriptor.getfilters(), filterscope.action);
    
            return controllerspecificfilters.concat(actionspecificfilters);
        }
    
        private ienumerable<filterinfo> orderfilters(ienumerable<ifilter> filters, filterscope scope)
        {
            return filters.oftype<iorderedfilter>()
                            .orderby(filter => filter.order)
                            .select(instance => new filterinfo(instance, scope));
        }
    }
    

    //note: here i am creating base attributes which you would need to inherit from.
    public interface iorderedfilter : ifilter
    {
        int order { get; set; }
    }
    
    public class actionfilterwithorderattribute : actionfilterattribute, iorderedfilter
    {
        public int order { get; set; }
    }
    
    public class authorizationfilterwithorderattribute : authorizationfilterattribute, iorderedfilter
    {
        public int order { get; set; }
    }
    
    public class exceptionfilterwithorderattribute : exceptionfilterattribute, iorderedfilter
    {
        public int order { get; set; }
    }
    

Tags: Javascript Jquery