如何围绕现有元素包装div?

时间:2016-03-07 22:16:31

标签: ruby xpath nokogiri

我正在尝试解析现有文档,并通过在某些现有表单元素周围包装div来修改它。

HTML表单看起来有点像这样:

<form>
  <label for="username">Username:</label>
  <input name="username" type="text" />
  <label for="password">Password:</label>
  <input name="password" type="password" />
</form>

我可以用Nokogiri解析文件OK,我知道wrap方法,但我很难掌握如何一次性选择标签和输入标签,然后围绕这些标签包裹div 。所以我要找的结果是:

<form>
  <div class="form-group">
    <label for="username">Username:</label>
    <input name="username" type="text" />
  </div>    
  <div class="form-group">
    <label for="password">Password:</label>
    <input name="password" type="password" />
  </div>
</form>

我尝试了各种XPath / CSS选择器,并且可以创建仅标签/输入或整个表单的所有元素的节点集。有没有办法实现这种修改?

1 个答案:

答案 0 :(得分:2)

单个XPath表达式只能返回单个节点集合,因此为了实现您的目标,您需要进行多次查询,每个label - input对一个查询。

您可以选择具有类似内容的单个对,假设标记表现良好(即每个input前面都有label):

//label[1] | //label[1]/following-sibling::input[1]

这将选择第一个label和以下input。但是,您想要选择所有这些对。一种方法是首先选择所有label个节点,然后为每个label选择它和以下输入。

labels = doc.xpath("//form/label")
labels.each do |l|
  nodes = l.xpath(". | ./following-sibling::input[1]")
  # nodes now contains a label-input pair...
end

我不认为wrap方法可以将div元素添加为每个对的祖先,因为它会将元素添加到每个成员节点集。您可能需要手动移动它们,例如

labels = doc.xpath("//form/label")
labels.each do |l|
  # Select this node and its neighbour.
  nodes = l.xpath(". | ./following-sibling::input[1]")

  # Create the new element, and add it before the label.
  div = Nokogiri::XML::Node.new('div', l.document)
  l.before(div)

  # Move each of the pair onto this new element.
  nodes.each do |n|
    div.add_child(n)
  end
end

请注意,此方法不会移动任何文本节点,因此您可能会发现文档的空白稍有变化。