Friday 26th February, 2016

Ellipsis renderer

During your work with DataTables, there will be occasions when the data obtained from the source feed is not in the format that you wish to show to your end users. For this DataTables has support for data renderers through columns.render. This option can be used as a function to perform data transformation (its a Javascript function, so anything you want!) and is called whenever DataTables requires data for that cell. Indeed, this option is also how DataTables provides its orthogonal data support.

The following is a short list of use cases where renderers can be useful:

  • Convert a date / time to a localised format
  • Format numbers for currency, readability and precision
  • Security to prevent XSS attacks
  • Shorten a long string

This article will detail how a renderer operates and can be constructed by implementing the final option in the list above as an example: long text will be shorted with an ellipsis showing that the text has been truncated.

Example

The table below shows an example - the first column uses ellipsis at 10 characters and does not allow words to be broken, while the second column uses ellipsis at 15 characters and does allow words to be truncated part way through. The example table also has Responsive enabled.

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 AssistantSydney232010-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 SpecialistSydney372011-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
NamePositionOfficeAgeStart dateSalary

Simple renderer

Let's start by considering the simplest case possible - an HTML sourced table where we want to limit the number of characters in the first column to be no more than ten characters:

$('#myTable').DataTable( {
    columnDefs: [ {
        targets: 0,
        render: function ( data, type, row ) {
            return data.substr( 0, 10 );
        }
    } ]
} );

The code above created a DataTable on the #myTable element and columnDefs is used to assign column options to the first column only (columnDefs.targets). The important part here is the use of the rendering function - columns.render. We use substr() to limit the data to only 10 characters.

This is trivial to implement, but there are a few issues:

  • There is no indication to the end user that the data has been truncated
  • Search and ordering data will also be truncated.

Let's address each in turn - to indicate to the end user that the data has been truncated we use ellipsis (). A trivial check to see if the string will be truncated gives us the ability to decide if ellipsis should be added or not:

render: function ( data, type, row ) {
    return data.length > 10 ?
        data.substr( 0, 10 ) +'…' :
        data;
}

To address the second point we can use the second parameter passed into the function, which is DataTables' orthogonal data type indicator - only if it is display do we wish to show ellipsis. Now we have:

render: function ( data, type, row ) {
    return type === 'display' && data.length > 10 ?
        data.substr( 0, 10 ) +'…' :
        data;
}

Easy!

Reusable renderers

The above is a few trivial lines of code, but if you are working on any web-site which has more than one page you will want to follow the Don't Repeat Yourself (DRY) principle of good software development.

Since functions are first class elements in Javascript we can store the rendering function in a variable and then assign it to the columns.render option as needed. DataTables provides an object that can be used as a common location to store all renderers: DataTable.render.

If we continue our ellipsis example we might have:

DataTable.render.ellipsis = function () {
    return function ( data, type, row ) {
        return type === 'display' && data.length > 10 ?
            data.substr( 0, 10 ) +'…' :
            data;
    }
};

Then to use this with DataTables we can use:

$('#myTable').DataTable( {
    columnDefs: [ {
        targets: 0,
        render: DataTable.render.ellipsis()
    } ]
} );

That rendering function can then be used for multiple tables, over multiple pages - just save the renderer code into a file that is loaded onto your pages (dataTables.plugins.js for example).

Configuration options

You will have noticed that we define ellipsis as a function that returns a function - this isn't particularly useful in this simple case, but for more complex cases, and continuing the DRY concept, the outer function can be used to configure the renderer. In our ellipsis example this can be the ability to define the number of characters that will be shown - for example:

DataTable.render.ellipsis = function ( cutoff ) {
    return function ( data, type, row ) {
        return type === 'display' && data.length > cutoff ?
            data.substr( 0, cutoff ) +'…' :
            data;
    }
};

Now the renderer can be used to define the number of characters as shown:

render: DataTable.render.ellipsis( 10 )
render: DataTable.render.ellipsis( 15 )
render: DataTable.render.ellipsis( 20 )
etc

Even for cases where configuration options are not required it is a good idea to use a container function, simply for consistency. Most rendering functions do require some kind of configuration, so it is easier if all rendering plug-ins are presented as functions that need to be executed.

Ellipsis rendering plug-in

Now that the groundwork for how to create a DataTables rendering plug-in have been laid, let's built the ellipsis plug-in up to provide greater utility with the following features:

  • Variable length truncation
  • Show the full string in a tooltip
  • Option to disallow breaking in the middle of a word
  • Ability to escape HTML for XSS security

The following function provides all of these features

DataTable.render.ellipsis = function ( cutoff, wordbreak, escapeHtml ) {
    var esc = function ( t ) {
        return t
            .replace( /&/g, '&' )
            .replace( /</g, '&lt;' )
            .replace( />/g, '&gt;' )
            .replace( /"/g, '&quot;' );
    };

    return function ( d, type, row ) {
        // Order, search and type get the original data
        if ( type !== 'display' ) {
            return d;
        }

        if ( typeof d !== 'number' && typeof d !== 'string' ) {
            return d;
        }

        d = d.toString(); // cast numbers

        if ( d.length < cutoff ) {
            return d;
        }

        var shortened = d.substr(0, cutoff-1);

        // Find the last white space character in the string
        if ( wordbreak ) {
            shortened = shortened.replace(/\s([^\s]*)$/, '');
        }

        // Protect against uncontrolled HTML input
        if ( escapeHtml ) {
            shortened = esc( shortened );
        }

        return '<span class="ellipsis" title="'+esc(d)+'">'+shortened+'&#8230;</span>';
    };
};

Breaking the function down you'll be able to see that the above is fairly basic Javascript. Only display data types will have their length truncated (as discussed above) and only strings are manipulated. The string is truncated and a regular expression is used to strip back to the last work if the no-word break option is enabled. Finally a span is returned which contains the shortened string with an ellipsis and a browser tooltip provided by the title attribute (you could also use Javascript tooltip libraries if you prefer).

Source

The ellipsis data renderer is detailed on its plug-ins page and is available on the DataTables CDN (keep in might that you might wish to combine multiple plug-ins into a single file for loading efficiency):

JS

The source is also available on GitHub and pull requests for improvements are always welcome.

Sharing plug-ins

If you create a rendering plug-in that you would like to share with the DataTables community - first of all - thank you! Please send pull requests to the DataTabels plug-ins repo in the dataRender directory. A documentation block should also be included to detail what the plug-in does and how it can be used (please refer to existing plug-ins for a template).

Enjoy!