熟悉xml的一定知道xpath这个东西吧,很好用的,以前做网站时用 xml做过数据库,查询时就是xpath,很快捷啊,那么在普通的html下能不能实现对dom文档的xpath似查询呢,答案是肯定的.以下是我自己写的代码:
if(!sx){
var sx={};
}
sx.$=function(id){
var t=(typeof(id)=="string"?document.getElementById(id):id);
function _$(){
this.e=t;
}
_$.prototype.xpath=function(mode){
if(window.HTMLElement) {
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;
});
XMLDocument.prototype.selectNodes = Element.prototype.selectNodes = function (){
//alert(arguments[0]);
var oNSResolver = this.createNSResolver(this.documentElement)
var aItems = this.evaluate(arguments[0].toLowerCase(), this, oNSResolver,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
var aResult = [];
for( var i = 0; i < aItems.snapshotLength; i++)
{
aResult[i] = aItems.snapshotItem(i);
}
//alert(aItems.snapshotLength);
return aResult;
}
}
p=this.e.cloneNode(true);
var s=p.getElementsByTagName("script");
for(var i=0;i<s.length;i++)
p.replaceChild(s[i].cloneNode(false)s[i]);
var html=p.outerHTML.replace(//=(?!"|')(.*?)(?=/s|>)/ig,"=/"$1/"");
if(window.ActiveXObject){
var x=new ActiveXObject("Msxml2.DOMDocument");
x.async=false;
x.loadXML("<?xml version=/"1.0/" encoding=/"gb2312/" ?>"+html);
}else{
var oParser = new DOMParser();
//alert(html);
var x = oParser.parseFromString(html,"text/xml");
//alert(x.documentElement.tagName);
}
var div=x.selectNodes(mode);
//alert(div.length);
var temp=[];
var a1=x.selectNodes(this.e.tagName.toUpperCase()+"//*");
//alert(a1.length);
var all=this.e.getElementsByTagName("*");
//alert(all.length);
var i1=0;
for(i=0;i<a1.length;i++){
//alert(i);
if(a1[i]==div[i1]){
temp.push(all[i]);
i1++;
}
}
x=null;
return temp;
}
return new _$;
}
代码是有点长了,不过是为了兼容ie和firefox啊,并且我还模拟了许多框架你的$函数,我首先说下这个函数:
sx.$=function(id){
var t=(typeof(id)=="string"?document.getElementById(id):id);
function _$(){
this.e=t;
}
_$.prototype.xpath=function(a){
.........................//实现方法的代码
}
return new _$;
}
缩减下代码就 不难理解这个函数了吧.就是自己封装个_$对象,并为这个对象设置方法,最后返回这个对象的实例即可.
那么接下来是正题,如何写xpath函数呢.我以前看过牛人用n多的正则表达式加递归循环去实现,是可以,但是太过麻烦,并且代码也过于冗长,非我等菜鸟可以完成的.于是我想到在浏览器内部就有xpath的实现,那么我们为什么不直接套用呢?
下面是ie下的代码:
p=this.e.cloneNode(true);
var s=p.getElementsByTagName("script");
for(var i=0;i<s.length;i++)
p.replaceChild(s[i].cloneNode(false),s[i]);
var html=p.outerHTML.replace(//=(?!"|')(.*?)(?=/s|>)/ig,"=/"$1/"");
if(window.ActiveXObject){
var x=new ActiveXObject("Msxml2.DOMDocument");
x.async=false;
x.loadXML("<?xml version=/"1.0/" encoding=/"gb2312/" ?>"+html);
注意那段正则表达式代码,是将元素的属性通通加上双引号,这样才符合xml标准,那将原有的script标签里的内容替换成空,此举实属无奈,因为script里的javascript代码处理太过复杂,很容易导致页面错误,所以我在此就忽略掉了.
下面是firefox下的代码:
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;
});
XMLDocument.prototype.selectNodes = Element.prototype.selectNodes = function (){
//alert(arguments[0]);
var oNSResolver = this.createNSResolver(this.documentElement)
var aItems = this.evaluate(arguments[0].toLowerCase(), this, oNSResolver,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
var aResult = [];
for( var i = 0; i < aItems.snapshotLength; i++)
{
aResult[i] = aItems.snapshotItem(i);
}
//alert(aItems.snapshotLength);
return aResult;
}
这里首先实现了firefox里outerhtml属性的实现,然后为xmldocument原型添加了selectnodes方法,那么我们如何来得到xml对象呢,看下面代码:
var oParser = new DOMParser();
//alert(html);
var x = oParser.parseFromString(html,"text/xml");
这样不就可以将一个字符串解析成xml文档了嘛.
最后再看下公用的代码:
var div=x.selectNodes(mode);
//alert(div.length);
var temp=[];
var a1=x.selectNodes(this.e.tagName.toUpperCase()+"//*");
//alert(a1.length);
var all=this.e.getElementsByTagName("*");
//alert(all.length);
var i1=0;
for(i=0;i<a1.length;i++){
//alert(i);
if(a1[i]==div[i1]){
temp.push(all[i]);
i1++;
}
}
x=null;
return temp;
注意这个最后数组里返回的可是原来的dom对象!我们可以根据返回的对象对查询的dom文档直接进行操作哦!仔细看代码,就明白为什么了,哈哈~
下面给个演示例子:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
</head>
<body marginleft="0" margintop="0">
<script src="core.js"></script>
<script src="dom.js"></script>
<div id="s" style="position:absolute;border:1px red solid;left:10px;top:10px;overflow:hidden;width:290px;height:290px"><span>werwe</span></div>
<div style="position:absolute;border:1px red solid;left:10px;top:10px;overflow:hidden;width:290px;height:290px">asedasdada</div>
<script id="t">
alert(sx.$(document.body).xpath("//SCRIPT")[2].id);
</script>
</body>
</html>
注意这里查询的元素的标签名一定要大写,我测试过了,ie里selectnodes只认标签名大写,而firefox里只认小写,我在代码里对这两种浏览器的大小写进行了转换,其他的什么的小写就行了.
好了,写了这么多,该伸伸懒腰了,文章有什么不妥之处,还望各位多多指教啊.