分享
 
 
 

用XML和XSLT进行高级的Web UI设计(三)

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

用XML和XSLT进行高级的Web UI设计(三)

编译:alpha2002

目录树的管理

本文要求读者熟悉JScript,MSXML,XSL/XSLT,DOM。

下载例子代码

这是系列文章的第三篇,主要介绍如何扩展前面两篇文章所介绍的技术,在目录树中插入、修改、删除和改名目录树节点项。所有操作都在客户端实现,这样就降低了服务器的负担。本文所指的客户端用的是IE5.5+。

在上一篇文章我们所讨论的技术的基础上。我们拟在本文对目录树做如下的更新,更新后的目录树会更加实用。这些新特性包括:

自动化的IDs

没有限制的元数据支持

插入/更新功能

改名功能

删除功能

自动化的IDs

大家已经看到,在我们所讨论的目录树中的每一个元素或者节点都需要一个唯一的ID号。在前面的文章中,这个ID号都由用户来指定,这样做很难保证ID号的唯一性。本文将把这个任务交给脚本来自动完成。

在目录树的这个版本中,唯一的ID号是在显示树之前通过XSLT来进行即时产生和赋值。请看下面的XSLT代码:

<xsl:stylesheet version=''1.1''

xmlns:xsl=''http://www.w3.org/1999/XSL/Transform''

xmlns:dt=''urn:schemas-microsoft-com:datatypes''>

<xsl:template match=''/tree''>

<xsl:element name=''tree''>

<xsl:apply-templates select=''entity''/>

</xsl:element>

</xsl:template>

<xsl:template match=''entity''>

<xsl:element name=''entity''>

<xsl:attribute name=''id''>

<xsl:value-of select=''generate-id(.)''/>

</xsl:attribute>

<xsl:for-each select=''*''>

<xsl:if test=''name() = ''contents''''>

<xsl:element name=''contents''>

<xsl:apply-templates select=''entity''/>

</xsl:element>

</xsl:if>

<xsl:if test=''name() != ''contents''''>

<xsl:element name=''{name()}''>

<xsl:value-of select=''.''/>

</xsl:element>

</xsl:if>

</xsl:for-each>

</xsl:element>

</xsl:template>

</xsl:stylesheet>

这个式样页生成脚本遍历tree.xml文件并用generate-id()方法将一个唯一的ID赋值给每一个元素或节点。

应该注意到,如果我们的应用是一个N层应用并且节点内容都从数据库里采集的话,我们时不需要这个ID号自动产生过程的,数据库的记录号本身就可以拿来当作我们的ID号。

没有限制的元数据支持

所有对目录树的操作都允许按需进行元数据描述。也就是说元素名称和值可以任意修改,以便XML文档元素得到充分描述。

例如,下面的客户XML文件是上一篇文章用到过的。此外,你还会注意到两个文件中用户特定的元数据的差别,象publisher,address和phone等。我们的目录树将自动读取并解释这些新的元数据。还有就是作为保留元素"image",可以将"image"元素名随意改为"imageBase"。

<entity id="e5">

<description>ATL编程</description>

<imageBase>images/book.gif</imageBase>

<imageOpen>images/bookOpen.gif</imageOpen>

<onClick></onClick>

<onContextMenu>context/contextVckb.xml</onContextMenu>

<contents>

</contents>

</entity>

下面是本文客户XML文件的一个例子

<entity id="e2">

<description>COM编程指南</description>

<imageBase>images/book.gif</imageBase>

<imageOpen>images/bookOpen.gif</imageOpen>

<onClick>displayTOC(12345)</onClick>

<onContextMenu>context/contextTOC.xml</onContextMenu>

<publisher>VCKBASE OnlineJournal</ publisher>

<address1>1234</address>

<address2>Suite 123</address>

<city>Beautiful City</city>

<state>CN</state>

<zip>12345</zip>

<phone>(123)132-1234</phone>

<contents>

</contents>

</entity>

有了目前这个目录树的结构,子元素可从它们的父元素继承。例如,当你在"数据库"节点上单击右键并选择"插入"操作,你会得到一个子元素,同时还得到"数据库"节点所有元数据的描述。

插入/更新功能

插入与更新很类似,因为两者所操作的是完全一样的数据集,使用的表单也完全一样。所不同的是插入操作的表单初始化为空,而更新操作则用某个值初始化。利用这种相似性,我们可以在一个XSLT式样页中合并插入和更新功能,并用一个显示函数,然后再进行分开的调用。下面是用于插入/更新的XSLT脚本,其功能就是创建表单:

<xsl:stylesheet version="1.1"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns:dt="urn:schemas-microsoft-com:datatypes">

<xsl:param name="action"/>

<xsl:param name="selectedEntity"/>

<xsl:template match="entity">

<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="1" WIDTH="100%">

<xsl:for-each select="*">

<xsl:if test="name() != ''contents''">

<TR>

<TD CLASS="dataLabel" STYLE="border-right: 1px solid black;

border-bottom: 1px solid black;">

<xsl:value-of select="name()"/>

</TD>

<TD STYLE="border-right: 1px solid black; border-bottom: 1px solid black;" WIDTH="100%">

<INPUT CLASS="dataInput"

ONFOCUS="document.body.onselectstart = null"

ONBLUR="document.body.onselectstart = returnFalse;">

<xsl:attribute name="NAME">

<xsl:value-of select="name()"/>

</xsl:attribute>

<xsl:if test="$action = ''update''">

<xsl:attribute name="VALUE">

<xsl:value-of select="."/>

</xsl:attribute>

</xsl:if>

</INPUT>

</TD>

</TR>

</xsl:if>

</xsl:for-each>

<TR>

<TD STYLE="padding-right: 0px;"/>

<TD STYLE="padding-top: 0px;padding-left: 0px;">

<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0">

<TR>

<TD>

<INPUT TYPE="button" CLASS="buttonOff" NAME="Save" VALUE="保存" ONFOCUS="this.blur();"

ONMOUSEOVER="swapClass(this, ''buttonOver'')" ONMOUSEOUT="swapClass(this, ''buttonOff'')">

<xsl:attribute name="ONCLICK">

<xsl:value-of select="$action"/>Entity(''<xsl:value-of select="$selectedEntity"/>'')

</xsl:attribute>

</INPUT>

</TD>

<TD STYLE="padding-left: 2px;">

<INPUT TYPE="button" CLASS="buttonOff" NAME="Cancel" VALUE="取消"

ONFOCUS="this.blur();" ONMOUSEOVER="swapClass(this, ''buttonOver'')"

ONMOUSEOUT="swapClass(this, ''buttonOff'')" ONCLICK="content.innerHTML = '''';"/>

</TD>

</TR>

</TABLE>

</TD>

</TR>

</TABLE>

</xsl:template>

</xsl:stylesheet>

XSLT式样页接受两个参数,一个是"action",另一个是"selectedEntity"。参数"action"的有效值只能是"insert"和"update"。而"selectedEntity"的值则为元素的ID号。

图一XSLT 转换结果

插入/更新显示函数

此函数带有一个参数,用来接收"insert"或者"update"。然后它用selectedEntity

来确定插入或更新哪一个实体元素。它使用上面的XSLT式样页来显示相应的表单。

function insertUpdateDisplay(action) {

var xslDoc

var xslTemplate;

var xslProc;

var entity;

xslDoc = new ActiveXObject(''MSXML2.FreeThreadedDOMDocument'')

xslDoc.async = false;

xslTemplate = new ActiveXObject(''MSXML2.XSLTemplate'')

xslDoc.load("admin/insertUpdate.xslt");

xslTemplate.stylesheet = xslDoc;

xslProc = xslTemplate.createProcessor();

entity = xmlDoc.documentElement.selectSingleNode("//entity[@id=''" + selectedEntity +"'']");

xslProc.input = entity;

xslProc.addParameter("action", action);

xslProc.addParameter("selectedEntity", selectedEntity);

xslProc.transform();

content.innerHTML = xslProc.output;

}

插入操作/函数

这个函数的参数是要插入元素的父元素的元素ID。一旦子元素被创建,它便被添加到复原素的contents集合中。

function insertEntity(parentEntityID) {

var entity;

var newEntity;

var element;

var attribute;

var xslDoc;

var i;

xslDoc = new ActiveXObject(''MSXML2.FreeThreadedDOMDocument'')

xslDoc.async = false;

xslDoc.load("admin/tree.xslt");

entity = xmlDoc.documentElement.selectSingleNode("//entity[@id=''" + parentEntityID +"'']");

newEntity = xmlDoc.createElement("entity");

attribute = xmlDoc.createAttribute("id");

attribute.text = document.uniqueID;

newEntity.attributes.setNamedItem(attribute);

for(i=0; i < entity.childNodes.length; i++) {

element = xmlDoc.createElement(entity.childNodes(i).baseName);

if(entity.childNodes(i).baseName != "contents") {

element.text = eval(entity.childNodes(i).baseName + ".value")

}

newEntity.appendChild(element)

}

entity.selectSingleNode("contents").appendChild(newEntity);

document.all[parentEntityID].insertAdjacentHTML("beforeEnd", newEntity.transformNode(xslDoc));

document.all[parentEntityID].lastChild.style.display = "block";

if(document.all[parentEntityID].open == "false") {

document.all[parentEntityID].onclick();

}

saveXML();

}

更新操作/函数

函数参数为需要更新的元素的ID,函数遍历当前表单,从中读出数据。修改后,重新转换并它在浏览器的DOM中与现存的节点进行交换。

function updateEntity(entityID) {

var entity;

var xslDoc;

var container;

var i;

xslDoc = new ActiveXObject(''MSXML2.FreeThreadedDOMDocument'')

xslDoc.async = false;

xslDoc.load("admin/tree.xslt");

entity = xmlDoc.documentElement.selectSingleNode("//entity[@id=''" + entityID +"'']");

for(i=0; i < entity.childNodes.length; i++) {

if(entity.childNodes(i).baseName != "contents") {

entity.childNodes(i).text = eval(entity.childNodes(i).baseName + ".value")

}

}

container = document.createElement("DIV");

container.innerHTML = entity.transformNode(xslDoc)

container.childNodes(0).style.display = "block";

document.all[entityID].swapNode(container.childNodes(0));

container = null;

saveXML();

}

改名功能

当用户选择更改元素/节点名称时,调用函数renameEntityBegin()。然后它使用selectedEntity变量来确定要改哪一个元素。

这个方法首先获取所选节点文档对象的name,找到后将contentEditable属性置为true,这样便允许用户敲入。

我们还将输入焦点放到此元素上,将光标移到name 的开始处,设置了几个限定按键输入的事件后,用户就可以修改实体名称了。

function renameEntityBegin() {

var name;

name = document.all[selectedEntity + "name"]

name.contentEditable = true;

name.focus();

name.style.cursor = "text";

name.onkeypress = function anonymous() { renameKeyPress(selectedEntity) };

name.onblur = function anonymous() { renameEntityEnd(selectedEntity) };

name.onselectstart = null;

document.all[selectedEntity].onclick = null;

}

如果用户按下"回车"键或者激活onBlur事件,renameEntityEnd方法被调用。此方法的参数为要修改名字的元素ID。它对XML文档对象模型(DOM)和浏览器的DOM两者作相应修改。

function renameEntityEnd(entityID) {

var entity;

var name;

entity = xmlDoc.documentElement.selectSingleNode("//entity[@id=''" + entityID +"'']");

name = document.all[entityID + "name"]

name.style.cursor = "hand";

name.contentEditable = false;

name.onselectstart = function anonymous() { return false };

entity.selectSingleNode("description").text = name.innerText;

document.all[entityID].onclick = function anonymous() { clickOnEntity(document.all[entityID]) };

document.body.onselectstart = returnFalse;

saveXML();

}

删除功能

删除功能需要被删除实体的ID作为参数。此函数首先检查此实体没有子孙。否则报错。检查完成后,函数在XML DOM和浏览器的DOM中分别进行删除。

function deleteEntity() {

var entity;

entity = xmlDoc.documentElement.selectSingleNode("//entity[@id=''" + selectedEntity +"'']");

if(entity.selectSingleNode("contents").childNodes.length > 0) {

displayError("You cannot remove an entity that contains children. First remove all children."

., 8000);

}

else {

entity = entity.parentNode.removeChild(entity)

document.all[selectedEntity].removeNode(true)

saveXML();

}

}

重定向用户

这个版本的目录树还包括一个简单的重定向方法,其参数为一个URL串。在你的目录树XML文件中,你必须在onClick元素的位置调用重定向方法,如图三之高亮部分。

图三在onClick 元素中调用redirect方法

下面图四的高亮部分描述了在tree.xslt文件中如何插入onClick 事件。

图四 插入onClick 事件

与数据库的接口

如果你需要与数据库的接口,我推荐你在init()、insertEntity()、updateEntity、deleteEntity和renameEntityEnd方法中使用XMLHTTP对象。这些方法应该与你的Web服务联系起来,并且只发送和接收需要实现所请求的XML文件。这个方法将降低大大降低服务器的负载。不用在客户端和服务器之间来回折腾, 一而再,再而三地传输并显示整个目录树XML文件。你可以在幕后轻松实现一次调用来更新你的数据,并根据所执行的动作,只更新客户浏览器需要更新的部分。

在下一部分我将介绍如何更酷功能--实现目录树中元素的拖拽(drag and drop)功能。(待续)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有