I'm trying to parse an existing document and modify it by wrapping a div around some existing form elements.
HTML form looks a bit like this:
<form>
<label for="username">Username:</label>
<input name="username" type="text" />
<label for="password">Password:</label>
<input name="password" type="password" />
</form>
I can parse the document OK with Nokogiri and i'm aware of the wrap
method but i'm struggling to grasp how to select both the label and input tags in one go and then wrap a div around these. So the result I am looking for is:
<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>
I have tried various XPaths / CSS selectors and can create a nodeset of just labels/inputs or all of the elements of the whole form. Is there any way to achieve this modification?
A single XPath expression can only return a single collection of nodes, so in order to achieve what you want you will need to make several queries, one for each label
– input
pair.
You can select an individual pair with something like this, assuming the markup is well behaved (i.e each input
has a label
before it):
//label[1] | //label[1]/following-sibling::input[1]
This will select the first label
and the following input
. However you want to select all such pairs. One way would be to first select all the label
nodes, and then for each label
select it and the following input.
labels = doc.xpath("//form/label")
labels.each do |l|
nodes = l.xpath(". | ./following-sibling::input[1]")
# nodes now contains a label-input pair...
end
I don’t think the wrap
method will work to add a div
element as an ancestor to each pair, as it will add the element to each member of the nodeset. You will probably have to move them manually, something like
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
Note that this method doesn’t move any text nodes, so you may find the whitespace of your document changes a bit.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments