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 相关文章推荐
理解Javascript_12_执行模型浅析
Oct 18 Javascript
Jquery对select的增、删、改、查操作
Feb 06 Javascript
jQuery实现页面内锚点平滑跳转特效的方法总结
May 11 Javascript
jQuery+ajax实现无刷新级联菜单示例
May 21 Javascript
Angular2表单自定义验证器的实现
Oct 19 Javascript
React组件的三种写法总结
Jan 12 Javascript
Node连接mysql数据库方法介绍
Feb 07 Javascript
详解vuejs之v-for列表渲染
Jun 22 Javascript
vue.js与后台数据交互的实例讲解
Aug 08 Javascript
微信小程序使用gitee进行版本管理
Sep 20 Javascript
vue3获取当前路由地址
Feb 18 Vue.js
vue使用wavesurfer.js解决音频可视化播放问题
Apr 04 Vue.js
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中3种方法删除字符串中间的空格
2014/03/10 PHP
PHP中使用循环实现的金字塔图形
2014/11/08 PHP
php检测图片主要颜色的方法
2015/07/01 PHP
ie和firefox中img对象区别的困惑
2006/12/27 Javascript
jQuery 判断元素上是否绑定了事件
2009/10/28 Javascript
jQuery的slideToggle方法实例
2013/05/07 Javascript
使用原生JS实现弹出层特效
2014/12/22 Javascript
JS模式之简单的订阅者和发布者模式完整实例
2015/06/30 Javascript
javascript动态获取登录时间和在线时长
2016/02/25 Javascript
Boostrap实现的登录界面实例代码
2016/10/09 Javascript
JS 插件dropload下拉刷新、上拉加载使用小结
2017/04/13 Javascript
iSlider手机端图片滑动切换插件使用详解
2019/12/24 Javascript
python连接oracle数据库实例
2014/10/17 Python
Python中AND、OR的一个使用小技巧
2015/02/18 Python
详解Django中的过滤器
2015/07/16 Python
python与php实现分割文件代码
2017/03/06 Python
Python基于list的append和pop方法实现堆栈与队列功能示例
2017/07/24 Python
Python探索之Metaclass初步了解
2017/10/28 Python
python3.5基于TCP实现文件传输
2020/03/20 Python
对pytorch网络层结构的数组化详解
2018/12/08 Python
python导入pandas具体步骤方法
2019/06/23 Python
python3实现用turtle模块画一棵随机樱花树
2019/11/21 Python
pyinstaller打包单文件时--uac-admin选项不起作用怎么办
2020/04/15 Python
HTML5全屏(Fullscreen)API详细介绍
2015/04/24 HTML / CSS
REN Clean Skincare官网:英国本土有机护肤品牌
2019/02/23 全球购物
欧舒丹俄罗斯官方网站:L’OCCITANE俄罗斯
2019/11/22 全球购物
Michael Kors澳大利亚官网:世界知名的奢侈饰品和成衣设计师
2020/02/13 全球购物
什么是事务?事务有哪些性质?
2012/03/11 面试题
服装促销活动方案
2014/02/23 职场文书
三年级学生评语
2014/04/23 职场文书
保研导师推荐信
2015/03/25 职场文书
中标通知书格式
2015/04/17 职场文书
高三语文教学反思
2016/02/16 职场文书
HTML速写之Emmet语法规则的实现
2021/04/07 HTML / CSS
C3 线性化算法与 MRO之Python中的多继承
2021/10/05 Python
Java实现给Word文件添加文字水印
2022/02/15 Java/Android