读Ext之十一(通过innerHTML创建元素)
innerHTML 这个由 IE 引入的属性成了事实标准,各浏览器均支持。尽管html4中没有承认它,但html5已经正式将其纳入 。
我们知道任何一个库都少不了DOM操作,因为用JS操作DOM(早期微软称DHTML)是日常开发中最基本的工作之一。
这篇主要讲述Ext.DomHelper中的 createHtml 函数。首先Ext.DomHelper为一个单例对象。使用其时可沿用Ext库的习惯使用别名dh
1 | var dh = Ext.DomHelper; // 使用dh别名 |
dh有以下方法:
markup
applyStyles
insertHtml
insertBefore
insertAfter
insertFirst
append
overwrite
createHtml
dh的createHtml方法就是整个闭包中私有的createHtml。
dh的markup方法内部调用的就是整个闭包中私有的createHtml。
dh的overwrite方法内部也用到了createHtml。
此外私有的doInsert函数内部用用到了createHtml,而dh的insertBefore、insertAfter、insertFirst、append方法用到了doInsert。
因此可以看到私有的createHtml函数是dh中处在底层的,最重要的函数 。它们之间的关系如下图
createHtml 的定义如下
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 | function createHtml(o){ var b = '' , attr, val, key, keyVal, cn; if (Ext.isString(o)){ b = o; } else if (Ext.isArray(o)) { for ( var i=0; i < o.length; i++) { if (o[i]) { b += createHtml(o[i]); } }; } else { b += '<' + (o.tag = o.tag || 'div' ); Ext.iterate(o, function (attr, val){ if (!/tag|children|cn|html$/i.test(attr)){ if (Ext.isObject(val)) { b += ' ' + attr + '="' ; Ext.iterate(val, function (key, keyVal){ b += key + ':' + keyVal + ';' ; }); b += '"' ; } else { b += ' ' + ({cls : 'class' , htmlFor : 'for' }[attr] || attr) + '="' + val + '"' ; } } }); // Now either just close the tag or try to add children and close the tag. if (emptyTags.test(o.tag)) { b += '/>' ; } else { b += '>' ; if ((cn = o.children || o.cn)) { b += createHtml(cn); } else if (o.html){ b += o.html; } b += '</' + o.tag + '>' ; } } return b; } |
虽然代码较多,但接口却很简单:传入了一个参数o,返回了一个字符串b。
createHtml 内部有三个分支
分支1 ,参数o为字符串时直接返回
分支2 ,参数o为数组时递归调用自身.Ext.isArray用来判断所传参数是否为一个数组类型,该方法在 读Ext之二(实用方法) 中提到。
分支3 ,参数o为对象时(通常使用最多的情况)
分之一的情况很简单
1 2 | var str = createHtml( '<div>test</div>' ); alert(str); // "<div>test</div>" |
分支二的情况
1 2 | var str = createHtml([ '<div>test</div>' , '<p>pp</p>' ]); alert(str); // "<div>test</div><p>pp</p>" |
分支三的情况
1 2 3 4 5 6 | var obj = { tag: 'ul' , children:[{tag: 'li' ,html: 'li 1' },{tag: 'li' ,html: 'li 2' }] }; var str = createHtml(obj); alert(str); // "<ul><li>li 1</li><li>li 2</li></ul>" |
分支三的详细情况如下
1 | b += '<' + (o.tag = o.tag || 'div' ); |
b为空字符串,该语句执行完为:"<tag",如果没有所传对象没有tag属性,默认创建div即"<div"
接下来是为根元素添加属性,
1 2 3 4 5 6 7 8 9 10 11 12 13 | Ext.iterate(o, function (attr, val){ if (!/tag|children|cn|html$/i.test(attr)){ if (Ext.isObject(val)) { b += ' ' + attr + '="' ; Ext.iterate(val, function (key, keyVal){ b += key + ':' + keyVal + ';' ; }); b += '"' ; } else { b += ' ' + ({cls : 'class' , htmlFor : 'for' }[attr] || attr) + '="' + val + '"' ; } } }); |
Ext.iterate方法是一个通用迭代器,可以迭代数组,也可以是对象。在 读Ext之二 提到。即所传对象o的属性不为tag、children、cn、html时给该元素添加html属性
1 2 3 4 5 6 7 8 | var obj = { tag: 'input' , name: 'uname' , cls: 'myClass' , id: 'myId' }; var str = createHtml(obj); // 创建input元素,添加name、class及id属性 alert(str); // <input name="uname" class="myClass" id="myId"/> |
对html元素的class,for属性做了特殊处理,因为js中class是保留字,for是关键字。
此外有些属性值是复合值组成,如style
1 2 3 4 5 6 | var obj = { tag: 'p' , style: {width: '100px' ,height: '100px' ,background: 'red' } }; var str = createHtml(obj); alert(str); // <p style="width:100px;height:100px;background:red;"></p> |
接下来
1 2 3 | if (emptyTags.test(o.tag)) { b += '/>' ; } |
emptyTags 是一个正则,这句是对非闭合标签的处理,非闭合标签如br、input、img等。
所有的非闭合标签,都会以"/>"结束,这里遵循xml的习惯。对于非闭合标签不会有html内容。
接下来
1 2 3 4 5 6 7 8 9 | else { b += '>' ; if ((cn = o.children || o.cn)) { b += createHtml(cn); } else if (o.html){ b += o.html; } b += '</' + o.tag + '>' ; } |
对于闭合标签,先完成首标签闭合。拿div示例,由先"<div"变成了"<div>"。
接下来如果有children或cn属性,则添加子元素,是一个递归调用,即子元素如果有属性,子元素仍然会继续递归添加。
最后是结束标签"</div>"
这就是这个createHtml函数了。该函数实现的很紧凑,巧妙。
createHtml最终返回的是html片段,该片段会通过innerHTML添加到文档中去。
上面的关系图已经可以看到:
markup方法直接调用了createHTML
1 2 3 | markup : function (o){ return createHtml(o); }, |
个人认为这个方法和markup方法重复了。
doInsert方法也直接调用了createHtml
1 2 3 4 | function doInsert(el, o, returnElement, pos, sibling, append){ var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o)); return returnElement ? Ext.get(newNode, true ) : newNode; } |
好了,下一篇会接着看DomHelper的其它方法。
【推荐】国内首个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-05-02 IE8+中XMLHttpRequest支持timeout属性及ontimeout事件
2011-05-02 JS Queue LazyLoad 之二