利用XML数据岛创建列表
1 树形列表(tree-list)
由于第一次加载页面而列表元素很多时会引起时间延迟和较低的性能,因而避免首次调用便加载整个列表结构,采用扩展列表节点时创建子节点的办法。为了达到performance,control of the manual list和automatic update with the changes of XML data的平衡,可以为需要绑定XML元素的HTML元素创建嵌套的数据绑定模版,同时确保仅当列表节点扩展时才设置dataSrc属性。 仅仅为了演示,所以下面的tree.xml文件所包含的元素节点很少,不能同时用作性能测试的参照。:)
当加载整个页面时,使用CSS的display: none属性隐藏列表节点包含的内层子节点。对于嵌套的两个绑定子表,先不设置其绑定源(dataSrc属性);当列表节点被点击时,先检查它是否包含子节点,只有存在的情况下嵌套的子表才绑定。
效果图为:
初略测试(不精确)约为1000个记录加载用时20秒。
2 virtual list
SHOW ME:)
The basic requirement of a virtual list is that it give the user the impression that all of the possible data is in the list, while behind the scenes, the elements are retrieved only when the user scrolls to see them.
We creat two tables:
The first table is our virtual list table (vtable). The vtable is actually a <DIV> of a defined width and height, with the overflow Cascading Style Sheets (CSS) property set to "scroll." The second, hidden, table (we'll call that the data table) actually does the data binding. The data table is set up to use the datapagesize property, so the amount of data downloaded can be easily controlled. As data becomes available in the data table, the rows are copied into the vtable. As the vtable is scrolled, it signals the data table that it needs more data, and then as soon as the data is downloaded, it's copied to the vtable. All the user sees is a seamless, scrolling list.
As any data bound table is loaded with data, its readyState property will sequence through the "loading," "interactive," and finally to the "complete" state. Every time the readyState property of the data table changes, the onreadystatechange event will fire.When the readyState of the table is "complete," the data is in the data table. At this point, the next chunk of rows in the data table can be copied to the vtable.
由于第一次加载页面而列表元素很多时会引起时间延迟和较低的性能,因而避免首次调用便加载整个列表结构,采用扩展列表节点时创建子节点的办法。为了达到performance,control of the manual list和automatic update with the changes of XML data的平衡,可以为需要绑定XML元素的HTML元素创建嵌套的数据绑定模版,同时确保仅当列表节点扩展时才设置dataSrc属性。 仅仅为了演示,所以下面的tree.xml文件所包含的元素节点很少,不能同时用作性能测试的参照。:)
//tree.xml
<?xml version="1.0"?>
<tree>
<node>
<image>pare.gif</image>
<value>第一层 第一行 一直显示</value>
<haschildren>true</haschildren>
<node>
<image>pare.gif</image>
<value>第二层 '第二行' 初始隐藏</value>
<haschildren>true</haschildren>
<node>
<image>end.gif</image>
<value>第三层 '第三行' 初始隐藏</value>
<haschildren>false</haschildren>
<node/>
</node>
</node>
</node>
<node>
<image>end.gif</image>
<value>第一层 初始为显式的第二行</value>
<haschildren>false</haschildren>
<node/>
</node>
</tree>
<?xml version="1.0"?>
<tree>
<node>
<image>pare.gif</image>
<value>第一层 第一行 一直显示</value>
<haschildren>true</haschildren>
<node>
<image>pare.gif</image>
<value>第二层 '第二行' 初始隐藏</value>
<haschildren>true</haschildren>
<node>
<image>end.gif</image>
<value>第三层 '第三行' 初始隐藏</value>
<haschildren>false</haschildren>
<node/>
</node>
</node>
</node>
<node>
<image>end.gif</image>
<value>第一层 初始为显式的第二行</value>
<haschildren>false</haschildren>
<node/>
</node>
</tree>
<xml id=TheData src="tree.xml"></xml>
<table id=PrimaryTable cellspacing=0 cellpadding=0 datasrc=#TheData border=0>
<tr onclick="toggle(this)">
<td>
<img id=Icon datafld="image">
<span datafld="value"></span>
<span id=HasChildren style="display: none" datafld="haschildren"></span>
</td></tr>
<tr style="display: none">
<td>
<!-- second level -->
<table cellspacing=0 cellpadding=0 datafld="node" border=0>
<tr onclick="toggle(this)">
<td>
<img id=Icon datafld="image">
<span datafld="value"></span>
<span id=HasChildren style="display: none" datafld="haschildren">
</span>
</td>
</tr>
<tr style="display: none">
<td>
<!-- third level -->
<table cellspacing=0 cellpadding=0 datafld="node.node" border=0>
<tr>
<td>
<img id=Icon datafld="image">
<span datafld="value"></span>
<span id=HasChildren style="display: none" datafld="haschildren">
</span>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
<table id=PrimaryTable cellspacing=0 cellpadding=0 datasrc=#TheData border=0>
<tr onclick="toggle(this)">
<td>
<img id=Icon datafld="image">
<span datafld="value"></span>
<span id=HasChildren style="display: none" datafld="haschildren"></span>
</td></tr>
<tr style="display: none">
<td>
<!-- second level -->
<table cellspacing=0 cellpadding=0 datafld="node" border=0>
<tr onclick="toggle(this)">
<td>
<img id=Icon datafld="image">
<span datafld="value"></span>
<span id=HasChildren style="display: none" datafld="haschildren">
</span>
</td>
</tr>
<tr style="display: none">
<td>
<!-- third level -->
<table cellspacing=0 cellpadding=0 datafld="node.node" border=0>
<tr>
<td>
<img id=Icon datafld="image">
<span datafld="value"></span>
<span id=HasChildren style="display: none" datafld="haschildren">
</span>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
<script>
//set the cache of the images
var i1, i2, i3;
i1 = new Image();
i1.src = "pare.gif";
i2 = new Image();
i2.src = "sub.gif";
i3 = new Image();
i3.src = "end.gif";
function toggle(e) {
var nextRow;
nextRow = e.nextSibling;
hc = e.all.HasChildren;
if (!hc)
return;
if (hc.innerText == "false")
return;
if (nextRow.style.display == "none") {
nextRow.style.display = "";
e.all.Icon.src = "sub.gif";
// skips the td and comment, getting the third child, which is the table
nextTable = nextRow.all[2];
if (!nextTable.dataSrc) {
nextTable.dataSrc = "#TheData";
}
} else {
nextRow.style.display = "none";
e.all.Icon.src = "pare.gif";
}
}
</script>
//set the cache of the images
var i1, i2, i3;
i1 = new Image();
i1.src = "pare.gif";
i2 = new Image();
i2.src = "sub.gif";
i3 = new Image();
i3.src = "end.gif";
function toggle(e) {
var nextRow;
nextRow = e.nextSibling;
hc = e.all.HasChildren;
if (!hc)
return;
if (hc.innerText == "false")
return;
if (nextRow.style.display == "none") {
nextRow.style.display = "";
e.all.Icon.src = "sub.gif";
// skips the td and comment, getting the third child, which is the table
nextTable = nextRow.all[2];
if (!nextTable.dataSrc) {
nextTable.dataSrc = "#TheData";
}
} else {
nextRow.style.display = "none";
e.all.Icon.src = "pare.gif";
}
}
</script>
效果图为:
初略测试(不精确)约为1000个记录加载用时20秒。
2 virtual list
SHOW ME:)
The basic requirement of a virtual list is that it give the user the impression that all of the possible data is in the list, while behind the scenes, the elements are retrieved only when the user scrolls to see them.
We creat two tables:
The first table is our virtual list table (vtable). The vtable is actually a <DIV> of a defined width and height, with the overflow Cascading Style Sheets (CSS) property set to "scroll." The second, hidden, table (we'll call that the data table) actually does the data binding. The data table is set up to use the datapagesize property, so the amount of data downloaded can be easily controlled. As data becomes available in the data table, the rows are copied into the vtable. As the vtable is scrolled, it signals the data table that it needs more data, and then as soon as the data is downloaded, it's copied to the vtable. All the user sees is a seamless, scrolling list.
<xml id=LargeData src=dom2.xml></xml>
<div id=ScrollContainer style="overflow: scroll; background-color: blue; color: yellow; width: 100%; height: 400px">
<table border=1 id=TargetTable width=100% style="table-layout: fixed;border-collapse: collapse;" >
<tbody id=TargetBody> </tbody>
</table>
</div>
//you might want to try out modifying the value of datapagesize property;
//but it has some pretty dramatic changes in the performance of this code.
<table id=SourceTable datasrc=#LargeData
datapagesize=20 width=100%
style="table-layout: fixed; display: none">
<tr>
<td>
<div datafld=data></div>
</td>
</tr>
</table>
<div id=ScrollContainer style="overflow: scroll; background-color: blue; color: yellow; width: 100%; height: 400px">
<table border=1 id=TargetTable width=100% style="table-layout: fixed;border-collapse: collapse;" >
<tbody id=TargetBody> </tbody>
</table>
</div>
//you might want to try out modifying the value of datapagesize property;
//but it has some pretty dramatic changes in the performance of this code.
<table id=SourceTable datasrc=#LargeData
datapagesize=20 width=100%
style="table-layout: fixed; display: none">
<tr>
<td>
<div datafld=data></div>
</td>
</tr>
</table>
// when the data bound table is readyState == complete
// this alerts that there is new data to transfer.
// this method then calls the copyRows() method, and calls
// testForScroll() to handle the case where the
// scroll thumb has been moved by a large amount
function dataAvailable() {
if (SourceTable.readyState == "complete") {
copyRows();
}
testForScroll();
}
// the readyState of the data table will change as
// data fills in, this can tell us when its ok to copy data
SourceTable.onreadystatechange = dataAvailable;
// this alerts that there is new data to transfer.
// this method then calls the copyRows() method, and calls
// testForScroll() to handle the case where the
// scroll thumb has been moved by a large amount
function dataAvailable() {
if (SourceTable.readyState == "complete") {
copyRows();
}
testForScroll();
}
// the readyState of the data table will change as
// data fills in, this can tell us when its ok to copy data
SourceTable.onreadystatechange = dataAvailable;
copyRows() loops through all the rows in the data table, and for each row, does a deep copy, and inserts the copy into the vtable.
var TotalRecordCount;
var ScrollBuffer = 50;
var LastRecordNumber = -1;
// this method actually moves the data from the
// bound table to the virtual table on demand
// it detects when all the data has been copied,
// and stops the transfer
function copyRows() {
var i, copyRow, row;
for (i=0; i<SourceTable.rows.length; i++) {
row = SourceTable.rows[i];
// test if all data copied
if (row.recordNumber <= LastRecordNumber) {
// if so, no more data to copy, so unhook
ScrollContainer.onscroll = null;
SourceTable.onreadystatechange = null;
return;
}
// deep copy the row from the table
copyRow = row.cloneNode(true);
TargetBody.insertBefore(copyRow, null);
// global variable LastRecordNumber used to keep track of
// how many rows are copied to insure no duplicates
LastRecordNumber = row.recordNumber;
// removes a placeholder for scrolling for each row
// inserted in the vtable
deletePlaceholder();
}
}
var ScrollBuffer = 50;
var LastRecordNumber = -1;
// this method actually moves the data from the
// bound table to the virtual table on demand
// it detects when all the data has been copied,
// and stops the transfer
function copyRows() {
var i, copyRow, row;
for (i=0; i<SourceTable.rows.length; i++) {
row = SourceTable.rows[i];
// test if all data copied
if (row.recordNumber <= LastRecordNumber) {
// if so, no more data to copy, so unhook
ScrollContainer.onscroll = null;
SourceTable.onreadystatechange = null;
return;
}
// deep copy the row from the table
copyRow = row.cloneNode(true);
TargetBody.insertBefore(copyRow, null);
// global variable LastRecordNumber used to keep track of
// how many rows are copied to insure no duplicates
LastRecordNumber = row.recordNumber;
// removes a placeholder for scrolling for each row
// inserted in the vtable
deletePlaceholder();
}
}
// event handler for onscroll for the ScrollContainer
// each time the table is scrolled, we check to see
// if the last placeholder is close to in view.
// if it is, we need to get more data.
function testForScroll() {
if (ScrollContainer.scrollTop +
ScrollContainer.offsetHeight + ScrollBuffer >=
firstspacer.offsetTop) {
getMoreData();
}
}
// this function advances the page in the databound table
// note that the nextPage() method is asynch, and will
// return before the new data is in.
// to resolve that problem, the onreadystatechange event
// is hooked in the databound table.
// this event is hooked by the dataAvailable() method
function getMoreData() {
if (SourceTable.readyState != "complete")
return;
SourceTable.nextPage();
}
// each time the table is scrolled, we check to see
// if the last placeholder is close to in view.
// if it is, we need to get more data.
function testForScroll() {
if (ScrollContainer.scrollTop +
ScrollContainer.offsetHeight + ScrollBuffer >=
firstspacer.offsetTop) {
getMoreData();
}
}
// this function advances the page in the databound table
// note that the nextPage() method is asynch, and will
// return before the new data is in.
// to resolve that problem, the onreadystatechange event
// is hooked in the databound table.
// this event is hooked by the dataAvailable() method
function getMoreData() {
if (SourceTable.readyState != "complete")
return;
SourceTable.nextPage();
}
The getMoreData() method actually does the work of advancing the cursor on the table to get the next chunk of data. However, as discussed above, the actual copying occurs only after the data is loaded.
// to make sure that the scroll bars are the right size,
// one spacer is created for eachrow that can be
// inserted. These are removed as real rows are created.
function insertScrollBlocks() {
var i, br;
for (i=0; i<TotalRecordCount; i++) {
br = document.createElement("SPAN");
if (i == 0) {
br.id = "firstspacer";
} else {
br.id = "spacer";
}
br.style.display = "block";
ScrollContainer.insertBefore(br, null);
}
}
// method to remove a placeholder when a real row is inserted
function deletePlaceholder() {
p = document.all.spacer;
if (p == null)
return;
if (p.length != null) {
brToRemove = p[p.length - 1];
if (brToRemove == null)
return;
brToRemove.removeNode();
}
}
// one spacer is created for eachrow that can be
// inserted. These are removed as real rows are created.
function insertScrollBlocks() {
var i, br;
for (i=0; i<TotalRecordCount; i++) {
br = document.createElement("SPAN");
if (i == 0) {
br.id = "firstspacer";
} else {
br.id = "spacer";
}
br.style.display = "block";
ScrollContainer.insertBefore(br, null);
}
}
// method to remove a placeholder when a real row is inserted
function deletePlaceholder() {
p = document.all.spacer;
if (p == null)
return;
if (p.length != null) {
brToRemove = p[p.length - 1];
if (brToRemove == null)
return;
brToRemove.removeNode();
}
}
The real trick of this sample was to get the scrollbar to work properly. The solution was to insert "spacer" objects in the vtable. One spacer was inserted for each record that could come in. As each real row came in, a corresponding spacer was deleted.
// onload handler for the document.
function doLoad() {
// figure out total size of virtual list, to get spacers right
TotalRecordCount = LargeData.recordset.recordcount - 10;
// insert spacers into the vtable
insertScrollBlocks();
// hook scrolling and data events
ScrollContainer.onscroll = testForScroll;
SourceTable.onreadystatechange = dataAvailable;
// initialize the first set of data
testForScroll();
}
window.onload = doLoad;
function doLoad() {
// figure out total size of virtual list, to get spacers right
TotalRecordCount = LargeData.recordset.recordcount - 10;
// insert spacers into the vtable
insertScrollBlocks();
// hook scrolling and data events
ScrollContainer.onscroll = testForScroll;
SourceTable.onreadystatechange = dataAvailable;
// initialize the first set of data
testForScroll();
}
window.onload = doLoad;