Daniel
Daniel

Reputation: 225

Searching for text in all nodes with xpath

I'm trying to find words in a fragment of html to replace them with a href. Somehow can't find the right path to use for Xpath. Example:

require 'nokogiri'

html = '
<p>A paragraph Apple<p>
<span>Apple</span>
<ul>
  <li>Item 1</li>
  <li>Apple <strong>Apple</strong></li>
  <li>Apple</li>
  <li>Orange</li>
</ul>
<p><i>Apple</i>Apple</p>'

doc = Nokogiri::HTML.fragment(html)
doc.xpath('.//*[text()="Apple"]').each do |node|
  puts "\n"
  puts node.name
  puts node.content
  puts node.replace('REPLACED')
end

puts doc.to_html

Result:

span
Apple
REPLACED

strong
Apple
REPLACED

li
Apple
REPLACED

i
Apple
REPLACED
<p>A paragraph Apple</p><p>
REPLACED

</p><ul>
  <li>Item 1</li>
  <li>Apple REPLACED</li>
  REPLACED
  <li>Orange</li>
</ul>
<p>REPLACEDApple</p>

So the words in the root p elements are not replaced and one in the li is left. Which path should i use in this case to search in root and all children? Reading on a page like this .//* should be the path used to search in root and child nodes. Any ideas on how to handle this correctly with nokogiri or xpath?

Thanks in advance!

Upvotes: 1

Views: 290

Answers (1)

Eric Duminil
Eric Duminil

Reputation: 54223

You're looking for nodes where the whole text is equal to "Apple", not nodes which contain "Apple"

html = '
<p>A paragraph Apple<p>
<span>Apple</span>
<ul>
  <li>Item 1</li>
  <li>Apple <strong>Apple</strong></li>
  <li>Apple</li>
  <li>Orange</li>
</ul>
<p><i>Apple</i>Apple</p>
<Apple>Dont replace!</Apple>
'

doc = Nokogiri::HTML.fragment(html)

doc.traverse do |node|
  if node.text?
    node.content = node.content.gsub('Apple', 'REPLACED')
  end
end

puts doc.to_html

It outputs :

<p>A paragraph REPLACED</p><p>
<span>REPLACED</span>
</p><ul>
  <li>Item 1</li>
  <li>REPLACED <strong>REPLACED</strong>
</li>
  <li>REPLACED</li>
  <li>Orange</li>
</ul>
<p><i>REPLACED</i>REPLACED</p>
<apple>Dont replace!</apple>

Upvotes: 1

Related Questions