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 相关文章推荐
用正则获取指定路径文件的名称
Feb 27 Javascript
常用的JavaScript验证正则表达式汇总
Nov 26 Javascript
js对象继承之原型链继承实例
Jan 10 Javascript
AngularJS入门教程之多视图切换用法示例
Nov 02 Javascript
微信小程序 页面传参实例详解
Nov 16 Javascript
详解升级react-router 4 踩坑指南
Aug 14 Javascript
Scala解析Json字符串的实例详解
Oct 11 Javascript
JS实现左边列表移到到右边列表功能
Mar 28 Javascript
微信小程序实现弹出菜单功能
Jun 12 Javascript
vue完成项目后,打包成静态文件的方法
Sep 03 Javascript
vue实现的组件兄弟间通信功能示例
Dec 04 Javascript
使用taro开发微信小程序遇到的坑总结
Apr 08 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中使用sleep函数实现定时任务实例分享
2014/08/21 PHP
PHP数字金额转换成中文大写显示
2019/01/05 PHP
PHP调用全国天气预报数据接口查询天气示例
2019/02/20 PHP
js借助ActiveXObject实现创建文件
2013/09/29 Javascript
JS关闭窗口或JS关闭页面的几种代码分享
2013/10/25 Javascript
js浏览器本地存储store.js介绍及应用
2014/05/13 Javascript
jQuery使用andSelf()来包含之前的选择集
2014/05/19 Javascript
JavaScript实现更改网页背景与字体颜色的方法
2015/02/02 Javascript
实例解析JS布尔对象的toString()方法和valueOf()方法
2015/10/25 Javascript
js修改onclick动作的四种方法(推荐)
2016/08/18 Javascript
原生js获取浏览器窗口及元素宽高常用方法集合
2017/01/18 Javascript
Node.js服务器开启Gzip压缩教程
2017/08/11 Javascript
你可能不知道的JSON.stringify()详解
2017/08/17 Javascript
JS实现的简单分页功能示例
2018/08/23 Javascript
javascript使用substring实现的展开与收缩文字功能示例
2019/06/17 Javascript
js加减乘除精确运算方法实例代码
2021/01/17 Javascript
[45:25]OG vs EG 2019国际邀请赛淘汰赛 胜者组 BO3 第一场 8.22
2019/09/05 DOTA
Python字符串、元组、列表、字典互相转换的方法
2016/01/23 Python
尝试用最短的Python代码来实现服务器和代理服务器
2016/06/23 Python
python中数组和矩阵乘法及使用总结(推荐)
2019/05/18 Python
python基于paramiko将文件上传到服务器代码实现
2019/07/08 Python
Django在admin后台集成TinyMCE富文本编辑器的例子
2019/08/09 Python
解决Python图形界面中设置尺寸的问题
2020/03/05 Python
Python unittest生成测试报告过程解析
2020/09/08 Python
python tkinter实现连连看游戏
2020/11/16 Python
CSS3属性box-sizing使用指南
2014/12/09 HTML / CSS
html5应用缓存_动力节点Java学院整理
2017/07/13 HTML / CSS
葡萄牙鞋子品牌:Fair
2016/12/10 全球购物
寻找完美的房车租赁:RVShare
2019/02/23 全球购物
Auguste The Label官网:澳大利亚一家精品女装时尚品牌
2020/06/14 全球购物
领导视察欢迎词
2014/01/15 职场文书
寄语十八大感言
2014/02/07 职场文书
融资租赁计划书
2014/04/29 职场文书
话题作文之关于呼唤
2019/11/29 职场文书
Hive HQL支持2种查询语句风格
2022/06/25 数据库
MySQL下载安装配置详细教程 附下载资源
2022/09/23 MySQL