DataTables logo DataTables

Wed, 8th Aug 2012

Creating feature plug-ins

DataTables provides a wealth of functionality built into its core, however it can't provide for every possible situation and to keep the core code to a sensible size it doesn't attempt to. For this DataTables provides a number of comprehensive plug-in options including sorting, filtering and API methods among others.

In this post I will look at the feature plug-in abilities and describe how a feature plug-in can be created, quite easily, for DataTables. I will also describe how to create and use a custom API method along the way - used as part of the feature plug-in. Creating feature plug-ins in this manner allow for greater code reuse and sharing of code between pages and authors. The extras available for DataTables use this feature plug-in method to integrate tightly with DataTables.

To demonstrate how to build a feature plug-in, here I will build one which will simply show and hide the paging control based on if it is needed or not (a reasonably common request in the forum). For example if the table rows can all be displayed on one page, there is no need to show the paging control (even although it is disabled at that point) - this situation can change dynamically based on the filtering applied to the table.

There are two parts to the work involved in creating this feature plug-in:

Example

In the example below, note that the pagination control is initially shown. Now enter some text into the filtering field and the control will be removed when the results from the filter fit onto a single page.

Rendering engine Browser Platform(s) Engine version CSS grade
Trident Internet Explorer 4.0 Win 95+ 4 X
Trident Internet Explorer 5.0 Win 95+ 5 C
Trident Internet Explorer 5.5 Win 95+ 5.5 A
Trident Internet Explorer 6 Win 98+ 6 A
Trident Internet Explorer 7 Win XP SP2+ 7 A
Trident AOL browser (AOL desktop) Win XP 6 A
Gecko Firefox 1.0 Win 98+ / OSX.2+ 1.7 A
Gecko Firefox 1.5 Win 98+ / OSX.2+ 1.8 A
Gecko Firefox 2.0 Win 98+ / OSX.2+ 1.8 A
Gecko Firefox 3.0 Win 2k+ / OSX.3+ 1.9 A
Gecko Camino 1.0 OSX.2+ 1.8 A
Gecko Camino 1.5 OSX.3+ 1.8 A
Gecko Netscape 7.2 Win 95+ / Mac OS 8.6-9.2 1.7 A
Gecko Netscape Browser 8 Win 98SE+ 1.7 A
Gecko Netscape Navigator 9 Win 98+ / OSX.2+ 1.8 A
Gecko Mozilla 1.0 Win 95+ / OSX.1+ 1 A
Gecko Mozilla 1.1 Win 95+ / OSX.1+ 1.1 A
Gecko Mozilla 1.2 Win 95+ / OSX.1+ 1.2 A
Gecko Mozilla 1.3 Win 95+ / OSX.1+ 1.3 A
Gecko Mozilla 1.4 Win 95+ / OSX.1+ 1.4 A
Gecko Mozilla 1.5 Win 95+ / OSX.1+ 1.5 A
Gecko Mozilla 1.6 Win 95+ / OSX.1+ 1.6 A
Gecko Mozilla 1.7 Win 98+ / OSX.1+ 1.7 A
Gecko Mozilla 1.8 Win 98+ / OSX.1+ 1.8 A
Gecko Seamonkey 1.1 Win 98+ / OSX.2+ 1.8 A
Gecko Epiphany 2.20 Gnome 1.8 A
Webkit Safari 1.2 OSX.3 125.5 A
Webkit Safari 1.3 OSX.3 312.8 A
Webkit Safari 2.0 OSX.4+ 419.3 A
Webkit Safari 3.0 OSX.4+ 522.1 A
Webkit OmniWeb 5.5 OSX.4+ 420 A
Webkit iPod Touch / iPhone iPod 420.1 A
Webkit S60 S60 413 A
Presto Opera 7.0 Win 95+ / OSX.1+ - A
Presto Opera 7.5 Win 95+ / OSX.2+ - A
Presto Opera 8.0 Win 95+ / OSX.2+ - A
Presto Opera 8.5 Win 95+ / OSX.2+ - A
Presto Opera 9.0 Win 95+ / OSX.3+ - A
Presto Opera 9.2 Win 88+ / OSX.3+ - A
Presto Opera 9.5 Win 88+ / OSX.3+ - A
Presto Opera for Wii Wii - A
Presto Nokia N800 N800 - A
Presto Nintendo DS browser Nintendo DS 8.5 C/A1
KHTML Konqureror 3.1 KDE 3.1 3.1 C
KHTML Konqureror 3.3 KDE 3.3 3.3 A
KHTML Konqureror 3.5 KDE 3.5 3.5 A
Tasman Internet Explorer 4.5 Mac OS 8-9 - X
Tasman Internet Explorer 5.1 Mac OS 7.6-9 1 C
Tasman Internet Explorer 5.2 Mac OS 8-X 1 C
Misc NetFront 3.1 Embedded devices - C
Misc NetFront 3.4 Embedded devices - A
Misc Dillo 0.8 Embedded devices - X
Misc Links Text only - X
Misc Lynx Text only - X
Misc IE Mobile Windows Mobile 6 - C
Misc PSP browser PSP - C
Other browsers All others - - U
Rendering engine Browser Platform(s) Engine version CSS grade

API method

Plug-in API methods for DataTables are exceptionally useful for being able to easily access information about and control the table. Being attached to the instance that is created when you create your DataTable, they look just like a regular API method. Plug-in API methods allow you to quickly and easily expand the capabilities of DataTables, and more importantly provide an abstraction layer between the DataTables core and your external program.

While it is possible to read properties directly from the DataTables settings object (available through fnSettings) this is not to be encouraged outside a plug-in since any changes to the core would mean you have to change your application's code wherever you access the settings object. Plug-in API methods allow greater code reuse and much easier code maintainability.

The plug-in API method we want to create here will get information from the table's settings object about the current pagination settings. The properties of the settings object are not documented on this site (since they are not strictly speaking part of the public API - a plug-in API method should be created and used as we are doing here). However, the settings object is extensively documented in the code. When creating a plug-in API method you will likely need to refer to the DataTables source to find the information you want.

To create a plug-in API method we simply need to attach a function to the $.fn.dataTableExt.oApi object that DataTables provides. Note that DataTables will automatically insert the first parameter passed to the API method as the table's settings object. Any other parameters passed to the method will then follow. So in our case we can do:

$.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings )
{
  return {
    "iStart":         oSettings._iDisplayStart,
    "iEnd":           oSettings.fnDisplayEnd(),
    "iLength":        oSettings._iDisplayLength,
    "iTotal":         oSettings.fnRecordsTotal(),
    "iFilteredTotal": oSettings.fnRecordsDisplay(),
    "iPage":          Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
    "iTotalPages":    Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
  };
}

This provides us with everything we could want about the state of the table's paging at the point when we call the function. A quick example of how you could call this method is (this will alert the page number on each page draw):

$(document).ready(function() {
	$('#example').dataTable( {
	  "fnDrawCallback": function () {
	    alert( "Now on page: "+ this.fnPagingInfo().iPage );
    }
	} );
} );

Feature plug-in

Feature plug-ins for DataTables are activated and arranged in the DOM in the same way as the core features: using sDom. Each feature is assigned a letter (plug-ins by convention use a capital letter) which when found in sDom will cause DataTables to initialise that feature, for example the sDom setting of "lt" would create the DOM showing the length changing menu followed by the main table.

Creating a feature plug-in for DataTables is almost as simple as the API method above - you simply need to add an object to the $.fn.dataTableExt.aoFeatures array. This object must have three properties:

In our case for this blog we will use the following:

$.fn.dataTableExt.aoFeatures.push( {
	"fnInit": function( oDTSettings ) {
	  new DT_PagingControl( oDTSettings );
	},
	"cFeature": "P",
	"sFeature": "PagingControl"
} );

Note that fnInit in this case creates a new instance of DT_PagingControl (which we will create shortly) and doesn't return anything. You can return a DOM element at this point if you wish it to be added to the page, or you can return nothing, as in this case, if nothing needs to be added. Also note that you don't need to create a new instance of an existing class - fnInit is just a Javascript function and can be used to run any code you want.

It is a good idea to perform a version check on the DataTables instance when creating your feature plug-in, in order to ensure that the core library is at least the version you would expect it to be. This is done using the fnVersionCheck static function. The following shows fnVersionCheck being used to check for v1.8.0 or newer of DataTables:

if ( typeof $.fn.dataTable == "function" &&
     typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
     $.fn.dataTableExt.fnVersionCheck('1.8.0') )
{
	$.fn.dataTableExt.aoFeatures.push( {
		"fnInit": function( oDTSettings ) {
		  new DT_PagingControl( oDTSettings );
		},
		"cFeature": "P",
		"sFeature": "PagingControl"
	} );
}
else
{
	alert( "Warning: PagingControl requires DataTables 1.8.0 or greater - www.datatables.net/download");
}

The last thing we need to do before actually using our code is to create the DT_PagingControl class and have it control the paging control display. This is the part of the code for creating plug-ins which will be highly dependent on what you want to achieve. In this case we need to call the API plug-in fnPagingInfo we created above and then show or hide any paging controls for the table. For this we will attach a draw callback function which is added to the aoDrawCallback array of the table's settings object:

var DT_PagingControl = function ( oDTSettings )
{
  	oDTSettings.aoDrawCallback.push( {
			"fn": function () {
			  var bShow = oDTSettings.oInstance.fnPagingInfo().iTotalPages > 1;
			  for ( var i=0, iLen=oDTSettings.aanFeatures.p.length ; i<iLen ; i++ ) {
			    oDTSettings.aanFeatures.p[i].style.display = bShow ? "block" : "none";
		    }
			},
			"sName": "PagingControl"
		} );
}

Note the use of aanFeatures.p. aanFeatures is used to hold all of the DOM elements created by DataTables features created through sDom. In this case we are interested in paging so we use the 'p' property. For the information elements we would use 'i' etc. Note also the loop over this array - DataTables allows all core features to be specified multiple times in sDom (for example you could have "ltl" to have the length change menu at the top and bottom of the table) - hence we need a loop to show or hide all instances of the paging control.

Again, to find further information about aanFeatures you would need to refer to the DataTables source. Only plug-in authors should use this parameter, hence why it is not part of the public API.

Make it go!

So we've done all the hard work and now we want our table to use our new feature plug-in. This is trivial for the table implementor now with sDom:

$(document).ready(function() {
	$('#example').dataTable( {
	  "sDom": "lfrtipP"
	} );
} );

Putting it all together

This is what the fully combined code looks like from our work above:

(function($, window, document) {


$.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings )
{
  return {
    "iStart":         oSettings._iDisplayStart,
    "iEnd":           oSettings.fnDisplayEnd(),
    "iLength":        oSettings._iDisplayLength,
    "iTotal":         oSettings.fnRecordsTotal(),
    "iFilteredTotal": oSettings.fnRecordsDisplay(),
    "iPage":          Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
    "iTotalPages":    Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
  };
}

var DT_PagingControl = function ( oDTSettings )
{
  	oDTSettings.aoDrawCallback.push( {
			"fn": function () {
			  var bShow = oDTSettings.oInstance.fnPagingInfo().iTotalPages > 1;
			  for ( var i=0, iLen=oDTSettings.aanFeatures.p.length ; i<iLen ; i++ ) {
			    oDTSettings.aanFeatures.p[i].style.display = bShow ? "block" : "none";
		    }
			},
			"sName": "PagingControl"
		} );
}

if ( typeof $.fn.dataTable == "function" &&
     typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
     $.fn.dataTableExt.fnVersionCheck('1.8.0') )
{
	$.fn.dataTableExt.aoFeatures.push( {
		"fnInit": function( oDTSettings ) {
		  new DT_PagingControl( oDTSettings );
		},
		"cFeature": "P",
		"sFeature": "PagingControl"
	} );
}
else
{
	alert( "Warning: PagingControl requires DataTables 1.8.0 or greater - www.datatables.net/download");
}


})(jQuery, window, document);


$(document).ready(function() {
	$('#example').dataTable( {
	  "sDom": "lfrtipP"
	} );
} );

Now you have a highly reusable and easily accessible feature for DataTables, which can be used on any table you wish!

Conclusion

As we have seen creating simple feature plug-ins for DataTables is relatively straight forward and has a number of advantages. It is of course possible to create much more complex plug-ins such as TableTools and FixedColumns and I'd encourage you to have a look at their source if you are interested in creating a plug-in for DataTables (all available on GitHub).

Let us know if you create any plug-ins for DataTables and we can get them up on the site!