javascript总结(六)页面元素的创建调整与关联

[size=medium]
[u][b]目录[/b][/u]
[url=http://www.iteye.com/topic/625734](一)有关框架[/url]
[url=http://www.iteye.com/topic/627308](二)文件组织与代码组织[/url]
[url=http://www.iteye.com/topic/627439](三)JS与FLASH交互[/url]
[url=http://www.iteye.com/topic/627538](四)ajax局部刷新与RPC[/url]
[url=http://www.iteye.com/topic/628228] (五)获取设置元素样式与监听元素事件[/url]
(六)页面元素的创建调整与关联
(七)浏览器兼容性问题
(八)WEB软件的前端架构实践
[/size]
[color=red]文章中的代码只为表达文章意义,非真正能执行的代码。[/color]
[size=large][b]动态创建、调整element[/b][/size]
[size=medium]一、你考虑清楚要直接操作element吗?[/size]

[b]用这个疑问来表示对这种行为的慎重,操作element有哪些地方你可能需要考虑呢:[/b]
[list]
[*]美工对设计作了更改后,引起HTML结构变化时和样式变化时,需要变更代码(而不是模板)
[*]用代码来组织HTML结构,一点也不形象,维护起来也麻烦
[*]数据变化后,要在JS逻辑中维护页面变化,逻辑复杂,同样设计更改后,这部分代码也得改
[*]部分元素在不同浏览器下的操作方法不一致,导致你可能要针对不同浏览器来写代码
[*]通过标准DOM接口直接操作element与使用innerHTML整块设置HTML来比较,速度稍慢[/list]

[b]即使有以上考虑,有以下情形我还是选择直接操作element[/b]
[list]
[*]如果业务逻辑足够简单,HTML结构也相对简单。比如更新用户名后更新页面上其他地方的用户名。
[*]如果HTML比较复杂,但数据或者状态变化后,只需要变化一个地方。
[*]在结构稳定的UI widget中,比如对话框。(以之相反的是grid,我会选择使用模板)
[*]HTML中输入控件比较多 ,大部分时候需要保持控件的当前值和光标聚焦位置
[/list]
另外,刷新整个部分与只更新变化的地方,用户体验会更好,不会闪的那么厉害,而且要做动画表示这个变化也方便。
[size=medium]二、应用模板进行局部更新[/size]
[b]应用整体模板[/b]
应用模板编程的优势及示例,我就不再重复。再次请大家参见金大为的[url=http://jindw.iteye.com/blog/567161]雏凤清音 -- 面向数据的前端编程方法[/url]

[b]应用局部模板[/b]

var groupManagerCmp = {
body: "#userGroup",
viewTemplete: new Template( "<tr><td>${name}</td><input type='button' value='编辑' /><td></td><td><input type='button' value='删除' /></td></tr>"),
editTemplate: new Template( "<tr><td><input type='text' value='${name}'></td><td><input type='button' value='提交' /></td><td><input type='button' value='取消' /></td></tr>"),
renderViewRow: function(id, name){
var tr = this.viewTemplete.bind(id,name);
$("input[0]",tr).click(function(){ //编辑
groupManagerCmp.edit(tr, id, name);
})
$("input[1]",tr).click(function(){ //删除
action.do("delete", {id: id}, function(){
groupManagerCmp.remove(tr);
})
})
return tr;
},
renderEditRow: function(id, name){
var tr = this.editTemplate.bind(id,name);
$("input[0]",tr).click(function(){ //提交
action.do("put", {id: id, name: name}, function(group){
groupManagerCmp.update(tr, group.id, group.name);
})
});
$("input[1]",tr).click(function(){ //取消
groupManagerCmp.update(tr, id, name);
})
return tr;
},
append: function(id, name){
$(this.body).appendChild(this.renderViewRow(id, name));
},
remove: function(trNode){
$(this.body).removeChild(trNode);
},
edit: function(trNode, id, name){
$(this.body).replaceNode(trNode, this.renderEditRow(id, name));
},
update: function(trNode, id, name){
$(this.body).replaceNode(trNode, this.renderViewRow(id,name));
}
}

var action = {
url: {
post: "group/new",
put: "group/update",
delete: "group/destroy"
}
do: function(action, params, callback){
ajax(this.url[action], params, function(data){
callback(decode(data))
});
}
}

[size=medium]三、应用状态模式应对复杂的界面变化[/size]
项目中有一个随机聊天的东西,系统从到达此页面的人中随机两两配对,进行聊天。
业务过程:

用户点击开始配对-----》系统开始找人与你聊天 <-------------------------------
| |
---------------------------- |
| | |
找到人,开始聊天 没找到,提示可重新配对 |
| | |
聊天中 点击重新配对-----------------------------
| |
| |
对方退出或中断,提示可重新配对 |
| |
--------------------------- |
| | |
关闭页面 点击重新配对--------------------------------



//某种状态变化后,都调用updateView更新界面
function updateView(state, args){
if(state == "connectSuccess"){//连接成功
chatWin.show(args);
jQuery("connectWin").hide();
}else{
chatWin.hide();
if(state == "ready"){ //未连接,等待用户点击连接
//code
}else if(state == "connecting"){ //正在连接
//code
}else if(state == "connectFail"){ //连接失败
jQuery("#status").text("连接失败");
jQuery("#btnConnect").show();
}else if(state == "strangerLeft"){ //对方离开
//code
}else if(state == "connectInterrupt"){ //连接中断
//code
}else if(state == "timeout"){//连接超时
//code
}
}
}

function connect(currentUser){
chatProxy.connect(currentUser.userId, function(state, data){
if(state == "success"){
updateView("connectSuccess", data.stranger);
}else{
updateView("connectFail");
}
})
}

[size=medium]三、应用观察者模式应对多处界面变化[/size]
示例:当用户变更它的个人信息时,更新见面中显示用户个人信息的地方。用户的个人信息可能在多个页面的不同位置显示。

//用户类
User = function(uinfo){
for(var p in uinfo){
this[p]=uinfo[p];
}
this._observers = [];
};

User.prototype = {
//更新属性
update: function(property, value){
if(this.hasOwnProperty(property)){
if(this[property] != value){
this[property] = value;
this.notice(property);
}
}
},
//增加属性变更监听者(函数)
addListener: function(property, observer){
if(!this._observers[property])this._observers[property] = [];
this._observers[property].push(observer);
},
//通知变更
notice: function(property){
var observers = this._observers[property];
if(observers && observers.length){
var value = this[property];
$.each(observers, function(i, fn){
fn && fn(value);
})
}
}
}

//在页面初始化代码中添加监听函数
jQuery(document).ready(function(){
currentUser.addListener("name", function(name){
jQuery("#userName").text(name);
})
currentUser.addLIstener("icon", function(icon){
jQuery("#userIcon").attr("src",icon);
})
})
//在业务代码中,当用户更新个人信息,用户对象自动调用监听函数更新页面。
currentUser.update("name","lucy");
currentUser.update("icon","url");

[size=medium]三、给元素操作取一个名字[/size]
给逻辑相关的元素操作封装在一个方法里面,并取一个与业务相关的名字是一个好的做法。特别是当这个操作的代码重复出现在多个地方的时候,更有这个必要。

function updateUserHometown(province, city){
jQuery("#homeTown").html(provinece + "," + city);
}

[size=large][b]在JS对象中关联element[/b][/size]
[size=medium]一、直接关联还是通过查找引用[/size]

var testDiv = document.createElement("div");
testDiv.id = "testDiv";
document.body.appendChild(testDiv);
document.body.removeChild(testDiv);
alert(testDiv.nodeName); //DIV
alert(testDiv.paretNode); //undefined
delete testDiv; 此时testDiv才被清除

通过以上代码可以看出,当你直接关联到元素时,就有可能造成元素被移除后,其对映DOM对象还在。基于这个原因,要小心因此可能造成内存泄露。
而通过ID或者其他选择器则不会有这个问题。但每次取不是很麻烦费时吗?其实你只要保证关联到元素的变量会被释放,还是应该直接引用的

function updateView(){
var con = document.getElementById("container");
//code ...
}
因为con是一个局部变量,所以它在函数调用后被释放了。
(function(){
var con = document.getElementById("container");
updateView = function(){
alert(con);
}
})()
document.getElementById("container").parentNode.removeChild(document.getElementById("container"));
updateView(); //con还是存在。

[size=medium]二、避免引用散布到代码中[/size]
将元素CSS选择表达式散布的代码中,维护起来确实很麻烦,也使得我们的代码和页面结合得更加紧密,这是我们不希望看到的,考虑以下办法避免:

//方法一:通过参数传递元素选择表达式
function submitForm(){
document.getElementById("userInfoForm").submit();
}
function submitForm(formId){
document.getElementById(formId).submit();
}
//<a href="javascript:submitForm("userInfoForm")" >提交</a>

//方法二:通过get方法把变化集中到一处
function getMainGrid(){
return document.getElementById("mainGrid");
}

//方法三:尽量把引用放到一处,然后通过传递参数引用元素。
function initSidebar(){
var s1 = document.getElementById("s1");
var s2 = document.getElementById("s2");
new Sidebar(s1,"news")
new SIdebar(s2, "videos");
}

[size=medium]三、考虑关联element被其他业务逻辑删除的情况[/size]
在实际项目中,我做了一个文字滚动,做好之后,我发现滚动部分在多种情况下会被替换掉,而文字滚动我是通过setInterval来做的,虽然滚动动画的类里有停止滚动的接口,但由于刷新页面的逻辑不是集中于一个函数内,并且局部刷新页面的动作很大可能还会在未来的代码里面出现,因此我考虑还是由滚动类自己监测容器元素是否存在,由此来决定是否停止滚动,并释放自己。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值