CSS 友好的 AJAX 树控件






2.20/5 (2投票s)
一个非常简单且轻量级的 AJAX 分层树形控件。
引言
我正在开发一个 WebPart,我需要一个客户端分层树形控件。 我还想要一个 CSS 友好的特定标记。 然后我发现这个很棒,并让我开始了。 我现在已经这样设置好了,效果很好。 唯一的问题是,我可能处理的一些数据集会非常大。 这个脚本做了一些初始设置,对于大数据来说代价很高。 因此,我衍生出了这段代码。 我不再使用树形控件 JavaScript,但我仍然使用 CSS,并且该代码是我最终代码的基础。
背景
我想指出一些我认为对 Web 开发的所有方面都有帮助的事情。
- 命名空间
- JavaScript 类
- 日志记录
- 简化的 AJAX 调用
- 服务器端代码
请参阅此链接,了解关于为什么这种方法可行的更详细的讨论。 Dustin 还有一本非常有用的书,现在就放在我的桌子上。
var DE = {
data : {},
net : {},
client : {},
util : {
coll: {}
},
widget : { },
example : { }
};
用法: 鉴于今天的代码是框架的子集,因此在您的代码中使用命名空间至关重要。
////DOM class
DE.util.DOM = function() {
//create private member vars here.
//Only protected methods have access to these vars
//this all falls in the realm of js classes and is not
//discussed fully in this article but
//can be found all over the web.
//protected method with access to private member vars
this.addClass = function() {
.......
}
this.anotherMethod = function() {...........}
};
//public method
DE.util.DOM.prototype.$ = function() {
using the above class:
var varName = new DE.util.DOM();
这非常有用而且必要。 警报很快就会过时!
var log = new DE.util.Logger();
log.show( true );
log.showTimeStamp(true);
现在,记录就像这样简单:
log.Log("log this now");
您还需要在您的页面上创建一个 div
,其 id="Log"
。
<div id="Log"></div>
var _cb = new DE.util.HierFilterCB('filterdata' );
ajax1.setAsyncCall(false); or not Async jax
ajax1.request('GET',"HierFilterPage.aspx?rnd="+ Math.random(), _cb );
HierFilter hier = null;
String parent = Request.QueryString["parent"];
if (!String.IsNullOrEmpty(parent))
{
//if index is not null then a specific request for a node has been made
//index is null only on intial load
hier = new HierFilter(@"C:/Downloads/CssFriendly" +
@"AjaxTreeCtrl/App_Data/hierdata.xml", Int32.Parse(parent));
Response.Write( hier.ToString() );
}
简而言之
客户端最初调用服务器以获取深度为 0 的 XML 数据。
var ajax1 = new DE.util.Ajax();
var _cb = new DE.util.HierFilterCB('filterdata' );
ajax1.setAsyncCall(false);
ajax1.request('GET',"HierFilterPage.aspx?rnd="+ Math.random(), _cb );
上面的代码不是异步的,因为我们需要确保返回所有数据,因为回调负责解析服务器返回的数据。 客户端调用以下代码块。 服务器通过传递数据(XML)的位置来实例化一个 HierFilter
类型。
//intial load
hier = new HierFilter(@"C:/Downloads/" +
@"CssFriendlyAjaxTreeCtrl/App_Data/hierdata.xml");
Response.Write(hier.ToString());
HierFilter
类继承自 FilterBase
。 在此示例中,FilterBase
负责将处理后的数据格式化回客户端。
HierFilter.cs
public HierFilter( String file) : base()
{
selectedIndicies = "";
XmlWriter w = base.getXmlWriter();
XmlDocument doc = new XmlDocument();
doc.Load( file );
CreateTree(w, doc);
}
FilterBase.cs
以下代码块将有助于创建格式良好的 XHTML 数据以发送回客户端。
public XmlWriter getXmlWriter()
{
//uses class variable sb
XmlWriterSettings settings = new XmlWriterSettings();
XmlWriter w;
//start first <ul> tag
//tell the xml parser that i am not abiding by strigent xml rules
settings.ConformanceLevel = ConformanceLevel.Fragment;
//pretty print - not necessary but good for debugging
settings.Indent = true;
//_settings.Encoding = Encoding.Unicode;
//dont show xml info in header
settings.OmitXmlDeclaration = true;
settings.CloseOutput = false;
//XmlWriter factory push xml to string builder with these settings
w = XmlWriter.Create(sb, settings);
return w;
}
HierFilter.cs
以下代码在级别 0 创建树数据。 CSS 友好的树标记是一种嵌套的无序列表元素的模式。
<ul id="mktree">
<li>......</li>
<li>......</li>
</ul>
代码
private void CreateTree(XmlWriter w, XmlDocument doc )
{
//get node list
// InitSettings();
w.WriteStartElement("ul"); // beginning <ul> tag
w.WriteStartAttribute("class");
w.WriteValue("mktree");
w.WriteStartAttribute("id");
w.WriteValue("hierfilter");
GoDeeper(w , doc , 0 );
w.WriteEndElement(); //</ul> outermost ul tag
w.Flush();
}
private void GoDeeper(XmlWriter w , XmlDocument doc, int level )
{
bool state =false;
int index = 0;
bool hasChildren = false;
//create a tree based on the level provided
XmlNodeList nodes = doc.SelectNodes("//n[@d='"+ level +"']");
foreach (XmlNode node in nodes)
{
if (node.Attributes.Count == 4)
{
//get state from xml
if (node.Attributes[3].Value == "0")
state = false;
else
state = true;
}
index = Int32.Parse(node.Attributes[0].Value);
hasChildren = Boolean.Parse(node.Attributes[2].Value);
w.WriteStartElement("li"); // beginning <ul> tag
if (hasChildren)
{
//if the node has children place a plus gif next to it using css
w.WriteStartAttribute("class");
w.WriteValue("liClosed");
}
w.WriteStartElement("span");
w.WriteStartAttribute("class");
w.WriteValue("index");
w.WriteEndAttribute();
w.WriteValue(index.ToString());
w.WriteEndElement();
w.WriteStartElement("span");
w.WriteStartAttribute("class");
w.WriteValue("bullet");
w.WriteEndAttribute();
w.WriteValue( node.InnerText );
w.WriteEndElement();
//w.WriteValue(node.InnerText );
w.WriteEndElement();
}
//close all oustanding xml tags
//CloseOut(w, elementStack);
}
单击节点时会调用以下代码。 这传入父 ID,并以格式良好的 XHTML 格式返回其所有子节点。
public void GetChildren( XmlWriter w, XmlDocument doc, int parent )
{
bool state = false;
int index = 0;
bool hasChildren = false;
//w.WriteStartElement("ul"); // beginning <ul> tag
//w.WriteStartAttribute("class");
//w.WriteValue("liOpen");
//create a tree based on the level provided
XmlNodeList nodes = doc.SelectNodes("//n[@p='" + parent + "']");
foreach (XmlNode node in nodes)
{
if (node.Attributes.Count == 4)
{
//get state from xml
if (node.Attributes[3].Value == "0")
state = false;
else
state = true;
}
index = Int32.Parse(node.Attributes[0].Value);
hasChildren = Boolean.Parse(node.Attributes[3].Value);
w.WriteStartElement("li"); // beginning <ul> tag
if (hasChildren)
{
//if the node has children place a plus gif next to it using css
w.WriteStartAttribute("class");
w.WriteValue("liClosed");
}
else
{
w.WriteStartAttribute("class");
w.WriteValue("liBullet");
}
w.WriteStartElement("span");
w.WriteStartAttribute("class");
w.WriteValue("index");
w.WriteEndAttribute();
w.WriteValue(index.ToString());
w.WriteEndElement();
w.WriteStartElement("span");
w.WriteStartAttribute("class");
w.WriteValue("bullet");
w.WriteEndAttribute();
w.WriteValue(node.InnerText);
w.WriteEndElement();
//w.WriteValue(node.InnerText );
w.WriteEndElement();
}
// w.WriteEndElement(); //end of ul of liOpen
w.Flush();
}
回到启动所有操作的客户端回调。 以下内容位于 success 方法中。 如果 AJAX 调用完成且没有错误,则将调用此方法。 返回的数据格式良好,因此我们解析它并查找某些标准,这些标准将允许 CSS 设置我们正在增长的树的样式。 如果元素具有类名 liClosed
,那么我们想将一个 OnClick
事件附加到它。 这转化为一个节点是否具有子节点。 如果一个节点没有子节点,那么我们就不需要调用服务器。 我们如何知道节点是否有子节点? XML 具有一个属性 'hc
',它在服务器上进行处理并传递给客户端。
随着树按级别展开,模式会作为嵌套的无序列表增长。
<ul>
<li>
<ul>
<li>....../<li>
</ul>
</li>
</ul>
代码
if ( n.className == 'liClosed')
{
n.onclick = function() {
log.Log("classname inside HierFilterCB " + n.className );
//check to see if node has been loaded
var ul = n.getElementsByTagName('ul');
if ( ul.length > 0 )
{
if ( n.className == 'liClosed')
{
n.className = 'liOpen';
}else{n.className = 'liClosed';}
}else
{
var spans = n.getElementsByTagName('span');
//get the index from here
for ( var j =0; j<spans.length;j++)
{
if ( spans[j].className == 'index')
{
var index = spans[j].innerText;
var child = new DE.util.ChildItemCB( n );
var childrenAjax = new DE.util.Ajax();
childrenAjax.request('GET',
'HierFilterPage.aspx?parent='+index,child);
}
log.Log("spans " + spans[j] );
}
}
}
}
单击节点时,如果尚未向服务器发出请求,则会调用以下代码。 如果已经请求过,那么上面有一些代码会修改 CSS 来展开或折叠树。
//callback method called when
//a node is clicked after intial loading is complete.
//see also DE.util.HierFilterCB for more info
//parsing is better understood by inspecting hierdata.xml
DE.util.ChildItemCB = function( child ) {
this.success = function(val){
log.Log("child className " + child.className ) ;
var children = document.createElement('ul')
children.innerHTML = val;
var li = children.getElementsByTagName('li');
for ( var i=0; i < li.length; i++ )
parseTree(li[i]);
上面的解析树是在服务器返回的每个 <li>
项上调用的。 同样,模式是,如果一个节点有子节点,那么我们需要将一个 OnClick 事件应用到它并修改它的类名,因此允许 CSS 用适当的加号/减号正确地设置我们的树的样式。
if ( n.className == 'liClosed')
{
log.Log("n "+ n.innerText);
log.Log("n.className " + n.className );
n.onclick = function(e) {
e = e || (window.event || {});
e.cancelBubble = true;
...................
上面的代码类似于第一次调用服务器。 Accept 必须取消从其父节点继承的任何事件冒泡。
这是一个使用的示例 XML 数据集
<n i='0' d='0' hc='true' s='0'>1 - Scope</n>
<n i='1' p='0' d='1' hc='false' s='0'>1.1 - Determine project scope</n>
<n i='2' p='0' d='1' hc='false' s='0'>1.2 - Secure project sponsorship</n>
<n i='3' p='0' d='1' hc='false' s='0'>1.3 - Define preliminary resources</n>
<n i='4' p='0' d='1' hc='false' s='0'>1.4 - Secure core resources</n>
<n i='5' p='0' d='1' hc='false' s='0'>1.5 - Scope complete</n>
<n i='6' p='0' d='1' hc='true' s='0'>1.6 - Program Group</n>
<n i='7' p='6' d='2' hc='false' s='0'>1.6.1 - Milestone 1</n>
<n i='8' p='6' d='2' hc='false' s='0'>1.6.2 - Milestone 2</n>
<n i='9' p='6' d='2' hc='false' s='0'>1.6.3 - Milestone 3</n>
使用代码
下载代码并更改 HierFilterPage.aspx 中 XML 文件的路径,然后从 Default.aspx 运行。
关注点
这仅适用于 IE6,不确定 IE7,但我希望收到有关如何使其与 Firefox 一起使用的反馈。 所以,请,如果有人让它工作,我想看看。 干杯!
这是在 WebDeveloper 2008 中开发的。但是,使用任何基于 Visual Studio 的 IDE 应该很容易,只需删除解决方案文件并从您选择的 IDE 打开项目即可。
历史
首次发布 - 2008 年 8 月 13 日。