DataTables 源码解析
Bootstarp 样式整体布局
整体布局是指各个组件显示在什么位置,例如分页菜单位置,搜索框位置,进度条位置等。
控制代码在dataTables.bootstrap.js
里。
/* Set the defaults for DataTables initialisation */
$.extend( true, DataTable.defaults, {
dom:
"<'row'<'col-sm-6'l><'col-sm-6'f>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
renderer: 'bootstrap'
} );
其中Dom是一个配置,在jquery.dataTables.js
源码中,会通过_fnAddOptionsHtml
方法解析这个配置,生成相应的HTML代码。
相关源码:
/**
* Add the options to the page HTML for the table
* @param {object} oSettings dataTables settings object
* @memberof DataTable#oApi
*/
function _fnAddOptionsHtml ( oSettings )
{
var classes = oSettings.oClasses;
var table = $(oSettings.nTable);
var holding = $('<div/>').insertBefore( table ); // Holding element for speed
var features = oSettings.oFeatures;
// All DataTables are wrapped in a div
var insert = $('<div/>', {
id: oSettings.sTableId+'_wrapper',
'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
} );
oSettings.nHolding = holding[0];
oSettings.nTableWrapper = insert[0];
oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
/* Loop over the user set positioning and place the elements as needed */
var aDom = oSettings.sDom.split('');
var featureNode, cOption, nNewNode, cNext, sAttr, j;
for ( var i=0 ; i<aDom.length ; i++ )
{
featureNode = null;
cOption = aDom[i];
if ( cOption == '<' )
{
/* New container div */
nNewNode = $('<div/>')[0];
/* Check to see if we should append an id and/or a class name to the container */
cNext = aDom[i+1];
if ( cNext == "'" || cNext == '"' )
{
sAttr = "";
j = 2;
while ( aDom[i+j] != cNext )
{
sAttr += aDom[i+j];
j++;
}
/* Replace jQuery UI constants @todo depreciated */
if ( sAttr == "H" )
{
sAttr = classes.sJUIHeader;
}
else if ( sAttr == "F" )
{
sAttr = classes.sJUIFooter;
}
/* The attribute can be in the format of "#id.class", "#id" or "class" This logic
* breaks the string into parts and applies them as needed
*/
if ( sAttr.indexOf('.') != -1 )
{
var aSplit = sAttr.split('.');
nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
nNewNode.className = aSplit[1];
}
else if ( sAttr.charAt(0) == "#" )
{
nNewNode.id = sAttr.substr(1, sAttr.length-1);
}
else
{
nNewNode.className = sAttr;
}
i += j; /* Move along the position array */
}
insert.append( nNewNode );
insert = $(nNewNode);
}
else if ( cOption == '>' )
{
/* End container div */
insert = insert.parent();
}
// @todo Move options into their own plugins?
else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
{
/* Length */
featureNode = _fnFeatureHtmlLength( oSettings );
}
else if ( cOption == 'f' && features.bFilter )
{
/* Filter */
featureNode = _fnFeatureHtmlFilter( oSettings );
}
else if ( cOption == 'r' && features.bProcessing )
{
/* pRocessing */
featureNode = _fnFeatureHtmlProcessing( oSettings );
}
else if ( cOption == 't' )
{
/* Table */
featureNode = _fnFeatureHtmlTable( oSettings );
}
else if ( cOption == 'i' && features.bInfo )
{
/* Info */
featureNode = _fnFeatureHtmlInfo( oSettings );
}
else if ( cOption == 'p' && features.bPaginate )
{
/* Pagination */
featureNode = _fnFeatureHtmlPaginate( oSettings );
}
else if ( DataTable.ext.feature.length !== 0 )
{
/* Plug-in features */
var aoFeatures = DataTable.ext.feature;
for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
{
if ( cOption == aoFeatures[k].cFeature )
{
featureNode = aoFeatures[k].fnInit( oSettings );
break;
}
}
}
/* Add to the 2D features array */
if ( featureNode )
{
var aanFeatures = oSettings.aanFeatures;
if ( ! aanFeatures[cOption] )
{
aanFeatures[cOption] = [];
}
aanFeatures[cOption].push( featureNode );
insert.append( featureNode );
}
}
/* Built our DOM structure - replace the holding div with what we want */
holding.replaceWith( insert );
oSettings.nHolding = null;
}
Dom 配置详解
<
将会先生成一个新的DIV标签 ,之后获取成对'
或者"
之间的属性,根据属性值,设置class样式或id。
支持的属性有:
H
使用JQuery UI Header ClassF
使用JQuery UI Footer Classxxx.XXX
使用指定的Class,点前面的内容作为id,后面的作为classname#XXX
使用指定的idXXX
使用指定的class
将当前div追加到insert节点,并设置insert节点为当前div。
>
结束当前节点,并设置insert为当前节点的父节点。
l
在当前位置生成PageLength的组件HTML。
f
在当前位置生成search的组件HTML。
r
在当前位置生成processing(处理中)的组件HTML。
t
在当前位置生成table的组件HTML。
i
在当前位置生成info(总多少条,当前多少条)的组件HTML。
p
在当前位置生成page(分页按钮)的组件HTML。
renderer 渲染处理
dataTables 自定义了两类渲染处理方法。
$.extend( true, DataTable.ext.renderer, {
header: {
_: function ( settings, cell, column, classes ) {
// No additional mark-up required
// Attach a sort listener to update on sort - note that using the
// `DT` namespace will allow the event to be removed automatically
// on destroy, while the `dt` namespaced event is the one we are
// listening for
$(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
if ( settings !== ctx ) { // need to check this this is the host
return; // table, not a nested one
}
var colIdx = column.idx;
cell
.removeClass(
column.sSortingClass +' '+
classes.sSortAsc +' '+
classes.sSortDesc
)
.addClass( columns[ colIdx ] == 'asc' ?
classes.sSortAsc : columns[ colIdx ] == 'desc' ?
classes.sSortDesc :
column.sSortingClass
);
} );
},
jqueryui: function ( settings, cell, column, classes ) {
$('<div/>')
.addClass( classes.sSortJUIWrapper )
.append( cell.contents() )
.append( $('<span/>')
.addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
)
.appendTo( cell );
// Attach a sort listener to update on sort
$(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
if ( settings !== ctx ) {
return;
}
var colIdx = column.idx;
cell
.removeClass( classes.sSortAsc +" "+classes.sSortDesc )
.addClass( columns[ colIdx ] == 'asc' ?
classes.sSortAsc : columns[ colIdx ] == 'desc' ?
classes.sSortDesc :
column.sSortingClass
);
cell
.find( 'span.'+classes.sSortIcon )
.removeClass(
classes.sSortJUIAsc +" "+
classes.sSortJUIDesc +" "+
classes.sSortJUI +" "+
classes.sSortJUIAscAllowed +" "+
classes.sSortJUIDescAllowed
)
.addClass( columns[ colIdx ] == 'asc' ?
classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
classes.sSortJUIDesc :
column.sSortingClassJUI
);
} );
}
}
} );
$.extend( true, DataTable.ext.renderer, {
pageButton: {
_: function ( settings, host, idx, buttons, page, pages ) {
var classes = settings.oClasses;
var lang = settings.oLanguage.oPaginate;
var aria = settings.oLanguage.oAria.paginate || {};
var btnDisplay, btnClass, counter=0;
var attach = function( container, buttons ) {
var i, ien, node, button;
var clickHandler = function ( e ) {
_fnPageChange( settings, e.data.action, true );
};
for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
button = buttons[i];
if ( $.isArray( button ) ) {
var inner = $( '<'+(button.DT_el || 'div')+'/>' )
.appendTo( container );
attach( inner, button );
}
else {
btnDisplay = null;
btnClass = '';
switch ( button ) {
case 'ellipsis':
container.append('<span class="ellipsis">…</span>');
break;
case 'first':
btnDisplay = lang.sFirst;
btnClass = button + (page > 0 ?
'' : ' '+classes.sPageButtonDisabled);
break;
case 'previous':
btnDisplay = lang.sPrevious;
btnClass = button + (page > 0 ?
'' : ' '+classes.sPageButtonDisabled);
break;
case 'next':
btnDisplay = lang.sNext;
btnClass = button + (page < pages-1 ?
'' : ' '+classes.sPageButtonDisabled);
break;
case 'last':
btnDisplay = lang.sLast;
btnClass = button + (page < pages-1 ?
'' : ' '+classes.sPageButtonDisabled);
break;
default:
btnDisplay = button + 1;
btnClass = page === button ?
classes.sPageButtonActive : '';
break;
}
if ( btnDisplay !== null ) {
node = $('<a>', {
'class': classes.sPageButton+' '+btnClass,
'aria-controls': settings.sTableId,
'aria-label': aria[ button ],
'data-dt-idx': counter,
'tabindex': settings.iTabIndex,
'id': idx === 0 && typeof button === 'string' ?
settings.sTableId +'_'+ button :
null
} )
.html( btnDisplay )
.appendTo( container );
_fnBindAction(
node, {action: button}, clickHandler
);
counter++;
}
}
}
};
// IE9 throws an 'unknown error' if document.activeElement is used
// inside an iframe or frame. Try / catch the error. Not good for
// accessibility, but neither are frames.
var activeEl;
try {
// Because this approach is destroying and recreating the paging
// elements, focus is lost on the select button which is bad for
// accessibility. So we want to restore focus once the draw has
// completed
activeEl = $(host).find(document.activeElement).data('dt-idx');
}
catch (e) {}
attach( $(host).empty(), buttons );
if ( activeEl !== undefined ) {
$(host).find( '[data-dt-idx='+activeEl+']' ).focus();
}
}
}
} );
一些特殊常量
DT_RowId
返回数据中有该列名,则设置该列为id列。