Recipe 8.9. Searching for and Highlighting Text Dynamically


Problem

You want to let users search a body of text on a page while highlighting matches for the search term as they type it.

Solution

Use the observe_field Prototype helper to send continuous Ajax search terms to the server for processing. For example, suppose you have an application that stores articles that you want users to be able to search. Assuming you have a Rails application created and configured to connect to a database, create an Article model with:

$ ruby script/generate model Article              

Then create and load a migration to instantiate the articles table:

db/migrate/001_create_articles.rb:

class CreateArticles < ActiveRecord::Migration   def self.up     create_table :articles do |t|       t.column :title, :string       t.column :body, :text     end   end   def self.down     drop_table :articles   end end

You'll also need to include the Prototype JavaScript library. Do that by creating the following layout template:

app/views/layouts/search.rhtml:

<html> <head>   <title>Search</title>   <%= javascript_include_tag :defaults %>   <style type="text/css">     #results {       font-weight: bold;       font-size: large;       position: relative;       background-color: #ffc;       margin-top: 4px;       padding: 2px;     }   </style>  </head> <body>   <%= yield %> </body> </html>

The index view of the application defines an observed field with the Prototype JavaScript helper function observe_field. This template also contains a div tag where search results are rendered.

app/views/search/index.rhtml:

<h1>Search</h1> <input type="text" > <%= observe_field("search", :frequency => 1,                             :update => "content",                             :url => { :action => "highlight"}) %> <div >   <%= render :partial => "search_results",               :locals => { :search_text => @article } %> </div>

As with all Ajax interactions, you need to define code on the server to handle each XMLHttpRequest. The highlight action of the following Search Controller contains that code, taking in search terms and then rendering a partial to display results:

app/controllers/search_controller.rb:

class SearchController < ApplicationController   def index   end   def highlight     @search_text = request.raw_post || request.query_string     @article = Article.find :first,                      :conditions => ["body like ?", "%#{@search_text}%"]                                  render :partial => "search_results",             :locals => { :search_text => @search_text,                          :article_body => @article.respond_to?('body') ?                                          @article.body : "" }   end end

Finally, the search results partial simply calls to the highlight helper, passing it local variables containing the contents of the article body (if any) along with the search text that should be highlighted.

app/views/search/_search_results.rhtml:

<p>   <%= highlight(article_body, search_text,        '<a href="http://en.wikipedia.org?search=\1"             title="Search Wikipedia for \1">\1</a>') %> </p>

The partial not only highlights each occurrence of the search text, but it creates a link to Wikipedia's search, passing the same search text.

Discussion

The solution demonstrates a cool effect called live search. Making it work is really a combination of a number of components, all working together to provide real-time, visual feedback about the search.

Here's how it works: a user navigates to the index view of the Search Controller. There, she finds a search box waiting for input. That text input field is configured to observe itself. As the user enters text, an Ajax call is sent to the server ever second (the interval is specified by the :frequency option).

For each one of these Ajax requests, the highlight action of the Search Controller is invoked. This action takes the text from the raw post and looks up the first article in the database that contains the text being searched for. Next, the search_results partial is rendered by the highlight action, with the search text and article body being passed in.

Finally, the partial _search_results.rhtml expects to receive the body text of the article found by Search#highlight along with the same search text that the user is in the process of entering. The partial processes the search text along with the search results using the view helper, highlight.

The highlight view helper takes a body of text as its first argument, and a phrase as the second. Each occurrence of the phrase within the body of text is surrounded with <strong> tags (by default). To treat the matched text differently (as the solution does) you pass a third argument to highlight, which is called the highlighter. The highlighter is just a string with an occurrence of "\1" somewhere in it. "\1" is substituted for the matched text. This way you can create whatever kind of treatment you like. The solution wraps the occurrences of the search terms in a hyperlink that points to Wikipedia.

Figure 8-9 shows the results of the solution's search form, highlighting words within the text that match the search term.

Figure 8-9. A search form that dynamically highlights matched words


See Also

  • Section 8.11"




Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net