Rendering custom data in the Custom Search element

April 13, 2010

Last year we introduced Rich Snippets in Custom Search, allowing you to define your own custom attributes that we'll index and return with your custom search results. A few months later we showed you how to render some of these rich snippets in your Custom Search element. Sure, this was a powerful way to let your visitors see thumbnails or interact with the results via actions. But we felt it was still too constrained. It is with great pleasure that today we're announcing that you now have full rendering control of all your metadata in the Custom Search element.

What do I mean? If a picture is worth a thousand words, an example is worth a thousand pictures. Here's a fully customized element, showcasing results and metadata from Scribd.com:

By  -  pages -  views  - last modified 


The results really jump out at you, huh? The thumbnails really help users see what they're looking at, but we've shown you those before. Same with the Download action links by each result. But never before could you include arbitrary, per-result metadata with your Google Custom Search results so easily. Notice the author, length, views and date information in each result. There's even an icon representing each result's document type.

So how can you add this to your page? Let's take a look.

First, you need to include custom attributes within your webpages, either via microformats, RDFa, or a special markup called PageMaps. A PageMap identifies specific attributes that Google recognizes and indexes, and then returns along with search results. Our prior blog post on Structured Custom Search tells you how to add PageMaps to your site. Please keep in mind that your site needs to be re-indexed, which can take some time. Therefore, your PageMaps might not show up immediately.

Once your custom attributes have been indexed, you're ready to tell the element how to render them. If you don't already have the element on your page, you can add it with a few lines:
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
// Load the Search API
google.load('search', '1');

// Set a callback to load the Custom Search Control when you page loads
google.setOnLoadCallback(
function(){
new google.search.CustomSearchControl('INSERT-YOUR-ID').draw('cse');
},
true);
</script>
<div id="cse"></div>
Here's the Scribd.com sample, with only the default rich snippet inclusion:

It looks okay, but let's make it better.

We have to override the default rendering, by adding this line to the onLoadCallback:
google.search.Csedr.addOverride("mysite_");
Now, create a div to hold all of the rendering information - put it right above the div that the element uses:
<div style="display:none">
</div>
Okay, now that everything's set up, it's time for the good part. First off, let's try shrinking the thumbnails a bit. Inside the div that was just added above, and tell the element to render the images at 48x48 pixels:
<div id="mysite_thumbnail">
<div data-if="Vars.thumbnail" class="gs-image-box gs-web-image-box">
<a class="gs-image" data-attr="{href:url, target:target}">
<img class="gs-image" data-attr="{src:thumbnail.src, width:48, height: 48}"/>
</a>
</div>
</div>
This replaces the default thumbnail rendering code with a similar version that sets the image dimensions directly.

Here's the same sample with the above code added:
Now it's time to add the new content and tweak the rendering of the basic content, too:
<div id="mysite_webResult">
<div class="gs-webResult gs-result"
data-vars="{longUrl:function() {
var i = unescapedUrl.indexOf(visibleUrl);
return i < 1 ? visibleUrl : unescapedUrl.substring(i);}}">

<table>
<tr>
<td valign="top">
<div data-if="Vars.richSnippet" data-attr="0"
data-body="render('thumbnail',richSnippet,{url:unescapedUrl,target:target})"></div>
</td>

<td valign="top">
<div class="gs-title">
<a class="gs-title" data-attr="{href:unescapedUrl,target:target}"
data-body="html(title)"></a>
</div>
<div class="gs-snippet" data-body="html(content)"></div>
<div class="gs-visibleUrl gs-visibleUrl-short" data-body="longUrl()"></div>
<div style="& Vars.richSnippet.document">
<img data-attr="{src:Vars.richSnippet.document.filetypeImage}">
By <span data-body="Vars.richSnippet.document.author"></span> -
<span data-body="Vars.richSnippet.document.pageCount"></span> pages -
<span data-body="Vars.richSnippet.document.viewCount"></span> views
- last modified <span data-body="Vars.richSnippet.document.timeAgo"></span>
</div>

<div data-if="Vars.richSnippet && Vars.richSnippet.action" class="gs-actions"
data-body="render('action',richSnippet,{url:unescapedUrl,target:target})"></div>
</td>
</tr>
</table>
</div>
</div>
Voila! The sample is now complete, and here's how it looks:

This example is a bit more complex. It shows how to use the render() function to call other code, and also how you can include any JavaScript logic. You can even define internal functions, such as longUrl().

Didn't quite catch all of that or want to do even more? Check out the documentation.

The best part of announcing new features like this is seeing what innovative implementations people come up with. Don't keep us waiting - come show off your handiwork in our support forum or IRC channel!
Posted by: David Gibson, Software Engineer and Adam Feldman, Product Manager