对dataTable
是怎么利用aDom
实现布局的,提出了疑问。由于很久以前看的源码,当时也没有跟他解释的很清楚,于是又重新梳理了一下源码,在这里做一下笔记。
首先看一下示例的实际效果:
源码中实现布局的方法是_fnAddOptionsHtml()
,形参是oSettings
。
下面看_fnAddOptionsHtml()
源码:
function _fnAddOptionsHtml ( oSettings )
{
/*
* Create a temporary, empty, div which we can later on replace with what we have generated
* we do it this way to rendering the 'options' html offline - speed :-)
* 作者说:这里先创建一个临时的空div,后面会被完整的内容替换掉
*/
var nHolding = $('<div></div>')[0];
/*
* 把上面生成的临时div插入到绑定dataTable插件的元素前面
* 这里的oSettings.nTable,追溯源码,发现是this,也就是绑定插件的对象
* 例如:$("#mainTable").dataTable({....}); oSettings.nTable 就是 id为mainTable的
*/
oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable );
/*
* All DataTables are wrapped in a div
* 作者说:创建一个div作为根节点,将所有的元素都包裹进去,以<table>的id拼接'_wrapper'
*/
oSettings.nTableWrapper = $('<div id="'+oSettings.sTableId+'_wrapper" class="'+oSettings.oClasses.sWrapper+'" role="grid"></div>')[0];
oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
/* Track where we want to insert the option
* 将oSettings.nTableWrapper赋给变量nInsertNode,跟踪我们想插入的元素
*/
var nInsertNode = oSettings.nTableWrapper;
/* Loop over the user set positioning and place the elements as needed */
/*
* 前方高能,重头戏开始了!!!
* 分割aDom,得到字符数组
* 示例中我们的aDom属性值为<lf>rt<lpi><"clear">
*/
var aDom = oSettings.sDom.split('');
var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j;
/*
* 迭代字符数组
*/
for ( var i=0 ; i<aDom.length ; i++ )
{
iPushFeature = 0;
cOption = aDom[i];
/*
* 如果当前字符是'<'执行以下操作
*/
if ( cOption == '<' )
{
/* New container div
* 创建一个空div
*/
nNewNode = $('<div></div>')[0];
/* Check to see if we should append an id and/or a class name to the container
* 看看是否要给div添加id属性或者class属性
* 如果<符号后面是'或"就要给div添加属性
*/
cNext = aDom[i+1];
if ( cNext == "'" || cNext == '"' )
{
sAttr = "";
j = 2;
while ( aDom[i+j] != cNext )
{
sAttr += aDom[i+j];
j++;
}
/* Replace jQuery UI constants */
if ( sAttr == "H" )
{
sAttr = oSettings.oClasses.sJUIHeader;
}
else if ( sAttr == "F" )
{
sAttr = oSettings.oClasses.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
* 如果字符串里面有#号,就给div加上id属性,如果字符串里面有.号,就给div加上class属性
*/
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 */
}
/*
* 将处理完成后的div放入根节点,并对根节点变量重新赋值为新创建的div
*/
nInsertNode.appendChild( nNewNode );
nInsertNode = nNewNode;
}
else if ( cOption == '>' )
{
/* End container div
* 这里标识一个div的闭合
*/
nInsertNode = nInsertNode.parentNode;
}
else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange )
{
/* Length
* 如果当前字符为l,则表示为dataTable增加可以操作每页显示行数的下来框
* 前置条件为bLengthChange和bPaginate配置为true,即允许分页并且允许用户选择每页显示行数
* 将在_fnFeatureHtmlFilter函数中创建class="mainTable_length"的元素,并且放入上面创建的div中
*/
nTmp = _fnFeatureHtmlLength( oSettings );
iPushFeature = 1;
}
else if ( cOption == 'f' && oSettings.oFeatures.bFilter )
{
/* Filter
* 如果当前字符为f,则表示为dataTable增加跨行搜索框
* 前置条件为bFilter配置为true,即启用内置搜索,可跨行搜索
* 将在_fnFeatureHtmlFilter函数中创建class="dataTables_filter"的元素,并且放入上面创建的div中
*/
nTmp = _fnFeatureHtmlFilter( oSettings );
iPushFeature = 1;
}
else if ( cOption == 'r' && oSettings.oFeatures.bProcessing )
{
/* pRocessing
* 如果当前字符为r,则表示为dataTable增加数据加载进度效果
* 前置条件为bProcessing配置为true,即显示加载时进度条
* 将在_fnFeatureHtmlProcessing函数中创建class="dataTables_processing"的元素,并且放入上面创建的div中
*/
nTmp = _fnFeatureHtmlProcessing( oSettings );
iPushFeature = 1;
}
else if ( cOption == 't' )
{
/* Table
* 这就是table实体了,将在_fnFeatureHtmlTable函数中创建table元素,table的属性、样式将沿用html代码中的定义
*/
nTmp = _fnFeatureHtmlTable( oSettings );
iPushFeature = 1;
}
else if ( cOption == 'i' && oSettings.oFeatures.bInfo )
{
/* Info
* 如果当前字符为i,则表示为dataTable增加表格相关信息,例如翻页信息等。
* 前置条件为bInfo配置为true,即显示表格相关信息
* 将在_fnFeatureHtmlInfo函数中创建class="dataTables_info"的元素,并且放入上面创建的div中
* */
nTmp = _fnFeatureHtmlInfo( oSettings );
iPushFeature = 1;
}
else if ( cOption == 'p' && oSettings.oFeatures.bPaginate )
{
/* Pagination
* 如果当前字符为p,则表示为dataTable增加分页功能。
* 前置条件为bPaginate配置为true,即开启分页功能
* 将在_fnFeatureHtmlPaginate函数中创建分页所需的元素,并且放入上面创建的div中
* */
nTmp = _fnFeatureHtmlPaginate( oSettings );
iPushFeature = 1;
}
else if ( DataTable.ext.aoFeatures.length !== 0 )
{
/* Plug-in features */
var aoFeatures = DataTable.ext.aoFeatures;
for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
{
if ( cOption == aoFeatures[k].cFeature )
{
nTmp = aoFeatures[k].fnInit( oSettings );
if ( nTmp )
{
iPushFeature = 1;
}
break;
}
}
}
/* Add to the 2D features array */
if ( iPushFeature == 1 && nTmp !== null )
{
if ( typeof oSettings.aanFeatures[cOption] !== 'object' )
{
oSettings.aanFeatures[cOption] = [];
}
oSettings.aanFeatures[cOption].push( nTmp );
nInsertNode.appendChild( nTmp );
}
}
/* Built our DOM structure - replace the holding div with what we want
* 将上面的临时div换成我们最后生成的div
*/
nHolding.parentNode.replaceChild( oSettings.nTableWrapper, nHolding );
}
最后附上一张,aDom
字符与界面元素的对应关系图: