Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have following method which highlight matched word in text:

# Hightlight matched term
# Ex(for term: some):
#   "<span class="bold">Some</span> CEO Event"
def highlight_matched(matched_word, text)
  regex = matched_word.gsub(/\*|\"|\'/, "")
                       .split(" ")
                       .map { |s| "\\b#{s}" }.join('|')

  text.gsub(/(#{regex})/i, '<span class="bold">\1</span>')

Is there a better solution for that?

I am using it here:

"%s %s" %[highlight_matched(title[0..70]), content_tag(:span, caption, class: 'search-type')]
share|improve this question
Is text pure text or HTML? can you add to the question some asserts? – tokland Dec 18 '12 at 20:06
Text is HTML – regedarek Dec 18 '12 at 20:18
up vote 3 down vote accepted

If text may contain HTML (in that case text is a somewhat misleading variable name) you definitely should use a HTML parser. What about this? (adding a <p> as wrapper, otherwise xpath won't find parent text nodes).

require 'nokogiri'

def highlight(html, word)
  Nokogiri::HTML::fragment("<p>" + html + "</p>").tap do |doc|'.//text()').each do |text_node|
      new_contents = text_node.text.gsub(/\b(#{word})\b/i, '<span class="bold">\1</span>')

puts highlight('<p><a href="">Run Rabbit Run</a></p>', "run")
# <p><a href=""><span class="bold">Run</span>     
# Rabbit <span class="bold">Run</span></a></p>
share|improve this answer
hmm, it not works for me: – regedarek Dec 19 '12 at 14:22
sometimes text has HTML tags sometimes not, like above – regedarek Dec 19 '12 at 14:25
Here is my output: – regedarek Dec 19 '12 at 14:32
I am using 1.9.3and nokogiri 1.5.5 – regedarek Dec 19 '12 at 14:37
oh, I see, the problem is that xpath won't get parent text nodes. – tokland Dec 19 '12 at 15:41

You should use a better class name, such as highlighted or search-term or search-match.


Your solution will break anchors, or titles, or other hmtl properties:

# Yields: "Broken <a href='<span>cat</span>nip'>example</a>."
puts highlight_matched("cat", "Broken <a href=''>example</a>.")

Another potential problem

And if you repeatedly run it on the same text with a list of words, you will get nested replacements if you don't have the words sorted by size-descending and replace a word ("cat") that is a substring of a previous replacement ("catnip").

This might be, or not, a problem.
For me it was, because I was replacing with anchors and HTML does not allow nested anchors.
For you it might be, too, depending on how you wish to style the matches.

intermediate = highlight_matched("catnip", "I love catnip!")
# Yields: "I love <span><span>cat</span>nip</span>!"
puts highlight_matched("cat", intermediate)
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.