从这一节开始分析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').onclick=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>