读Ext之五(Dom的低级封装)
上篇看了ext-base-event.js(Ext.lib.Event),这篇看ext-base-dom.js(Ext.lib.Dom)。
后续EventManager.js(Ext.EventManager)其中会用到该篇的Ext.lib.Dom。
整体浏览ext-base-dom.js可以看到还是一个匿名函数执行,与上一篇不同的是这次并非返回包装好的对象pub,而是直接将对象赋值给Ext.lib.Dom。
1 2 3 4 5 6 7 8 | ( function (){ var doc = document, ...; Ext.lib.Dom = { ... }; })(); |
一些变量声明,
1 2 3 4 5 | var doc = document, isCSS1 = doc.compatMode == "CSS1Compat" , MAX = Math.max, ROUND = Math.round, PARSEINT = parseInt; |
doc、Max、ROUND、PARSEINT为简写。好处在上一篇已经提到,clue 还提到了这样写提高了压缩率。即document,Math.max等不会被压缩工具进行名称替换,doc,MAX等则会,可用 YUI Compressor 测试下。
isCSS1用来判断浏览器的文档模式(doctype),为true是标准模式,false是怪异模式。第一篇
提到了Ext.js中有个Ext.isStrict,和这里的isCSS1是一样的。
Ext作者没有使用Ext.isStrict而是新增了一个变量isCSS1个人猜测原因同doc,MAX,亦或是为了保持此处代码一致性。
接下来是Ext.lib.Dom对象的定义,先看第一个方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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(child)是否是p(parent)的子节点。这里有contains和compareDocumentPosition各浏览器 兼容列表 。这个方法与ext-base-event.js的私有方法elContains(上一篇已提到)作用类似,不知为何Ext作者要冗余。
接下来是几个获取视图,文档,视窗宽高的方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | getViewWidth : function (full) { return full ? this .getDocumentWidth() : this .getViewportWidth(); }, 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; }, |
这里仅讲述宽。
getViewportWidth 获取视口的宽。即怪异模式且非Opera使用doc.body.clientWidth获取,IE使用doc.documentElement.clientWidth获取,其它浏览器使用self.innerWidth获取。
getDocumentWidth 获取文档的宽。我们知道如果文档中出现水平滚动条,那么文档的宽将大于视口。因此比较scroll宽度和文档宽度,取其中较大的。
这里仍仍然需要区分浏览器的文档模式,怪异模式使用doc.body.scrollWidth,标准模式使用doc.documentElement.scrollWidth获取。
getViewWidth 是对以上两个方法的封装,full为true则取整个文档宽(含滚动),否则只取文档可视区域宽。
接下来是获取元素相当于文档的位置方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | getY : function (el) { return this .getXY(el)[1]; }, getX : function (el) { return this .getXY(el)[0]; }, 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 { 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 }, |
核心是getXY方法,getX和getY都是调用getXY。获取html元素在页面中(窗口客户区域)的位置(top,left)不是件容易的事。
该方法实现用到了Ext.Element相关方法,后续阅读。
接下来是设置元素位置的方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | setXY : function (el, xy) { (el = Ext.fly(el, '_setXY' )).position(); var pts = el.translatePoints(xy), style = el.dom.style, pos; for (pos in pts) { if (!isNaN(pts[pos])) style[pos] = pts[pos] + "px" } }, setX : function (el, x) { this .setXY(el, [x, false ]); }, setY : function (el, y) { this .setXY(el, [ false , y]); } |
核心是setXY,setX和setY都是调用setXY。setXY也依赖于Ext.fly,Ext.fly实际上是Ext.Element.fly。后续阅读。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
2011-04-27 新API解析JSON-Ajax之七
2011-04-27 创建xhr时异常处理-Ajax之六