HTML to DOM

如何将HTML转为DOM,尽管现在你可以使用 DOMParser 或者 XMLHttpRequest对象来完成此项工作, 但毕竟除Firefox以外的大部分浏览器都没有实现这些功能. 在这些功能被所有浏览器实现之前,你可以在自己的网站中使用下面的方法来完成HTML字符串转DOM的需求.

安全的将简单HTML解析为DOM

当使用 XMLHttpRequest 获取远程页面的 HTML 内容时,将 HTML 字符串转解析成 DOM 后就可以很很方便地进行操作。但是,将远程内容注入具有特权的扩展上下文时可能会伴随着潜在的风险,因此,安全地解析 HTML 就很有必要。

下面的方法就可以安全的解析简单 HTML 并返回一个像 web 页面元素那样可以操作的DOM对象。这会移除诸如 <script><style><head><body><title> 和 <iframe> 的标签,同样也会移除所有的 JavaScript,包括哪些包含 JavaScript 属性的元素。

function HTMLParser(aHTMLString){
  var html = document.implementation.createDocument("http://www.w3.org/1999/xhtml", "html", null),
    body = document.createElementNS("http://www.w3.org/1999/xhtml", "body");
  html.documentElement.appendChild(body);

  body.appendChild(Components.classes["@mozilla.org/feed-unescapehtml;1"]
    .getService(Components.interfaces.nsIScriptableUnescapeHTML)
    .parseFragment(aHTMLString, false, null, body));

  return body;
}

它在当前页面创建一个内容级别 ( content-level ) ( 比chrome级别更安全 ) 的 <div> ,然后解析 HTML 片段并将结果插入 <div> 中。这个 <div> 将被返回,但实际上它并不会被添加到当前页面中。返回的 <body> 对象是 Element 类型的。

下面的例子可以计算出一个HTML字符串中包含的段落数.

var DOMPars = HTMLParser('<p>foo</p><p>bar</p>');
alert(DOMPars.getElementsByTagName('p').length);

如果 HTMLParser() 返回 html 变量 ( 而不是 body ),你将获得所有的 dom 对象及其全部的方法,然后你就可以像下面这样获取 div 标签内的信息。 

var DOMPars = HTMLParser("<div id='userInfo'>John was a mediocre programmer, but people liked him <strong>anyway</strong>.</div>");
alert(DOMPars.getElementById('userInfo').innerHTML);

为了解析一个完成的 HTML 页面,你应当将其载入到一个类型为 content ( 而不是 chrome 类型 ) 的 iframe 中。参考下面的 使用隐藏的 iframe 将 HTML 解析到 DOM 中

使用隐藏的iframe标签将HTML解析为DOM

下面的例子代码还做了一些其他工作,例如:创建一个唯一name 和 ID 的函数等.

var frame = document.getElementById("sample-frame");
if (!frame) {
	// create frame
		frame = document.createElement("iframe"); // iframe (or browser on older Firefox)
		frame.setAttribute("id", "sample-frame");
		frame.setAttribute("name", "sample-frame");
		frame.setAttribute("type", "content");
		frame.setAttribute("collapsed", "true");
		document.getElementById("main-window").appendChild(frame);
		// or 
			// document.documentElement.appendChild(frame);

	// set restrictions as needed
		frame.webNavigation.allowAuth = false;
		frame.webNavigation.allowImages = false;
		frame.webNavigation.allowJavascript = false;
		frame.webNavigation.allowMetaRedirects = true;
		frame.webNavigation.allowPlugins = false;
		frame.webNavigation.allowSubframes = false;

	// listen for load
		frame.addEventListener("load", function (event) {
		  // the document of the HTML in the DOM
			var doc = event.originalTarget;
		  // skip blank page or frame
			if (doc.location.href == "about:blank" || doc.defaultView.frameElement) return;

		  // do something with the DOM of doc
		  	alert(doc.location.href);

		  // when done remove frame or set location "about:blank"
			  setTimeout(function (){
				  var frame = document.getElementById("sample-frame");
				  // remove frame
				  		// frame.destroy(); // if using browser element instead of iframe
						frame.parentNode.removeChild(frame);
					// or set location "about:blank"
						// frame.contentDocument.location.href = "about:blank";
			  },10);
		}, true);
} 


// load a page
	frame.contentDocument.location.href = "http://www.mozilla.org/"; 
	// or 
		// frame.webNavigation.loadURI("http://www.mozilla.org/",Components.interfaces.nsIWebNavigation,null,null,null);

如果你一开始获取到的是一个包含HTML的字符串,那么你可以将该字符串转换为 data URI 格式,然后用 iframe 打开这个 data URI .

使用隐藏的XUL Iframe(备用方法)

有时,使用浏览器元素会导致过犹不及,或者不能满足你的需求,或者你不能满足一些使用条件。当处理 Donkeyfire 时,可以使用 iframe XUL 元素,它非常的好实现。

作为示例,下面将展示浏览器重叠项 .xul 文件,以及一些访问它的 JavaScript 代码。

下面是一些可以加入到浏览器重叠项 .xul 文件的 XUL 代码。别忘记修改它的 ID 和 name。

<vbox hidden="false" height="0">
  <iframe type="content" src="" name="donkey-browser" hidden="false" id="donkey-browser" height="0"/>
</vbox>

然后在你的扩展程序的load 事件回调中添加如下代码:

onLoad: function() {
	donkeybrowser = document.getElementById("donkey-browser");
	if (donkeybrowser) {
		donkeybrowser.style.height = "0px";
		donkeybrowser.webNavigation.allowAuth = true;
		donkeybrowser.webNavigation.allowImages = false;
		donkeybrowser.webNavigation.allowJavascript = false;
		donkeybrowser.webNavigation.allowMetaRedirects = true;
		donkeybrowser.webNavigation.allowPlugins = false;
		donkeybrowser.webNavigation.allowSubframes = false;
		donkeybrowser.addEventListener("DOMContentLoaded", function (e) { donkeyfire.donkeybrowser_onPageLoad(e); }, true);
	}


通过以上代码,可以访问到在 .xul 文件中声明的 iframe 元素。以上代码中最有趣的便是为元素定义的 DOMContentLoaded 事件函数。让我们看一下 donkeyfire.donkeybrowser_onPageLoad() 回调:

donkeybrowser_onPageLoad: function(aEvent) {
	var doc = aEvent.originalTarget;
	var url = doc.location.href;
	if (aEvent.originalTarget.nodeName == "#document") { // ok, it's a real page, let's do our magic
		dump("[DF] URL = "+url+"\n");
		var text = doc.evaluate("/html/body/h1",doc,null,XPathResult.STRING_TYPE,null).stringValue;
		dump("[DF] TEXT in /html/body/h1 = "+text+"\n");
	}
},

如你所见,我们拥有访问在背景中载入的页面的全部权限,我们甚至可以使用 XPath 表达式。在这个例子中,我们向终端打印了页面的 URL 以及页面 <body> 第一个 h1 标签里的内容。

但是,我们仍然需要看一下如何在 iframe 中如何调用 loadURI() 方法:

donkeybrowser.webNavigation.loadURI("http://developer.mozilla.org",
              Components.interfaces.nsIWebNavigation, null, null, null);

另外,建议参考 nsIWebNavigation 接口.

文档标签和贡献者

 此页面的贡献者: holynewbie, ziyunfei
 最后编辑者: holynewbie,