Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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>')
end

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
add comment

2 Answers

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|
    doc.search('.//text()').each do |text_node|
      new_contents = text_node.text.gsub(/\b(#{word})\b/i, '<span class="bold">\1</span>')
      text_node.replace(new_contents)
    end
  end.xpath("p").inner_html
end

puts highlight('<p><a href="http://runner.com">Run Rabbit Run</a></p>', "run")
# <p><a href="http://runner.com"><span class="bold">Run</span>     
# Rabbit <span class="bold">Run</span></a></p>
share|improve this answer
    
hmm, it not works for me: gist.github.com/b5015d8932990efc786b –  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: gist.github.com/a03d7b44aa70cb6e0345 –  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
add comment

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

Bug

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

# Yields: "Broken <a href='http://www.example.org/<span>cat</span>nip'>example</a>."
puts highlight_matched("cat", "Broken <a href='http://catnip.com'>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
add comment

Your Answer

 
discard

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.