从这一节开始分析ExtJs对元素(Element)的封装与扩展,首先看Ext.lib.Dom
类Ext.lib.Dom
该类源代码对应的文件为ext-base-dom.js。Ext.lib.Dom主要实现了对document中元素部分方法的兼容性实现。在前面事件(Ext.EventManager)的讲解中已经用到了该类。
该类的实现采用了匿名闭包自执行函数实现,执行完该函数,把封装好的对象赋给了对象Ext.lib.Dom,实现了对该类的封装,这种实现的好处把不需要暴露的变量封装到闭包中,同时这些变量的作用域随Ext.lib.Dom自始自终,而不是调用后立马回收。援引别人写的一段话“严格意义上讲任何一个function在执行时就构成一个闭包,闭包的实质是作用域。普通的function,内部声明的变量在执行完会全被回收掉,这时闭包也被回收了,所以这种闭包对我们没有用处,他只是个作用域。如果一个function的局部变量在执行时被生命周期更长的变量引用,那么这个function执行完时就不能回收掉这个局部变量,这个局部变量的生命周期超出了他的作用域,这时的作用域也不能被回收掉,就形成了典型的闭包。闭包的作用就是使局部变量拥有更大的生命周期,甚至全局的生命周期,这使得闭包可以保存function执行时的状态,特别是那种function中又返回一个function时,被返回的function就可以访问父function执行时的所有局部变量(典型的带状态函数)。因为JS中回收一个变量不是看作用域是否结束,而是看引用数。”
先看Ext.lib.Dom一些变量的声明
var doc = document, isCSS1 = doc.compatMode == "CSS1Compat", MAX = Math.max, ROUND = Math.round, PARSEINT = parseInt;
这些变量的声明一是全局管理,二是可以提高js文件代码的压缩率,http://yui.2clics.net/ 可以在线压缩js文件。
isCSS1用来判断浏览器的文档模式(doctype),true为标准模式,false为怪异模式。在Ext类中用Ext.isStrict来判断,这里采用isCSS1,实现的功能是一样的。
下面看Ext.lib.Dom中方法的定义,首先看方法isAncestor
isAncestor : function(p, c) { var ret = false; p = Ext.getDom(p); c = Ext.getDom(c); if (p && c) { if (p.contains) { return p.contains(c); } else if (p.compareDocumentPosition) { return !!(p.compareDocumentPosition(c) & 16); } else { while (c = c.parentNode) { ret = c == p || ret; } } } return ret; },
该方法的功能为判断c是否是p的子元素,并且实现了各浏览器的兼容contains和compareDocumentPosition,关于这两个方法的讲解,参照 javascript contains和compareDocumentPosition 方法来确定是否HTML节点 。在ext-base-event.js Ext.lib.EventObject 中有类似实现elContains。
接下来是返回文档和页面视图高宽,代码如下:
/** * 如果full为真返回页面的实际宽度,否则返回可视宽度 * 实际宽度指整个页面的宽度,包括滚动条看不到的区域,而可视宽度指当前可以看到的宽度 */ getViewWidth : function(full) { return full ? this.getDocumentWidth() : this.getViewportWidth(); }, /** * 如果full为真返回页面的实际高度,否则返回可视高度 */ getViewHeight : function(full) { return full ? this.getDocumentHeight() : this.getViewportHeight(); }, /** * 得到页面的实际高度 */ getDocumentHeight: function() { return MAX(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, this.getViewportHeight()); }, /** * 得到页面的实际宽度 */ getDocumentWidth: function() { return MAX(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, this.getViewportWidth()); }, /** * 得到页面的可视高度 */ getViewportHeight: function(){ return Ext.isIE ? (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) : self.innerHeight; }, /** * 得到页面的可视宽度 */ getViewportWidth : function() { return !Ext.isStrict && !Ext.isOpera ? doc.body.clientWidth : Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth; },
这一组方法可以分为两类,返回页面的实际高宽度和返回页面的可视宽度,当出现滚动条时,实际高宽度会大于可视高宽度,方法处理了不同浏览器之间的兼容。关于效果请看以下例子
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Ext中getDocumentWidth与getViewportWidth的区别</title>
<link rel="stylesheet" type="text/css"
href="../ext-3.3.1/resources/css/ext-all.css" />
<script type="text/javascript"
src="../ext-3.3.1/adapter/ext/ext-base-debug.js"></script>
<script type="text/javascript"
src="../ext-3.3.1/ext-all-debug-w-comments.js"></script>
<script type="text/javascript"
src="../ext-3.3.1/src/locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="../ext-3.3.1/src/debug.js"></script>
<script type="text/javascript">
Ext.onReady(function() {
Ext.BLANK_IMAGE_URL = '../ext-3.3.1/resources/images/default/s.gif';
Ext.QuickTips.init();
var dom = Ext.lib.Dom;
alert("页面的实际宽度:"+dom.getDocumentWidth());
alert("页面的可视宽度:"+dom.getViewportWidth());
});
</script>
</head>
<body>
<div style="width:2000px;">
Ext中getDocumentWidth与getViewportWidth的区别Ext中getDocumentWidth与getViewportWidth的区别Ext中getDocumentWidth与getViewportWidth的区别
Ext中getDocumentWidth与getViewportWidth的区别
</div>
</body>
</html>
接下来的方法是获取元素相对于文档的位置
/** * 得到给定元素的left */ getX : function(el) { return this.getXY(el)[0]; }, /** * 得到给定元素的[top,left] */ getXY : function(el) { var p, pe, b, bt, bl, dbd, x = 0, y = 0, scroll, hasAbsolute, bd = (doc.body || doc.documentElement), ret = [0,0]; el = Ext.getDom(el); if(el != bd){ if (el.getBoundingClientRect) { b = el.getBoundingClientRect(); scroll = fly(document).getScroll(); ret = [ROUND(b.left + scroll.left), ROUND(b.top + scroll.top)]; } else {//不支持getBoundingClientRect,实现起来比较啰嗦,得循环获得元素在页面中的绝对位置 p = el; hasAbsolute = fly(el).isStyle("position", "absolute"); while (p) { pe = fly(p); x += p.offsetLeft; y += p.offsetTop; hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute"); if (Ext.isGecko) { y += bt = PARSEINT(pe.getStyle("borderTopWidth"), 10) || 0; x += bl = PARSEINT(pe.getStyle("borderLeftWidth"), 10) || 0; if (p != el && !pe.isStyle('overflow','visible')) { x += bl; y += bt; } } p = p.offsetParent; } if (Ext.isSafari && hasAbsolute) { x -= bd.offsetLeft; y -= bd.offsetTop; } if (Ext.isGecko && !hasAbsolute) { dbd = fly(bd); x += PARSEINT(dbd.getStyle("borderLeftWidth"), 10) || 0; y += PARSEINT(dbd.getStyle("borderTopWidth"), 10) || 0; } p = el.parentNode; while (p && p != bd) { if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) { x -= p.scrollLeft; y -= p.scrollTop; } p = p.parentNode; } ret = [x,y]; } } return ret; },
这里需要说明一下关于方法el.getBoundingClientRect,该方法获得页面中某个元素的左、上、右和下分别相对浏览器视窗的位置。该方法已经不再是IE Only了,FF3.0+和Opera9.5+已经支持了该方法,可以说在获得页面元素位置上效率能有很大的提高,在以前版本的Opera和Firefox中必须通过循环来获得元素在页面中的绝对位置。见代码中不支持的实现。关于该方法的运行效果看以下例子
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>方法getBoundingClientRect Demo</title>
</head>
<body style="width: 2000px; height: 1000px;">
<div id="demo"
style="position: absolute; left: 518px; right: 100px; width: 500px; height: 500px; background: #CC0000; top: 114px;">
Demo为了方便就直接用绝对定位的元素
</div>
</body>
</html>
<script>
document.getElementById('demo').οnclick=function (){
if (document.documentElement.getBoundingClientRect) {
alert("left:"+this.getBoundingClientRect().left)
alert("top:"+this.getBoundingClientRect().top)
alert("right:"+this.getBoundingClientRect().right)
alert("bottom:"+this.getBoundingClientRect().bottom)
var X= this.getBoundingClientRect().left+document.documentElement.scrollLeft;
var Y = this.getBoundingClientRect().top+document.documentElement.scrollTop;
alert("Demo的位置是X:"+X+";Y:"+Y)
}
}
</script>
接下来是设置元素的x和y坐标,这三个方法依赖于Ext.fly,Ext.fly实际上是Ext.Element.fly和el.translatePoints(xy)(修改元素的left属性值和top属性),接下来会讲解道这两个方法。