Firefox outerHTML实现代码


Posted in Javascript onJune 04, 2009

减少DOM数可以加快浏览器的在解析页面过程中DOM Tree和render tree的构建,从而提高页面性能。为此我们可以把页面中那些首屏渲染不可见的部分HTML暂存在TextArea中,等完成渲染后再处理这部分HTML来达到这个目的。 要把TextArea 中暂存的HTML内容添加到页面中,使用元素的outerHTML属性是最简单方便的了,不过在DOM标准中并没有定义outerHTML,支持的浏览器有IE6+,safari, operal和 Chrome,经测试FF4.0- 中还不支持。所以我们就来实现一个可以跨浏览器的outerHTML。
outerHTML 就是获取或设置包含元素标签本身在内的html。下面是实现代码:

if(typeof HTMLElement !== "undefined" && !("outerHTML" in HTMLElement.prototype)) { 
//console.log("defined outerHTML"); 
HTMLElement.prototype.__defineSetter__("outerHTML",function(str){ 
var fragment = document.createDocumentFragment(); 
var div = document.createElement("div"); 
div.innerHTML = str; 
for(var i=0, n = div.childNodes.length; i<n; i++){ 
fragment.appendChild(div.childNodes[i]); 
} 
this.parentNode.replaceChild(fragment, this); 
}); 
// 
HTMLElement.prototype.__defineGetter__("outerHTML",function(){ 
var tag = this.tagName; 
var attributes = this.attributes; 
var attr = []; 
//for(var name in attributes){//遍历原型链上成员 
for(var i=0,n = attributes.length; i<n; i++){//n指定的属性个数 
if(attributes[i].specified){ 
attr.push(attributes[i].name + '="' + attributes[i].value + '"'); 
} 
} 
return ((!!this.innerHTML) ? 
'<' + tag + ' ' + attr.join(' ')+'>'+this.innerHTML+'</'+tag+'>' : 
'<' + tag + ' ' +attr.join(' ')+'/>'); 
}); 
}

代码说明:
1 代码中首先条件判断来监测浏览器是否支持outerHTML以避免覆盖浏览器原生的实现。
2 "__defineSetter__","__defineGetter__" 是firefox浏览器私有方面。分别定义当设置属性值和获取属性要执行的操作。
3 在"__defineSetter__" "outerHTML"中为了避免插入页面中元素过多导致频繁发生reflow影响性能。使用了文档碎片对象fragment来暂存需要插入页面中DOM元素。
4 在"__defineGetter__" "outerHTML" 中使用元素attributes属性来遍历给元素指定的属性。结合innerHTML返回了包含原属本身在内的html字符串。
测试代码:
<!DOCTYPE html> 
<html> 
<head> 
<meta charset="utf-8" /> 
<title>outerHTML</title> 
</head> 
<body> 
<div id="content" class="test"> 
<p>This is <strong>paragraph</strong> with a list following it</p> 
<ul> 
<li>Item 1</li> 
<li>Item 2</li> 
<li>Item 3</li> 
<li>Item 4</li> 
</ul> 
</div> 
<script> 
if(typeof HTMLElement !== "undefined" && !("outerHTML" in HTMLElement.prototype)) { 
console.log("defined outerHTML"); 
HTMLElement.prototype.__defineSetter__("outerHTML",function(str){ 
var fragment = document.createDocumentFragment(); 
var div = document.createElement("div"); 
div.innerHTML = str; 
for(var i=0, n = div.childNodes.length; i<n; i++){ 
fragment.appendChild(div.childNodes[i]); 
} 
this.parentNode.replaceChild(fragment, this); 
}); 
// 
HTMLElement.prototype.__defineGetter__("outerHTML",function(){ 
var tag = this.tagName; 
var attributes = this.attributes; 
var attr = []; 
//for(var name in attributes){//遍历原型链上成员 
for(var i=0,n = attributes.length; i<n; i++){//n指定的属性个数 
if(attributes[i].specified){ 
attr.push(attributes[i].name + '="' + attributes[i].value + '"'); 
} 
} 
return ((!!this.innerHTML) ? 
'<' + tag + ' ' + attr.join(' ')+'>'+this.innerHTML+'</'+tag+'>' : 
'<' + tag + ' ' +attr.join(' ')+'/>'); 
}); 
} 
var content = document.getElementById("content"); 
alert(content.outerHTML) 
</script> 
</body> 
</html>

假设要获取 <p id="outerID">sdfdsdfsd</p> 的 P的outerHTML
代码:
var _p = document.getElementById('outerID'); 
_P = _P.cloneNode(); 
var _DIV = document.createElement(); 
_DIV.appendChild(_P); 
alert(_DIV.innerHTML); 就是P的outerHTML;

firefox没有outerHTML用以下方法解决
/** 
* 兼容firefox的 outerHTML 使用以下代码后,firefox可以使用element.outerHTML 
**/ 
if(window.HTMLElement) { 
HTMLElement.prototype.__defineSetter__("outerHTML",function(sHTML){ 
var r=this.ownerDocument.createRange(); 
r.setStartBefore(this); 
var df=r.createContextualFragment(sHTML); 
this.parentNode.replaceChild(df,this); 
return sHTML; 
}); 
HTMLElement.prototype.__defineGetter__("outerHTML",function(){ 
var attr; 
var attrs=this.attributes; 
var str="<"+this.tagName.toLowerCase(); 
for(var i=0;i<attrs.length;i++){ 
attr=attrs[i]; 
if(attr.specified) 
str+=" "+attr.name+'="'+attr.value+'"'; 
} 
if(!this.canHaveChildren) 
return str+">"; 
return str+">"+this.innerHTML+"</"+this.tagName.toLowerCase()+">"; 
}); 
HTMLElement.prototype.__defineGetter__("canHaveChildren",function(){ 
switch(this.tagName.toLowerCase()){ 
case "area": 
case "base": 
case "basefont": 
case "col": 
case "frame": 
case "hr": 
case "img": 
case "br": 
case "input": 
case "isindex": 
case "link": 
case "meta": 
case "param": 
return false; 
} 
return true; 
}); 
}

测试有效.
关于insertAdjacentHTML兼容的解新决办法
//---在组件最后插入html代码 
function InsertHtm(op,code,isStart){ 
if(Dvbbs_IsIE5) 
op.insertAdjacentHTML(isStart ? "afterbegin" : "afterEnd",code); 
else{ 
var range=op.ownerDocument.createRange(); 
range.setStartBefore(op); 
var fragment = range.createContextualFragment(code); 
if(isStart) 
op.insertBefore(fragment,op.firstChild); 
else 
op.appendChild(fragment); 
} 
}

关于inner/outerHTML在NC6中的参考
DOM level 1 has no methods to allow for insertion of unparsed HTML into the document tree (as IE allows with insertAdjacentHTML or assignment to inner/outerHTML).NN6 (currently in beta as NN6PR3) know supports the .innerHTMLproperty of HTMLElements so that you can read or write the innerHTML of a page element like in IE4+.NN6 also provides a DOM level 2 compliant Range object to which a createContextualFragment('html source string')was added to spare DOM scripters the task of parsing html and creating DOM elements.You create a Range with var range = document.createRange();Then you should set its start point to the element where you want to insert the html for instance var someElement = document.getElementById('elementID'); range.setStartAfter(someElement);Then you create a document fragment from the html source to insert for example var docFrag = range.createContextualFragment('<P>Kibology for all.</P>');and insert it with DOM methods someElement.appendChild(docFrag);The Netscape JavaScript 1.5 version even provides so called setters for properties which together with the ability to prototype the DOM elements allows to emulate setting of outerHMTL for NN6:<SCRIPT LANGUAGE="JavaScript1.5">if (navigator.appName == 'Netscape') { HTMLElement.prototype.outerHTML setter = function (html) { this.outerHTMLInput = html; var range = this.ownerDocument.createRange(); range.setStartBefore(this); var docFrag = range.createContextualFragment(html); this.parentNode.replaceChild(docFrag, this); }}</SCRIPT> If you insert that script block you can then write cross browser code assigning to .innerHTML .outerHTMLfor instance document.body.innerHTML = '<P>Scriptology for all</P>';which works with both IE4/5 and NN6.The following provides getter functions for .outerHTMLto allow to read those properties in NN6 in a IE4/5 compatible way. Note that while the scheme of traversing the document tree should point you in the right direction the code example might not satisfy your needs as there are subtle difficulties when trying to reproduce the html source from the document tree. See for yourself whether you like the result and improve it as needed to cover other exceptions than those handled (for the empty elements and the textarea element).<HTML><HEAD><STYLE></STYLE><SCRIPT LANGUAGE="JavaScript1.5">var emptyElements = { HR: true, BR: true, IMG: true, INPUT: true};var specialElements = { TEXTAREA: true};HTMLElement.prototype.outerHTML getter = function () { return getOuterHTML (this);}function getOuterHTML (node) { var html = ''; switch (node.nodeType) { case Node.ELEMENT_NODE: html += '<'; html += node.nodeName; if (!specialElements[node.nodeName]) { for (var a = 0; a < node.attributes.length; a++) html += ' ' + node.attributes[a].nodeName.toUpperCase() + '="' + node.attributes[a].nodeValue + '"'; html += '>'; if (!emptyElements[node.nodeName]) { html += node.innerHTML; html += '<\/' + node.nodeName + '>'; } } else switch (node.nodeName) { case 'TEXTAREA': for (var a = 0; a < node.attributes.length; a++) if (node.attributes[a].nodeName.toLowerCase() != 'value') html += ' ' + node.attributes[a].nodeName.toUpperCase() + '="' + node.attributes[a].nodeValue + '"'; else var content = node.attributes[a].nodeValue; html += '>'; html += content; html += '<\/' + node.nodeName + '>'; break; } break; case Node.TEXT_NODE: html += node.nodeValue; break; case Node.COMMENT_NODE: html += '<!' + '--' + node.nodeValue + '--' + '>'; break; } return html;}</SCRIPT></HEAD><BODY><A HREF="javascript: alert(document.documentElement.outerHTML); void 0">show document.documentElement.outerHTML</A>|<A HREF="javascript: alert(document.body.outerHTML); void 0">show document.body.outerHTML</A>|<A HREF="javascript: alert(document.documentElement.innerHTML); void 0">show document.documentElement.innerHTML</A>|<A HREF="javascript: alert(document.body.innerHTML); void 0">show document.body.innerHTML</A><FORM NAME="formName"><TEXTAREA NAME="aTextArea" ROWS="5" COLS="20">JavaScript.FAQTs.comKibology for all.</TEXTAREA></FORM><DIV><P>JavaScript.FAQTs.com</P><BLOCKQUOTE>Kibology for all.<BR>All for Kibology.</BLOCKQUOTE></DIV></BODY></HTML>Note that the getter/setter feature is experimental and its syntax is subject to change.
HTMLElement.prototype.innerHTML setter = function (str) { var r = this.ownerDocument.createRange(); r.selectNodeContents(this); r.deleteContents(); var df = r.createContextualFragment(str); this.appendChild(df); return str;}HTMLElement.prototype.outerHTML setter = function (str) { var r = this.ownerDocument.createRange(); r.setStartBefore(this); var df = r.createContextualFragment(str); this.parentNode.replaceChild(df, this); return str;}
HTMLElement.prototype.innerHTML getter = function () { return getInnerHTML(this);}
function getInnerHTML(node) { var str = ""; for (var i=0; i<node.childNodes.length; i++) str += getOuterHTML(node.childNodes.item(i)); return str;}
HTMLElement.prototype.outerHTML getter = function () { return getOuterHTML(this)}
function getOuterHTML(node) { var str = ""; switch (node.nodeType) { case 1: // ELEMENT_NODE str += "<" + node.nodeName; for (var i=0; i<node.attributes.length; i++) { if (node.attributes.item(i).nodeValue != null) { str += " " str += node.attributes.item(i).nodeName; str += "=\""; str += node.attributes.item(i).nodeValue; str += "\""; } }
if (node.childNodes.length == 0 && leafElems[node.nodeName]) str += ">"; else { str += ">"; str += getInnerHTML(node); str += "<" + node.nodeName + ">" } break; case 3: //TEXT_NODE str += node.nodeValue; break; case 4: // CDATA_SECTION_NODE str += "<![CDATA[" + node.nodeValue + "]]>"; break; case 5: // ENTITY_REFERENCE_NODE str += "&" + node.nodeName + ";" break;
case 8: // COMMENT_NODE str += "<!--" + node.nodeValue + "-->" break; }
return str;}
var _leafElems = ["IMG", "HR", "BR", "INPUT"];var leafElems = {};for (var i=0; i<_leafElems.length; i++) leafElems[_leafElems[i]] = true;
然后我们可以封成JS引用
if (/Mozilla\/5\.0/.test(navigator.userAgent)) document.write('<script type="text/javascript" src="mozInnerHTML.js"></sc' + 'ript>');
<script language="JavaScript" type="Text/JavaScript"> 
<!-- 
var emptyElements = { HR: true, BR: true, IMG: true, INPUT: true }; var specialElements = { TEXTAREA: true }; 
HTMLElement.prototype.outerHTML getter = function() { 
return getOuterHTML(this); 
} 
function getOuterHTML(node) { 
var html = ''; 
switch (node.nodeType) { 
case Node.ELEMENT_NODE: html += '<'; html += node.nodeName; if (!specialElements[node.nodeName]) { 
for (var a = 0; a < node.attributes.length; a++) 
html += ' ' + node.attributes[a].nodeName.toUpperCase() + '="' + node.attributes[a].nodeValue + '"'; 
html += '>'; 
if (!emptyElements[node.nodeName]) { 
html += node.innerHTML; 
html += '<\/' + node.nodeName + '>'; 
} 
} else 
switch (node.nodeName) { 
case 'TEXTAREA': for (var a = 0; a < node.attributes.length; a++) 
if (node.attributes[a].nodeName.toLowerCase() != 'value') 
html 
+= ' ' + node.attributes[a].nodeName.toUpperCase() + '="' + node.attributes[a].nodeValue 
+ '"'; 
else 
var content = node.attributes[a].nodeValue; 
html += '>'; html += content; html += '<\/' + node.nodeName + '>'; break; 
} break; 
case Node.TEXT_NODE: html += node.nodeValue; break; 
case Node.COMMENT_NODE: html += '<!' + '--' + node.nodeValue + '--' + '>'; break; 
} 
return html; 
} 
//--> 
</script>
Javascript 相关文章推荐
JS命名空间的另一种实现
Aug 09 Javascript
在JavaScript中操作时间之getMonth()方法的使用
Jun 10 Javascript
AngularJS基础 ng-options 指令详解
Aug 02 Javascript
概述如何实现一个简单的浏览器端js模块加载器
Dec 07 Javascript
详解angularJs中自定义directive的数据交互
Jan 13 Javascript
xmlplus组件设计系列之分隔框(DividedBox)(8)
May 02 Javascript
在Vue组件上动态添加和删除属性方法
Feb 23 Javascript
详解如何在vue-cli中使用vuex
Aug 07 Javascript
使用微信小程序开发弹出框应用实例详解
Oct 18 Javascript
vue-router之实现导航切换过渡动画效果
Oct 31 Javascript
webpack3.0升级4.0的方法步骤
Apr 02 Javascript
javascript实现滚轮轮播图片
Dec 13 Javascript
IE innerHTML,outerHTML所引起的问题
Jun 04 #Javascript
js 鼠标点击事件及其它捕获
Jun 04 #Javascript
一些常用的JS功能函数(2009-06-04更新)
Jun 04 #Javascript
javascript globalStorage类代码
Jun 04 #Javascript
IE8 兼容性问题(属性名区分大小写)
Jun 04 #Javascript
JavaScript效率调优经验
Jun 04 #Javascript
cookie丢失问题(认证失效) Authentication (用户验证信息)也会丢失
Jun 04 #Javascript
You might like
整合了前面的PHP数据库连接类~~做成一个分页类!
2006/11/25 PHP
phpmyadmin MySQL 加密配置方法
2009/07/05 PHP
使用php计算排列组合的方法
2013/11/13 PHP
PHP的一个完美GIF等比缩放类,附带去除缩放黑背景
2014/04/01 PHP
ThinkPHP查询返回简单字段数组的方法
2014/08/25 PHP
PHP IDE PHPStorm配置支持友好Laravel代码提示方法
2015/05/12 PHP
PHP+MySQL统计该库中每个表的记录数并按递减顺序排列的方法
2016/02/15 PHP
php+ajax无刷新上传图片的实现方法
2016/12/06 PHP
在JavaScript中遭遇级联表达式陷阱
2007/03/08 Javascript
extjs DataReader、JsonReader、XmlReader的构造方法
2009/11/07 Javascript
Jquery getJSON方法详细分析
2013/12/26 Javascript
javascript event在FF和IE的兼容传参心得(绝对好用)
2014/07/10 Javascript
js重写alert控件(适合学习js的新手朋友)
2014/08/24 Javascript
JavaScript实现基于Cookie的存储类实例
2015/04/10 Javascript
JavaScript创建对象的方式小结(4种方式)
2015/12/17 Javascript
JavaScript数组的一些奇葩行为
2016/01/25 Javascript
jQuery实现的自定义弹出层效果实例详解
2016/09/04 Javascript
jQuery滑动到底部加载下一页数据的实例代码
2017/05/22 jQuery
vue.js 图片上传并预览及图片更换功能的实现代码
2018/08/27 Javascript
vue+element-ui JYAdmin后台管理系统模板解析
2020/07/28 Javascript
Openlayers实现图形绘制
2020/09/28 Javascript
[05:09]第二届DOTA2亚洲邀请赛决赛日比赛集锦:iG 3:0 OG夺冠
2017/04/05 DOTA
在Python中关于使用os模块遍历目录的实现方法
2019/01/03 Python
Pycharm最常用的快捷键及使用技巧
2020/03/05 Python
Python实现加密接口测试方法步骤详解
2020/06/05 Python
python excel多行合并的方法
2020/12/09 Python
医药专业应届毕业生求职信范文
2014/01/01 职场文书
空乘英文求职信
2014/04/13 职场文书
学校个人对照检查材料
2014/08/26 职场文书
个人授权委托书格式
2014/08/30 职场文书
教导主任个人总结
2015/03/03 职场文书
民事申诉状范本
2015/05/20 职场文书
职工培训工作总结
2015/08/10 职场文书
2019年年中工作总结讲话稿模板
2019/03/25 职场文书
JDBC连接的六步实例代码(与mysql连接)
2021/05/12 MySQL
Python pandas之求和运算和非空值个数统计
2021/08/07 Python