Customizing DataTables Global Search

09 Apr 2021

Table of Contents


Note: See also the more recent example in DataTables Filtering for Accented Characters.

Introduction

The DataTables global search box is this input field here:

It searches across all data in your table, using “smart” logic:

  • matches words out of order
  • matches on partial words
  • can match on exact phrases, surrounded by double-quotes

Full details can be found here.

Also, by default, this field is interactive: data is re-searched following each keyup event. In fact, it uses several events to provide a responsive experience:

You can extend this behavior, or even replace it entirely.

Terminology

I tend to use the terms “search” and “filter” interchangeably here. DataTables itself refers to the “global search” feature, but notes that this is actually a subtractive filtering process. I try not to get too caught up in the differences.

Extending the Search Function

This is well documented in the DataTables web site, with detailed demos, such as this one: Custom filtering - range search. This uses two custom input fields, to capture “minumum age” and “maximum age” values - and then uses these to filter data in the “age” column.

It uses a DataTables API method to for this:

$.fn.dataTable.ext.search

This holds an array of functions (it is initially empty). Each function is given access to the table’s row data, and returns true if the row passes the provided filter and should be displayed - or false otherwise.

You add your function to this array as you might expect, using push():

JavaScript
1
2
3
4
5
$.fn.dataTable.ext.search.push(
  function( settings, rowData, dataIndex ) {
    // your implementation goes here
  }
);

Because this is an array, you can even push multiple functions onto it. In this case, each function will be executed in order, and will receive only those rows which have remained unfiltered by prior functions.

However, a key point to note here is that there is a “default” function, which always runs first: the DataTables global search function (the “smart” function mentioned earlier).

Therefore you are simply augmenting that global search function, not replacing it. If that global search would result in all data being filtered out, then your custom functions would never fire, because they would never receive any data.

Here is a simple demo.

The test data:

HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<table id="example" class="display dataTable cell-border" style="width:100%">
    <thead>
        <tr>
            <th>Name</th>
            <th>Position</th>
            <th>Office</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Tiger Nixon</td>
            <td>System Architect</td>
            <td>Edinburgh</td>
        </tr>
        <tr>
            <td>Garrett Winters</td>
            <td>Accountant</td>
            <td>Tokyo</td>
        </tr>
        <tr>
            <td>Rhona Davidson</td>
            <td>Integration Specialist</td>
            <td>Tokyo</td>
        </tr>
    </tbody>
</table>

The DataTable:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$(document).ready(function() {

  $('#example').DataTable();

  // a dumb filter:
  $.fn.dataTable.ext.search.push(
    function( settings, searchData, index, rowData, counter ) {
      console.log( "func 1" );
      console.log( rowData );
      if ( rowData[1] === 'Accountant' ) {
        return false;
      } else {
        return true;
      }
    }
  );

  // a do-nothing filter - just to see which rows it receives:
  $.fn.dataTable.ext.search.push(
    function( settings, searchData, index, rowData, counter ) {
        console.log( "func 2" );
        console.log( rowData );
        return true;
    }
  );

} );

The table initially looks like this:

When I enter the letter k into the global search box, the display changes to this:

This is because:

(1) First of all, the DataTables global filter keeps all records containing a k - which means Tiger Nixon is filtered out.

(2) The remaining rows are passed to my first filter, where all the accountants are filtered out. This removes Garrett Winters.

(3) Rhona Davidson is the one remaining record.

And our console logging statements show the same thing:

Note: It’s a really bad idea to hard-code a filter criterion such as this, in the real world:

if ( rowData[1] === 'Accountant' ) { return false; }

Once that data has been filtered out, it cannot be re-displayed, except by refreshing the entire web page. It’s just used here for demo purposes.

You can see more details about $.fn.dataTable.ext.search here.

Replacing the Global Filter

Here, I will use the example of providing an inverted filter. By this, I mean a search box which displays only those records which do not match the provided search term.

This cannot be implemented using the previous technique ($.fn.dataTable.ext.search) because the DataTables search function would be executed first (as described above) and would take the search term “literally”, causing all records not matching the provided value to be filtered out - thus defeating the purpose of our inverted filter, before our filter even runs. Our filter would then run, and would filter out all those remaining records, leaving an empty table.

Using the same test data as above, we can do the following:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$(document).ready(function() {

  var table = $('#example').DataTable();

  var filterTerm;

  $('.dataTables_filter input').off().on('change', function() {
    filterTerm = this.value.trim();
    var term = '^((?!' + filterTerm + ').)*$';
    table.search(term, true, false, true).draw();
    this.value = filterTerm;
  });

  table.on( 'draw', function () {
    $('.dataTables_filter input').val( filterTerm );
  } );

} );

In this case, we are using the DataTables search() API.

This option provides various ways to control how the DataTables global searching works:

  • regex (true/false): controls whether the search term is treated as a regular expression or not (defaults to false).

  • smart (true/false): controls whether or not DataTables “smart” searching (see ebove) is used (defaults to true).

  • case insensitive (true/false): controls whether the search term is treated as case insensitive or not (defaults to true).

But first of all, this example removes all of the default global search events provided by DataTables:

$('.dataTables_filter input').off()

And then it provides its own events.

In our case, it provides just the one change event. This means changes are not detected until the user leaves the input field, or hits <enter>. But we could obviously have chosen one or more other events (such as keyup).

In our demo, we then capture the filter term provided by the user, and wrap it in a regular expression - for example, assuming a search term of foo, the regex would be:

^((?!foo).)*$

I took this from this Stack Overflow answer, where a full explanation is provided.

Then we apply the search:

table.search(term, true, false, true).draw();

Note how we have set “regex” to true, but “smart” to false. The “smart” logic also uses a regular expression - and we want to avoid any conflicts between our regex and that one.

We also need to explicitly call a table re-draw (unlike the $.fn.dataTable.ext.search example, where this was not needed).

Finally, we restore the contents of the input field back to what the user originally typed, using the DataTables draw event. This is needed because otherwise, DataTables will automatically replace the user-provided foo with the text of the regular expression - ^((?!foo).)*$ - which would probably be confusing for our users.

Additional Notes

In both the examples shown here, we used (or re-purposed) the global search field provided by DataTables. You do not need to use this field at all. You can provide your own field and hide the out-of-the-box field using the appropriate dom option. Which is, of course, f for filter, just to add to the terminology blend.

You can read more about DataTables searching here.

The filter() Function

There is also a completely separate DataTables API function called filter(). I have never used it - but you can think of it as a type of “utility” function provided by DataTables, similar to functions such as map(), flatten(), and unique(). These are more likely to be used when you are iterating through a set of rows, columns, or cells, in other contexts - not for filtering/searching displayed data.