>

XPath是一个好工具,DOM可以做什么

- 编辑:至尊游戏网站 -

XPath是一个好工具,DOM可以做什么

XPath是一个好工具

2014/02/23 · CSS · 3 评论 · CSS

本文由 伯乐在线 - 杨帅 翻译。未经许可,禁绝转发!
英文出处:rapgenius。应接参与翻译组。

本身第一遍接触X帕特h是在二〇〇六年,但这段时间才开端对它发出兴趣。曾经在超越五成情形下自己都会尽量制止使用它,而当自个儿只好尝试接收它时,每一回都以诉讼失败告终。那时候XPath对自个儿的话并未有怎么意义。

可是后来本身遇到了贰个特种的剖判难题(对CSS选取器来讲过于复杂,而用手工业代码的话又过分简短),于是小编主宰再尝试贰回XPath。令本人认为到兴奋的是,那的确行得通,并且很有用。

以下是本身的亲身经历

DOM节点音信
各样DOM节点都封存了有关该节点的一些音信,咱们来探望怎么着访谈那么些音信。
nodeName、nodeValue和nodeType
相像,依据字面意思:nodeName表示节点名称,nodeValue表示节点值,nodeType则为节点类型。各种DOM节点都富含那个音信,大家得以好低价地经过JavaScript来拜访那一个属性。上边大家就来看多少个运用这几个属性的二个事例。
运用实例
HTML代码:
<div>
  <p id="test">test文字</p>
</div>

自身遇上的标题

倘令你管理二个乐章网站,为了保障生机勃勃致的翻阅体验,你要搜求每行歌词的率先个单词。如若歌词使用纯文本格式保存,那么能够平素用上面包车型客车代码来兑现。

JavaScript

lyrics.gsub!(/^./) { |character| character.upcase }

1
lyrics.gsub!(/^./) { |character| character.upcase }

唯独如若歌词被保存肯html格式就平素不及此轻松了,因为dom结构本人并未”行”的概念,所以并未主意使用贰个简易的正则表明式来识别行。

为此我们要做的首先件职业是概念什么是dom结构中的“行的起源”,下边是多个简易的例证:

  • <p>标签中第二个公文节点
  • <br>前边的率先个公文节点

就好像上边那样:

XHTML

<p>This is the beginning of a line.<br>This is too.</p>

1
<p>This is the beginning of a line.<br>This is too.</p>

然则除外大家可能还要管理嵌套的行内成分:

XHTML

<p><em>This</em> is the beginning of a line. <strong>This is not.</strong></p>

1
<p><em>This</em> is the beginning of a line. <strong>This is not.</strong></p>

JavaScript代码
<script type="text/javascript">
function getName(){
        var x =document.getElementById("test");
        alert(x.nodeName);
}
function getValue(){
        var x =document.getElementById("test");
        alert(x.nodeValue);
}
function getType(){
        var x =document.getElementById("test");
        alert(x.nodeType);
}
</script>

正规的解决方案

自己想开的首先个减轻办法是用Ruby写四个办法来围观dom中存有相关的意气风发对并递归找寻富有相符条件的节点。此中使用了多少个轻量级的css选取器:

JavaScript

def each_new_line(document) document.css('p').each { |p| yield first_text_node(p) } document.css('br').each { |br| yield first_text_node(br.next) } end def first_text_node(node) if node.nil? then nil elsif node.text? then node elsif node.children.any? then first_text_node(node.children.first) end end

1
2
3
4
5
6
7
8
9
10
11
def each_new_line(document)
  document.css(&#039;p&#039;).each { |p| yield first_text_node(p) }
  document.css(&#039;br&#039;).each { |br| yield first_text_node(br.next) }
end
 
def first_text_node(node)
  if node.nil? then nil
  elsif node.text? then node
  elsif node.children.any? then first_text_node(node.children.first)
  end
end

那是一个相比客观的应用方案,不过11行的代码就好像有些儿长。有一些儿杀鸡用牛刀的感到,仅仅为了获得dom的节点而用上Ruby的迭代器和标准语句以为某些犯不上。应该有越来越好的艺术呢?

实效:
test文字

终于聊到正题了(XPath)

X帕特h有须臾间多少个原因轻易令人纳闷。第一点是互连网大致向来不得以参考的事物(W3Schools!就不用想了)。EnclaveFC已然是自己找到的最棒的文书档案了。

其次点是XPath看上去有个别像CSS。方法名里就有“path”,所以本身接连假如XPath的表达式中的 / 和CSS采用器中的 > 是三个情趣。

JavaScript

document.xpath('//p/em/a') == document.css('p > em > a')

1
document.xpath(&#039;//p/em/a&#039;) == document.css(&#039;p &gt; em &gt; a&#039;)

其实,XPath表明式富含了广大简写,假如大家想要弄通晓上边代码运维时毕竟爆发了什么样就要弄通晓那些简写。上边是用全拼写出来的生机勃勃律的表明式:

JavaScript

/descendant-or-self::node()/child::p/child::em/child::a/

1
/descendant-or-self::node()/child::p/child::em/child::a/

以此XPath表达式和地点的CSS采取器的效应是均等的,但并不像自个儿事先假使的那样。一个XPath表明式是由一个或三个被 / 分割的原则性步(location steps)组成。表明式中的第多少个 / 代表了文书档案(document)的根节点。每一种定位步都注解了已经被相称的节点并转达一下三条新闻:

自己想从当下的岗位移动到哪?

答案是轴(Axis),是可选的。暗中认可的轴是child,表示“当前被入选节点的全数子节点”。在地点的例证中,descendant-or-self是首先个定位部的轴,表示“全部当前被入选的节点和她们具备的子节点”。大多数XPath标准中定义的轴都有像“descendant-or-self”那样的语义化的名字。

本身想要选用怎么着项目标节点?

筛选的内容是由节点测量试验来钦定的,那也是每一个定位步中不得缺点和失误的片段。在大家事先的例证中,node()相配的是整套项目;text()相称到的是文件节点;element()只好合作到成分,并必需指明节点名称(像p,em等),节点名称必填。

或者增添额外的过滤器吗?

唯恐大家只想接收当前全部节点的率先个子成分或只想选则有href属性的<a>标签。对于此类断言(assertion),我们能够运用谓词(predicates)根据额外的遍历树(additional tree traversals)来过滤出相符条件的节点。那样大家就能够依据那一个节点的性质(children, parents, or siblings)来过滤出相符条件的节点。

咱们的例证中从未谓词,以后让我们来加一个只特别常有href属性的<a>标签:

JavaScript

/descendant-or-self::node()/child::p/child::em/child::a[attribute::href]

1
/descendant-or-self::node()/child::p/child::em/child::a[attribute::href]

虽说谓词看上去很像三个括号中的定位步,可是谓词中的“节点测量检验(node test)”部分有比定位步中的节点测验越来越多的效果与利益。

getName()
getValue()
getType()

换二个角度来看XPath

与二个加强型的CSS选取器相比,XPath与JQuery的福利更相近。举个例子,我们得以把前边的XPath表明式换成JQuery的方式:

JavaScript

$(document).find('*'). children('p'). children('em'). children('a').filter('[href]')

1
2
3
4
$(document).find('*').
  children('p').
  children('em').
  children('a').filter('[href]')

地点的代码中,我们用到的JQuery的不二等秘书籍与轴的遵循是风流倜傥律的:

JavaScript

.children()也正是轴中的child,.find()也等于descendant。

1
.children()相当于轴中的child,.find()相当于descendant。

JQuery方法中的采纳器也就是XPath中的节点测验,只缺憾jQuery不允许选择文本节点。

jQuery中的.filter()方法也便是XPath中的谓词,.children(’em’)的成效是特别全体相配到的<p>标签中的全部<em>子成分。那样看来,XPah要比jQuery强盛得多。

从例子中得以窥见:一个p标签的nodeName是P,尽管在XHMTL中我们把段落写作小些的p,该属性仍然是大写字母。它的nodeValue是null,而并非它所包含的文件,那是因为文件自个儿也是二个节点,而文本节点的nodeValue才是文字。
最后,nodeType是“1”。它的意思是因季秋点(大家领略,p是三个HTML成分)。此外首要的DOM节点类型:
2 —— 属性节点
3 —— 文本节点

让大家回去识别行首的标题

现行反革命大家对XPath的干活原理已经有了深深的摸底,下边来用它消弭早前涉嫌的标题。首先大家先把难点简化一下,只寻找每段的第四个公文节点:

JavaScript

/descendant-or-self::node()/child::p/child::text()[position()=1]

1
/descendant-or-self::node()/child::p/child::text()[position()=1]

地点的代码的作用依次是:

  • 1.寻找文书档案中的全部节点
  • 2.寻觅那些节点的兼具为<p>的子节点
  • 3.物色那些<p>的文本子节点
  • 4.只保留这个节点中切合条件的率先个要素

注意position() function 在代码中意味着的是每种<p>中的第一个文本子节点并不是整个文书档案中的第叁个<p>的文本子节点。

接下去,为了找到<p>中被嵌套得很深的公文节点,大家把child换来descendant

JavaScript

/descendant-or-self::node()/child::p/descendant::text()[position()=1]

1
/descendant-or-self::node()/child::p/descendant::text()[position()=1]

接下去是甄别换行的难题,首先大家给这一长串代码折下行(因为太长了),X帕特h是同意那样做的。参加换行的识别后,代码如下:

JavaScript

/descendant-or-self::node()/ child::br/ following-sibling::node()[position=1]/ descendant-or-self::text()[position()=1]

1
2
3
4
/descendant-or-self::node()/
child::br/
following-sibling::node()[position=1]/
descendant-or-self::text()[position()=1]

每意气风发行代码的情趣分别是:

  • 1.找到全部节点
  • 2.找到到那个节点的<br>子节点
  • 3.找到那个<br>的下四个同级节点
  • 4.固然地方取到的不是文件节点,则取它们的子节点中的第贰个文本节点

那样一来大家就能够并且选出<p>卯月<br>后的新的一站式。下边大家上述的代码合併成三个表明式:

JavaScript

(/descendant-or-self::node()/child::p| /descendant-or-self::node()/child::br/following-sibling::node()[position=1])/ descendant-or-self::text()[position()=1]

1
2
3
(/descendant-or-self::node()/child::p|
/descendant-or-self::node()/child::br/following-sibling::node()[position=1])/
descendant-or-self::text()[position()=1]

终极大家把简写替换进去:

JavaScript

(//p|//br/following-sibling::node()[position=1])/ descendant-or-self::text()[position=1]

1
2
(//p|//br/following-sibling::node()[position=1])/
descendant-or-self::text()[position=1]

这么大家就把三个繁杂的定义用贰个回顾的表达式表示出来了。假设大家想加盟越来越多的对行的操作,只须要往完成相称的代码中到场越多的成分名称就足以了。

8 —— 注释节点

DOM信息nodeType的应用
正文介绍了动用DOM节点的nodeType属性来过滤出特定类型节点的法门。何况举了四个总计文书档案内有着因素商点的实例。
利用DOM时叁个很意外(也很可恶)的处境便是连代码中的换行都恐怕会被视为节点。以下边包车型大巴HTML代码为例。
HTML代码
代码如下,大家得以知道地看来div节点内独有八个段落。
<div id="test">
        <p>我是测量检验段落</p>
</div>
<div id="result"> </div>
JavaScript代码
事后大家利用上边包车型客车JavaScript代码来展现id为test的div的子节点。首先,大家采纳getElementById获得id为test的节点。之后接受childNodes获得它的子结点。最终在id为result的节点中体现那么些子结点的节点名称和节点类型。
<script type="text/javascript">
function test(){
        var str = "";
        var nodes =document.getElementById("test").childNodes;
        for( var i = 0;i <nodes.length; i++){
               str += nodes[i].nodeName;
               str += ":";
               str += nodes[i].nodeType;
               str += "<br/>";
        }
        document.getElementById("result").innerHTML= str;
}
</script>
nodeType的应用
点击下边包车型客车开关就可以看看你所选择的浏览器感觉那几个div有多少个子结点。
test()
自家是测量试验段落
 
在Fire福克斯下,得到如下的结果:
#text:3
P:1
#text:3
在IE下,获得如下结果:
P:1
足见,在FireFox中,代码中的换行也被以为是八个门类为1,名叫#text的节点。可是普通大家对代码中的换行并不感兴趣。那时我们就足以应用nodeType来过滤掉那类没有必要的要素。使用上边包车型地铁JS代码就足以兑现了。
for( var i = 0;i < nodes.length; i++){
        if(nodes[i].nodeType == 1){
        //代码,独有在节点为因早秋点的时候才实行
        }
}
遍历文书档案全部的成分节点www.2cto.com
下边大家在来看生龙活虎段使用nodeType的JS代码。它的功力是遍历文书档案中保有的成分节点,並且总括成分节点的总的数量。当然我们以能够改善代码来对那个要九秋点做任何操作。JavaScript代码如下:
<script type="text/javascript">
function countElements(node){
        var total = 0;
        if(node.nodeType == 1){ total +=1;}
        var children = node.childNodes;
        for(var i = 0;i <children.length; i++){
               total +=countElements(children[i]);
        }
        return total;
}
function test2(){
        var elementsCount =countElements(document);
        alert(elementsCount);
}
</script>
test2()
点击上边的开关就能够见到那几个网页的总成分节点数了

======================================================================================================
修改DOM——innerHTML
本节介绍使用innerHTML来访问以致改正HTML DOM的艺术。
作者们事先早就理解到一些询问DOM节点的函数,比方getElementById和getElementsByTagName。但是我们不仅可以够访谈DOM节点,更能够转移它们,以致校订一切节点树的组织。上边我们就来探视改动DOM的最不难易行的章程——innerHTML。
innerHTML的情致正是“里面包车型地铁HTML”,就像它的名字同样轻巧了然,使用它也是特别的简短。我们先来看一个事例。
做客innerHTML实例代码
<div id="test">
        <p>笔者是测量检验段落。</p>
</div>
成效如下:
本人是测验段落。
test() ,点击这么些开关就能够显示出div的innerHTML,它调用的JS代码如下:
<script type="text/javascript">
function test(){
        alert(document.getElementById("test").innerHTML);
}
</script>
改正innerHTML实例代码
给成分的innerHTML赋值就能够变动成分的剧情了,相符是地点的div,我们创造其它风度翩翩段代码来改变它的内容:
<script type="text/javascript">
function testW(){
        var str = "<p>万象更新!¥%……&*()</p>";
        document.getElementById("test").innerHTML= str;
}
</script>
testW() ,点击这些开关就足以实行上边的代码,将率先个例子中div的源委更改。
 
删除DOM节点——removeChild
本节介绍了哪些利用removeChild从文书档案中剔除DOM节点,举出二个归纳例子的还要还简单介绍了该函数的重回值。
上生龙活虎节大家看了哪些运用innerHTML来改革DOM,而接受removeChild函数则足以去除文书档案中的内定节点。大家如故采用二个例证来申明该函数的施用方法。
HTML代码
<div id="test">
        <p>小编是将要被剔除的节点</p>
</div>
实例JavaScript代码
<script type="text/javascript">
function remove(){
        var test =document.getElementById("test");
        var children = test.childNodes;
        for(i=0;i<children.length;i++){
               test.removeChild(children[i]);
        }
}
</script>
剔除节点示例
自家是快要被剔除的节点
remove() ,点击那个按钮就能够将地点div的子结点全部剔除。
从地方的JS代码能够见到,使用removeChild的格式为:
父节点.removeChild(子结点)
removeChild的再次回到值
removeChild删除节点后会再次回到被剔除节点的援引,那样当大家以往再想重新将它增加到文档中,大概是对它实行别的操作的时候即可运用那么些援用了。比方上面包车型客车代码段:
var removed = xxx.removeChild(xxxx);
则removed就是被删除的节点,能够在背后的代码中应用。
添加DOM节点
本节介绍使用正式的DOM方法向文书档案中增添节点,用到了createElement、createTextNode以致appendChild等艺术。
前方大家透过innerHTML来更换文书档案的情节,当然也足以用它来增添节点。下边大家来看别的大器晚成种想文书档案中增添节点的措施——使用正规的DOM方法。
应用DOM方法纠正文书档案比选取innerHTML要麻烦一些,我们先来看黄金时代段示例HTML代码:
<div id="test" style="border:1pxsolid"></div>
行使DOM方法增多一个段落节点的思绪如下:
成立叁个p节点
穿件二个文书节点,何况拉长到p节点中
将p节点增添到div中
实例JavaScript代码
依照下面的思路大家得出如下的JS代码:
<script type="text/javascript">
function test(){
        var test =document.getElementById("test");
        var para = document.createElement("P");
        var text =document.createTextNode("要充裕的文本");
        para.appendChild(text);
        test.appendChild(para);
}      
</script>
加多节点的效益
上边正是原先的div,由于没有内容,大家只可以见到边框。
增多节点 ,点击这么些开关就足以运维方面包车型大巴代码。能够看出上面包车型客车div里多出了我们创制的段落节点。
代码解释
先是,大家用到了var para =document.createElement("P");该语句创立了二个段落节点,然而它并不会及时出现在文书档案中。要开创其余节点,只供给吗传递给该函数的字符串参数改过部分就可以,举个例子document.createElement("DIV")。
var text = document.createTextNode("要加上的文件");一句创造了三个文件节点。
para.appendChild(text);将text作为子结点加多到para那么些段落节点上。比较轻易看见,appendChild的调用语法如下:父节点.appendChild.(子结点)。
末尾一句test.appendChild(para);则将段落节点para加多到test那个div节点上,由于test节点原本即是文书档案的一片段,这一句会招致文书档案的翻新,也等于大家会看见段落文字被加多到了div节点里。

摘自 刘海闯的特辑

每一种DOM节点都保存了关于该节点的有的音讯,大家来探视哪些访谈那个新闻。 nodeName、nodeValue和nodeType 相同,依照字面意思:...

笔者们到底能从当中得到如何?

既然如此我们能用相对易懂的Ruby来得以达成为啥还要选拔X帕特h呢?

绝大繁多意况下,Ruby是用来编排高层代码的,比如商业逻辑,整合应用组件,描述复杂的园地模型。从中能够看到最棒的Ruby代码是用来陈诉意图而非用于贯彻。所以用Ruby来做一些低级次或与运用非亲非故的事务(遍历dom树来找钦命属性的节点)令人蛋疼。

XPath的中间一个优势是它的进程:XPath的遍历是透过libxml实现的,而原生代码的速度是至极快的。对于自己下边举的例子,与Ruby的兑现相比较,XPath实际上要慢得多。笔者猜导致那么些情状的案由是对于<br>标签的下叁个要素的探索。因为在此个动作中实际上是先筛选出了<br>后面的有着与之同级的要素然后才过滤出此中的率先个。

为此XPath快慢与拒绝议于你的运用方式,但是左臂有一些儿难。那是三个特意用来令你利用简单的惯用表明式来遍历dom的工具。

赞 收藏 3 评论

有关小编:杨帅

图片 1

(微博今日头条:@JAVA程序猿杨帅) 个人主页 · 作者的稿子

图片 2

本文由IT-综合发布,转载请注明来源:XPath是一个好工具,DOM可以做什么