Tuesday 26th August, 2014

Alphabet input search - Part I

Search is a core feature of DataTables - in fact it was the first feature that was implemented in the code, way back in 2007 as it was key to what DataTables was designed to do: let users find information in complex data sets quickly and easily. DataTables' search options have come along way since that initial work and provide a range of options through the search() method and among others.

Often, however, you will wish to customise the search options of DataTables to match your own needs. This might be as simple as providing a custom input that fits in perfectly with your site's general UX, or you might want to use a complex search algorithm.

To meet this need, DataTables provides a custom search API which can be used to perform whatever search logic you wish. In this three part series of posts I will introduce this API, showing how to use it and provide custom search functions that match your specific needs. As a working example an alphabet search bar will be developed as part of this series showing you how various aspects of the DataTables plug-in system work.

  • Part I (this post): Introducing customer search and showing a clickable alphabet filter
  • Part II: Add user information showing the number of search results for each option in the filter
  • Part III: Creating a feature plug-in for DataTables incorporating the previously developed code for reuse flexibility.

The alphabet search

Let's start at the end, by seeing what we are going to create in this post - a list of letters above a DataTable, which when clicked, will activate a table search, finding data that starts with the letter that was clicked upon in the first column:

NamePositionOfficeAgeStart dateSalary
NamePositionOfficeAgeStart dateSalary
Tiger NixonSystem ArchitectEdinburgh612011/04/25$320,800
Garrett WintersAccountantTokyo632011/07/25$170,750
Ashton CoxJunior Technical AuthorSan Francisco662009/01/12$86,000
Cedric KellySenior Javascript DeveloperEdinburgh222012/03/29$433,060
Airi SatouAccountantTokyo332008/11/28$162,700
Brielle WilliamsonIntegration SpecialistNew York612012/12/02$372,000
Herrod ChandlerSales AssistantSan Francisco592012/08/06$137,500
Rhona DavidsonIntegration SpecialistTokyo552010/10/14$327,900
Colleen HurstJavascript DeveloperSan Francisco392009/09/15$205,500
Sonya FrostSoftware EngineerEdinburgh232008/12/13$103,600
Jena GainesOffice ManagerLondon302008/12/19$90,560
Quinn FlynnSupport LeadEdinburgh222013/03/03$342,000
Charde MarshallRegional DirectorSan Francisco362008/10/16$470,600
Haley KennedySenior Marketing DesignerLondon432012/12/18$313,500
Tatyana FitzpatrickRegional DirectorLondon192010/03/17$385,750
Michael SilvaMarketing DesignerLondon662012/11/27$198,500
Paul ByrdChief Financial Officer (CFO)New York642010/06/09$725,000
Gloria LittleSystems AdministratorNew York592009/04/10$237,500
Bradley GreerSoftware EngineerLondon412012/10/13$132,000
Dai RiosPersonnel LeadEdinburgh352012/09/26$217,500
Jenette CaldwellDevelopment LeadNew York302011/09/03$345,000
Yuri BerryChief Marketing Officer (CMO)New York402009/06/25$675,000
Caesar VancePre-Sales SupportNew York212011/12/12$106,450
Doris WilderSales AssistantSidney232010/09/20$85,600
Angelica RamosChief Executive Officer (CEO)London472009/10/09$1,200,000
Gavin JoyceDeveloperEdinburgh422010/12/22$92,575
Jennifer ChangRegional DirectorSingapore282010/11/14$357,650
Brenden WagnerSoftware EngineerSan Francisco282011/06/07$206,850
Fiona GreenChief Operating Officer (COO)San Francisco482010/03/11$850,000
Shou ItouRegional MarketingTokyo202011/08/14$163,000
Michelle HouseIntegration SpecialistSidney372011/06/02$95,400
Suki BurksDeveloperLondon532009/10/22$114,500
Prescott BartlettTechnical AuthorLondon272011/05/07$145,000
Gavin CortezTeam LeaderSan Francisco222008/10/26$235,500
Martena MccrayPost-Sales supportEdinburgh462011/03/09$324,050
Unity ButlerMarketing DesignerSan Francisco472009/12/09$85,675
Howard HatfieldOffice ManagerSan Francisco512008/12/16$164,500
Hope FuentesSecretarySan Francisco412010/02/12$109,850
Vivian HarrellFinancial ControllerSan Francisco622009/02/14$452,500
Timothy MooneyOffice ManagerLondon372008/12/11$136,200
Jackson BradshawDirectorNew York652008/09/26$645,750
Olivia LiangSupport EngineerSingapore642011/02/03$234,500
Bruno NashSoftware EngineerLondon382011/05/03$163,500
Sakura YamamotoSupport EngineerTokyo372009/08/19$139,575
Thor WaltonDeveloperNew York612013/08/11$98,540
Finn CamachoSupport EngineerSan Francisco472009/07/07$87,500
Serge BaldwinData CoordinatorSingapore642012/04/09$138,575
Zenaida FrankSoftware EngineerNew York632010/01/04$125,250
Zorita SerranoSoftware EngineerSan Francisco562012/06/01$115,000
Jennifer AcostaJunior Javascript DeveloperEdinburgh432013/02/01$75,650
Cara StevensSales AssistantNew York462011/12/06$145,600
Hermione ButlerRegional DirectorLondon472011/03/21$356,250
Lael GreerSystems AdministratorLondon212009/02/27$103,500
Jonas AlexanderDeveloperSan Francisco302010/07/14$86,500
Shad DeckerRegional DirectorEdinburgh512008/11/13$183,000
Michael BruceJavascript DeveloperSingapore292011/06/27$183,000
Donna SniderCustomer SupportNew York272011/01/25$112,000

Search plug-in

Search plug-ins for DataTables are attached to the $.fn.dataTable.ext.search array. Simply push() a function onto this array and DataTables will execute the function for each row in a table whenever it needs to perform a search operation. The function should then return true or false to indicate to DataTables if the row should be included in the result set (true) or not (false).

In the case of the alphabet search, we simply want to check the first letter of the first column of data (which can be obtained from the second parameter passed into the function, in combination with the charAt() method) against a variable which stores the character that the user has activated the search upon (call it _alphabetSearch).

That gives us this simple little function:

var _alphabetSearch;

$.fn.dataTable.ext.search.push( function ( settings, searchData ) {
    if ( ! _alphabetSearch ) { // No search term - all results shown
        return true;
    }

    if ( searchData[0].charAt(0) === _alphabetSearch ) {
        return true;
    }

    return false;
} );

The DataTables manual contains detailed information about the search plug-in API if you are interested in finding out more information about this API.

Interface

The interface we want to present to the end user here is actually very simple. Just a list of the letters in the alphabet, A-Z. There is no DataTables specific code here, just simple jQuery manipulation of the DOM to create a div element with a list of spans inside, one for each letter, so I won't go into detail about this code, however there are a couple of points worth noting:

  • A None option is include to clear the search term, allowing all entries in the table to be shown.
  • String.formCharCode is used to convert an ASCII code to a letter.
  • The data property letter is used to store the letter to be used for the search term.
// DataTables initialisation
var table = $('#example').DataTable();

var alphabet = $('<div class="alphabet"/>').append( 'Search: ' );

$('<span class="clear active"/>')
    .data( 'letter', '' )
    .html( 'None' )
    .appendTo( alphabet );

for ( var i=0 ; i<26 ; i++ ) {
    var letter = String.fromCharCode( 65 + i );

    $('<span/>')
        .data( 'letter', letter )
        .html( letter )
        .appendTo( alphabet );
}

alphabet.insertBefore( table.table().container() );

The above is a simple case for the letters used in Latin script only, but it could be readily extended to use characters from any other alphabet, or also to include numeric values.

Behaviour

To activate a search we use a standard jQuery $().on() click handler to listen for the click event. The listener will read the letter data property for the element and assign it to the _alphabetSearch variable we defined in our search function above and then use draw() to redraw the table and have the new search term taken into account. It also does a little class manipulate to show visually which element is the active search term.


alphabet.on( 'click', 'span', function () { alphabet.find( '.active' ).removeClass( 'active' ); $(this).addClass( 'active' ); _alphabetSearch = $(this).data('letter'); table.draw(); } );

Styling

We have a very simple DOM structure for our alphabet list, consisting of a div with span elements in it. To display neatly we can use the CSS display: table property to have the alphabet shown as a flexible width, nicely laid out grid. A couple of other styles are also added just to provide visual feedback to the end user.

div.alphabet {
    display: table;
    width: 100%;
    margin-bottom: 1em;
}

div.alphabet span {
    display: table-cell;
    color: #3174c7;
    cursor: pointer;
    text-align: center;
    width: 3.5%
}

div.alphabet span:hover {
    text-decoration: underline;
}

div.alphabet span.active {
    color: black;
}

Complete code

The Javascript shown above is complete, but if you want a copy and paste solution, rather than to assemble the code, the entire code block used for this example is shown below:

var _alphabetSearch = '';

$.fn.dataTable.ext.search.push( function ( settings, searchData ) {
    if ( ! _alphabetSearch ) {
        return true;
    }

    if ( searchData[0].charAt(0) === _alphabetSearch ) {
        return true;
    }

    return false;
} );


$(document).ready(function() {
    var table = $('#example').DataTable();

    var alphabet = $('<div class="alphabet"/>').append( 'Search: ' );

    $('<span class="clear active"/>')
        .data( 'letter', '' )
        .html( 'None' )
        .appendTo( alphabet );

    for ( var i=0 ; i<26 ; i++ ) {
        var letter = String.fromCharCode( 65 + i );

        $('<span/>')
            .data( 'letter', letter )
            .html( letter )
            .appendTo( alphabet );
    }

    alphabet.insertBefore( table.table().container() );

    alphabet.on( 'click', 'span', function () {
        alphabet.find( '.active' ).removeClass( 'active' );
        $(this).addClass( 'active' );

        _alphabetSearch = $(this).data('letter');
        table.draw();
    } );
} );