`
bellstar
  • 浏览: 148147 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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

阅读更多

目录
(一)有关框架
(二)文件组织与代码组织
(三)JS与FLASH交互
(四)ajax局部刷新与RPC
  (五)获取设置元素样式与监听元素事件
  (六)页面元素的创建调整与关联
(七)浏览器兼容性问题
(八)WEB软件的前端架构实践

文章中的代码只为表达文章意义,非真正能执行的代码。
动态创建、调整element
一、你考虑清楚要直接操作element吗?

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

即使有以上考虑,有以下情形我还是选择直接操作element
      
  • 如果业务逻辑足够简单,HTML结构也相对简单。比如更新用户名后更新页面上其他地方的用户名。
  • 如果HTML比较复杂,但数据或者状态变化后,只需要变化一个地方。
  • 在结构稳定的UI widget中,比如对话框。(以之相反的是grid,我会选择使用模板)
  • HTML中输入控件比较多 ,大部分时候需要保持控件的当前值和光标聚焦位置
  •     

另外,刷新整个部分与只更新变化的地方,用户体验会更好,不会闪的那么厉害,而且要做动画表示这个变化也方便。
二、应用模板进行局部更新
应用整体模板
        应用模板编程的优势及示例,我就不再重复。再次请大家参见金大为的雏凤清音 -- 面向数据的前端编程方法

应用局部模板
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))
      });
     }
}

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

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


    //某种状态变化后,都调用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");
           }
       })
   }

三、应用观察者模式应对多处界面变化
        示例:当用户变更它的个人信息时,更新见面中显示用户个人信息的地方。用户的个人信息可能在多个页面的不同位置显示。
//用户类
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");

三、给元素操作取一个名字
给逻辑相关的元素操作封装在一个方法里面,并取一个与业务相关的名字是一个好的做法。特别是当这个操作的代码重复出现在多个地方的时候,更有这个必要。
function updateUserHometown(province, city){
   jQuery("#homeTown").html(provinece + "," + city);
}

在JS对象中关联element
一、直接关联还是通过查找引用
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还是存在。

二、避免引用散布到代码中
将元素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");
}

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

相关推荐

    jQuery详细教程

    如果您的网站包含许多页面,并且您希望您的 jQuery 函数易于维护,那么请把您的 jQuery 函数放到独立的 .js 文件中。 当我们在教程中演示 jQuery 时,会将函数直接添加到 &lt;head&gt; 部分中。不过,把它们放到一个单独的...

    ASP.NET4高级程序设计第4版 带目录PDF 分卷压缩包 part1

    2.4.1 代码隐藏文件如何与页面连接 2.4.2 控件标签如何与页面变量连接 2.4.3 事件如何与事件处理程序连接 2.5 Web项目 2.5.1 基于项目的开发 2.5.2 创建Web项目 2.5.3 迁移旧版Visual Studio创建的网站...

    ASP.NET4高级程序设计(第4版) 3/3

    2.4.1 代码隐藏文件如何与页面连接 44 2.4.2 控件标签如何与页面变量连接 44 2.4.3 事件如何与事件处理程序连接 46 2.5 Web项目 47 2.5.1 基于项目的开发 47 2.5.2 创建Web项目 48 2.5.3 迁移旧版Visual...

    Colorinator在线颜色编辑器源码.rar

    KnockoutJS 提供两个重要的元素使用MVVM模式: • JavaScript 类observable 和observableArray,用于监视ViewModel 变量的变动。 • 当页面中变量改变时,和observables 关联的HTML 扩展标记会自动更新数据。HTML...

    html入门到放弃笔记

    9、行内元素 与 块级元素 按照元素们的表现形式来分类,分为 行内元素 和 块级元素1、行内元素 在一行内允许显示多个元素的,称为 "行内元素" span,i,b,s,u,sup,sub 作用:包裹文本,并处理文本的表现形式 2、...

    Responsive-Tabs:用于创建响应式标签的jQuery脚本。 控件的行为类似于指定屏幕宽度(由关联的css文件中的可编辑媒体查询定义)上方的常规选项卡,并且行为于该宽度以下的屏幕上的手风琴

    1.0初始版本1.1功能的命名空间1.2提高代码的效率和可读性,尤其是围绕元素创建和属性设置1.3更有效的选择器,更清晰的变量命名1.4删除了在标记内设置初始活动选项卡的必要性1.5消除了将标题和面板类手动添加到标记中...

    Tcl_TK编程权威指南pdf

    使用cgi创建动态页面 guestbook.cgi脚本程序 定义表单以及处理表单数据 cgi.tcl软件包 接下去的几步 第4章 tcl中的字符串处理 string命令 append命令 format命令 scan命令 binary命令 相关章节 第5章...

    PHP开发实战1200例(第1卷).(清华出版.潘凯华.刘中华).part1

    实例038 动态输出JavaScript代码 69 实例039 当数字遇到了字符串 70 实例040 PHP程序员的基础——变量的应用 72 实例041 打印系统环境变量信息print_r($_ENV) 73 实例042 使用可变变量输出“I Like PHP!” 73 实例...

    PHP开发实战1200例(第1卷).(清华出版.潘凯华.刘中华).part2

    实例038 动态输出JavaScript代码 69 实例039 当数字遇到了字符串 70 实例040 PHP程序员的基础——变量的应用 72 实例041 打印系统环境变量信息print_r($_ENV) 73 实例042 使用可变变量输出“I Like PHP!” 73 实例...

    TextSearch:谷歌场所 API (https

    文本搜索Google Places API ( ) 创建一个小型的单页 Web 应用程序,显示从该 API 检索到的所有地点的列表,并允许用户... 如果存在与给定过滤器相关联的服务,则在选择过滤器时可以触发相关服务并填充结果。 #Resize o

    DotNetTextBox所见即所得编辑器控件 v3.3.1

    &lt;br&gt;2007/6/25 Version 3.1.5 beta &lt;br&gt;Updates: 1) 修正在VS2005设计模式中无法动态调整控件高度的BUG。 2) 修正height属性无效的BUG。 3) 修正CodeHighlighter外挂插件调用图片位置指向不正确...

    SEO魔法书-最全的优化教程

    关于这本书..............................................................................................................................................1 第一章 搜索引擎基础.............................

    搜索引擎优化魔法书

    目录 关于这本书.............................................................................................................................................1 第一章 搜索引擎基础..........................

Global site tag (gtag.js) - Google Analytics