元素的操作Ext.DomHelper
该类中的函数主要来简化创建DOM元素操作,采用单例模式实现。主要提供了createHtml和createDom函数。createHtml函数根据配置来创建HTML标签,最终通过innerHTML插入到文档中,二createDom函数根据配置来创建DOM对象,最终通过appendChild或insertBefore函数插入到文档中。下面先看方法createHtml
- function createHtml(o){
- var b = '',
- attr,
- val,
- key,
- cn;
- if(typeof o == "string"){
- 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');
- for (attr in o) {
- val = o[attr];
- if(!confRe.test(attr)){
- if (typeof val == "object") {
- b += ' ' + attr + '="';
- for (key in val) {
- b += key + ':' + val[key] + ';';
- };
- 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;
- }
该方法即是内部私有方法,又是类的静态方法(createHtml : createHtml,利用这句把私有方法赋给类的静态方法),可以通过方法 markup返回元素的装饰(markup)
- markup : function(o){
- return createHtml(o);
- },
下面详细的讲解一些该方法的内部实现,执行该方法会根据传入参数类型的不同分为三种情况
- 字符串:直接返回
- 数组:循环递归调用
- 对象:处理后转换为html字符串再返回
前两种情况很简单,以下是其简单例子
- var str = Ext.DomHelper.createHtml('<div>demo1</div>');
- alert(str); // <div>demo1</div>
- var str = Ext.DomHelper.createHtml(['<div>demo1</div>','<div>demo2</div>']);
- alert(str); // <div>demo1</div><div>demo2</div>
这里主要看第三种情况,首先看以下例子
- var spec = {
- id: 'my-ul',
- tag: 'ul',
- cls: 'my-list',
- style : {width:'20px',height:'30px'},
- // append children after creating
- children: [ // may also specify 'cn' instead of 'children'
- {tag: 'li', id: 'item0', html: 'List Item 0'},
- {tag: 'li', id: 'item1', html: 'List Item 1'},
- {tag: 'li', id: 'item2', html: 'List Item 2'}
- ]
- };
- var str = Ext.DomHelper.createHtml(spec);
- alert(str); // <ul id="my-ul" class="my-list" style="width:20px;height:30px;">
- //<li id="item0">List Item 0</li><li id="item1">List Item 1</li><li id="item2">List Item 2</li></ul>
实现代码中
b += '<' + (o.tag = o.tag || 'div');
首先生成父节点,默认为div
if(!confRe.test(attr)){
排除掉这些标签tag|children|cn|html
if (typeof val == "object") {
b += ' ' + attr + '="';
for (key in val) {
b += key + ':' + val[key] + ';';
};
b += '"';
}else{
b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
}
上面的语句分为两种情况,如果val是对象时,比如style : {width:'20px',height:'30px'},会遍历该对象转化为字符串;如果不是对象时,会直接与b连接,注意对html元素的class,for属性做了特殊处理,因为js中class是保留字,for是关键字。
if (emptyTags.test(o.tag)) {
b += '/>';
}
对于html中单标签(即没有内容的标签),直接结束,比如br、hr
else {
b += '>';
if ((cn = o.children || o.cn)) {
b += createHtml(cn);
} else if(o.html){
b += o.html;
}
b += '</' + o.tag + '>';
}
如果是children或cn则需要递归遍历孩子节点,如果是html则赋值。
该函数中特殊情况利用正则表达式来实现,这种实现可参考。
createHtml最终返回的是html片段,该片段会通过innerHTML添加到文档中去。
下面看方法
- applyStyles : function(el, styles){
- if (styles) {
- var matches;
- el = Ext.fly(el);
- if (typeof styles == "function") {
- styles = styles.call();
- }
- if (typeof styles == "string") {
- cssRe.lastIndex = 0;
- while ((matches = cssRe.exec(styles))) {
- el.setStyle(matches[1], matches[2]);
- }
- } else if (typeof styles == "object") {
- el.setStyle(styles);
- }
- }
- },
该方法的功能为把指定的样式应用到元素,styles 可以是表示样式的特定格式字符串,如“width:100px”,也可以是对象的形式如{width:"100px"},还可以是能返回这些格式的函数。
下面看元素插入方法,该类提供了以下插入方法
- insertBefore : function(el, o, returnElement) 在指定元素之前插入新元素
- insertAfter : function(el, o, returnElement) 在指定元素之后插入新元素
- insertFirst : function(el, o, returnElement) 在指定元素内部首位置插入新元素
- append : function(el, o, returnElement) 在指定元素内部尾部插入新元素
- overwrite : function(el, o, returnElement) 创建新的Dom元素并覆盖el的内容
前四个方法是调用doInsert插入DOM元素,最后一个方法是插入HTML片段。而doInsert调用了关键方法insertHtml,下面看该代码的实现
- insertHtml : function(where, el, html){
- var hash = {},
- hashVal,
- setStart,
- range,
- frag,
- rangeEl,
- rs;
- where = where.toLowerCase();
- // add these here because they are used in both branches of the condition.
- hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
- hash[afterend] = ['AfterEnd', 'nextSibling'];
- if (el.insertAdjacentHTML) {
- if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
- return rs;
- }
- // add these two to the hash.
- hash[afterbegin] = ['AfterBegin', 'firstChild'];
- hash[beforeend] = ['BeforeEnd', 'lastChild'];
- if ((hashVal = hash[where])) {
- el.insertAdjacentHTML(hashVal[0], html);
- return el[hashVal[1]];
- }
- } else {
- range = el.ownerDocument.createRange();
- setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
- if (hash[where]) {
- range[setStart](el);
- frag = range.createContextualFragment(html);
- el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
- return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
- } else {
- rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
- if (el.firstChild) {
- range[setStart](el[rangeEl]);
- frag = range.createContextualFragment(html);
- if(where == afterbegin){
- el.insertBefore(frag, el.firstChild);
- }else{
- el.appendChild(frag);
- }
- } else {
- el.innerHTML = html;
- }
- return el[rangeEl];
- }
- }
- throw 'Illegal insertion point -> "' + where + '"';
- },
该方法内部分别实现了支持el.insertAdjacentHTML的浏览器和不支持的浏览器,支持el.insertAdjacentHTML的浏览器(IE/Chrome/Safari/Opera)使用el.insertAdjacentHTML。不支持el.insertAdjacentHTML的浏览器(Firefox)使用range.createContextualFragment+el.insertBefore/el.appendChild。对于IE中不支持Table的innerHTML写操作,该方法进行了特殊处理,使用div,把table添加到div中,并返回table,间接实现。下面代码在IE中会报错,连最新的IE9也不支持。
- var table = document.createElement('table');
- table.innerHTML = '<tr><td>Test table innerHTML</td></tr>';
- document.body.appendChild(table);
再看以下例子
- <table id='insertTable' border='1'></table>
- var ieTable = {
- tag: 'tbody',
- children: [
- {tag: 'tr',
- children: [
- {tag: 'td',html:'Test table innerHTML in IE'},
- {tag: 'td',html:'Nasty code for IE is broken table implementation'}
- ]
- }
- ]
- };
- var table1 = Ext.get('insertTable');
- Ext.DomHelper.append(table1,ieTable);
需要说明一下,在文件DomHelper-more.js中进行了扩展,实际上是对DOM元素的创建采用方法createDom,部分实现调用了createHtml中的方法,也可以利用参数useDom来限制使用createDom。createDom的实现与createHtml类似。