Libraries like jQuery have revolutionized the way that developers create sites, spurring the development of a large collection of ready-to-go scripts that you can just add to HTML pages. Microdata has the potential to take this revolution a step further, making it easier to create scripts that work seamlessly on any site, regardless of variations in the HTML templates.
For example, to display planned events on a map, jQuery plugins integrate with Google maps and make it easy to create an interactive map on your site. The only effort required on your part is to create a file in a structured format, such as JSON or CSV, that contains the locations. Having this data in a separate structured format can be a problem, though, if you also want to display the information in HTML on the page. To maintain the content in both the HTML and the data file, either you have to write a script that translates between the HTML on the page and the data file or you have to duplicate your effort. This approach can lead to the HTML and data files getting out of sync very quickly.
Keeping files in sync is one main advantage of using HTML data formats like microdata and RDFa—you can write the HTML as you normally would and then add a few attributes in the HTML tags to make it double as your data store. Because these HTML data formats are web standards developed and sanctioned by the WHATWG and W3C, people already build tools that understand how to extract data from the HTML, and you can easily reuse these tools on your site.
As microdata just started gaining traction recently, only a few plugins have been released with support. An example is the jQuery UI Map plug-in, which provides the kind of map described above.
Creating a Rich Snippet-ready event map
You can use one small segment of HTML to get both an interactive event map and better SEO.
As described in an earlier article (see Resources for a link), microdata can be used with terms from the Schema.org vocabulary to help search engines understand your content better. This approach allows the search engines to show Rich Snippets, which bring the most important information about the page into the search result itself.
The annotations you add to get Rich Snippets can pull double duty. In addition to better search results, you can also use them on your own page to provide a better display.
You'll use a jQuery plugin to do this. I include the relevant files in the example code download, which includes a slightly modified version of the plug-in.
Let's create a listing of DrupalCamp events throughout the world. To start, create an HTML page with two events as in Listing 1.
Listing 1. Basic HTML listing two events
<!DOCTYPE html>
<head>
<title>Upcoming DrupalCamps</title>
</head>
<body id="doc">
<h1>Upcoming DrupalCamps</h1>
<!-- Event 1: DrupalCamp Toulouse -->
<div>
<h2><a href="http://example.com/drupalcamp_fr">DrupalCamp Toulouse 2011</a></h2>
<img src='images/drupalicon_fr.png' />
<p>Vous l’attendiez tous, le DrupalCamp Toulouse aura bien lieu,
réuni avec 2 autres événements sous la bannière
"Capitole du Libre": le DrupalCamp, une Ubuntu Party, une Akademy
(évènement KDE).</p>
<div>
ENSEEIHT, Toulouse, Haute-Garonne, FR
</div>
<div>
<time datetime="2011-11-26T09:00:00+01:00">November 26, 10:00am</time> –
<time datetime="2011-11-27T17:00:00+01:00">November 27, 6:00pm</time>
</div>
</div>
<!-- Event 2: DrupalCamp Ohio -->
<div>
<h2><a href="http://example.com/drupalcampohio">Drupalcamp Ohio 2011</a></h2>
<img src='images/drupalicon_oh.png' />
<p>The Central Ohio Drupal User Group (CODUG) is proud to announce
Ohio’s first Drupalcamp. On Saturday, December 3rd, we’ll hold an
all-day camp with keynote speaker, breakout sessions and Birds of Feather groups at The
Ohio State University’s Nationwide and Ohio Farm Bureau 4-H Center.</p>
<div>
The Ohio State University, Columbus, Ohio, US
</div>
<div>
<time datetime="2011-12-03T09:00:00-05:00">December 3, 9:00am</time> –
<time datetime="2011-12-03T17:00:00-05:00">5:00pm</time>
</div>
</div>
</body>
</html>
|
Notice that you marked up the dates with a special element. This element is the new time element, which is part of the core HTML5 standard. It enables you to add exact times to pages so they can be easily parsed and used by applications. In this example, Google uses these time elements to choose the next three events from the page to display in Rich Snippets. Note that at the time of publishing, there is discussion about whether to change the time element to a more generic data element. If this goes through, "time" would be replaced by "data" and "datetime" would be replaced by "value" in the code below.
The datetime is formed starting with the date in YYYY-MM-DD format. Next, a "T" separates the date from the time, which is given in HH:MM:SS format. Then the offset from Universal Coordinated Time (UTC) is given. For example, in winter France is 1 hour ahead of UTC, so the offset adds '+01:00' to the end of the datetime string. Ohio is 5 hours behind UTC in winter, so the offset adds '-05:00' to the end of the datetime string. This addition results in the machine-readable value "2011-12-03T09:00:00-05:00" for 9am Central Standard Time on December 3, 2011.
Save the file to the jquery-ui-map/demos directory on your web server and load it in your browser. Figure 1 shows the details for both events described in Listing 1.
Figure 1. Basic page listing two events
Making the events Rich Snippet-ready
The page with the time element includes a little bit of machine-understandable information. Now see if this addition has changed how Google displays the results. Go to Google's Rich Snippets Testing Tool and enter the URL for your page (see Figure 2). Rather than event details, you see preview generation errors that the page has no authorship markup, rich snippet markup, or authorship information.
Figure 2. No results for basic HTML in the Rich Snippets testing tool
Even though Google knows that your page lists times, it doesn't know that these times are start and end times for events. To help Google understand this, you need to add few Schema.org terms (see Listing 2). Publishing Schema.org markup with microdata is covered in more detail in a previous article (see Resources for a link).
Listing 2. Make the <div> of each event into an Event item
<!-- Event 1: DrupalCamp Toulouse --> <div itemscope itemtype="http://schema.org/Event"> ... </div> <!-- Event 2: DrupalCamp Ohio --> <div itemscope itemtype="http://schema.org/Event"> ... </div> |
Next add the url and name properties to the event. Note that you add a <span> around the name to have a place to add the
itemprop
attribute. You also add the image property. You don't need the image for this Rich
Snippet, but you will use it later.
Listing 3. Add the url and name properties to the event
<!-- Event 1: DrupalCamp Toulouse --> <div itemscope itemtype="http://schema.org/Event"> <h2><a href="http://example.com/drupalcamp_fr" itemprop="url"><span itemprop="name">DrupalCamp Toulouse 2011</span></a></h2> <img itemprop="image" src='images/drupalicon_fr.png' /> ... </div> <!-- Event 2: DrupalCamp Ohio --> <div itemscope itemtype="http://schema.org/Event"> <h2><a href="http://example.com/drupalcampohio" itemprop="url"><span itemprop="name">Drupalcamp Ohio 2011</span></a></h2> <img itemprop="image" src='images/drupalicon_oh.png' /> ... </div> |
Now you can add a start time and (optionally) an end time. Because you used the time element, you already have a machine-readable value for the start and end times. You just need to add the Schema.org terms to let Google know which is which (see Listing 4).
Listing 4. Add the startDate and endDate properties on the time elements
<!-- Event 1: DrupalCamp Toulouse -->
<div itemscope itemtype="http://schema.org/Event">
...
<div>
<time itemprop="startDate" datetime="2011-11-26T09:00:00+01:00">November 26,
10:00am</time> –
<time itemprop="endDate" datetime="2011-11-27T17:00:00+01:00">November 27,
6:00pm</time>
</div>
</div>
<!-- Event 2: DrupalCamp Ohio -->
<div itemscope itemtype="http://schema.org/Event">
...
<div>
<time itemprop="startDate" datetime="2011-12-03T09:00:00-05:00">December 3,
9:00am</time> –
<time itemprop="endDate"
datetime="2011-12-03T17:00:00-05:00">5:00pm</time>
</div>
</div>
|
Test the page again in the Testing Tool. Because the URLs point to a different domain than the base URL, you see warnings that prevent the Rich Snippet from displaying (Figure 3).
Figure 3. Warning that prevent the Rich Snippet display
Google requires that the links to the events point to pages within the same domain. This requirement is designed to prevent spamming practices. If you search and replace example.com with your domain and test again, you should see the Rich Snippet display as in Figure 4.
Figure 4. Rich Snippet displaying both events with their start dates
Note that even if the testing tool shows you a Rich Snippet, you are not guaranteed to have Rich Snippet displays in real search results. You must apply to Google to have your site reviewed before Google will enable Rich Snippet output for your pages in search results. For more information on this, see the FAQ.
Because the DrupalCamps are held all over the world, your visitors will want a map showing which events are within a reasonable travel distance. To get a map, just add a few more pieces of information.
First, set up the map for display. Add a <div> in the document to place the map below the page heading and above the event listings (Listing 5).
Listing 5. Adding the map <div>.
<body id="doc">
<h1>Upcoming DrupalCamps</h1>
<div>
<div id="map_canvas"></div>
</div>
<!-- Event 1: DrupalCamp Toulouse -->
|
You need to add some JavaScript files to turn this <div> into an interactive map. Also add some CSS files, which give a height to the
#map_canvas
<div> so you can see it. The local files are included in the source code (see Download).
Listing 6. Adding the external CSS, JavaScript libraries, and inline jQuery
<head> <title>Upcoming DrupalCamps</title> <!-- External Stylesheets --> <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.2.0/build/cssreset/reset-min.css" /> <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.2.0/build/cssbase/base-min.css" /> <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.2.0/build/cssfonts/fonts-min.css" /> <link rel="stylesheet" type="text/css" href="css/main.css" /> <!-- Google Maps API and jQuery, served by Google --> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js"></script> <!-- jQuery UI Maps files for placing markers and for parsing Microdata --> <script type="text/javascript" src="../web/jquery.fn.gmap.js"></script> <script type="text/javascript" src="../ui/jquery.ui.map.services.js"></script> <script type="text/javascript" src="../ui/jquery.ui.map.microdata.js"></script> ... |
Then add a block of jQuery, which gets the
#map_canvas
div when the page initializes and turns it into a map (see
Listing 7). You'll add more to this function later.
Listing 7. Initializing the map
...
<!-- Grab the #map_canvas div and turn it into a map -->
<script type="text/javascript">
$(function() {
map = $('#map_canvas');
map.gmap().bind('init', function () {
});
});
</script>
</head>
|
When you reload the page, you will see the map (in Figure 5). It doesn't yet have any events marked; you still need to add a little more information for those to show up.
Figure 5. A basic map without any markers
Adding the locations in microdata
You need to add the locations of the events in microdata before you can add the markers to the map. For now, treat each address as a single string. However, addresses have multiple distinct parts, and you want to express this structure in the microdata to easily and consistently get the right values, even if you change the HTML structure later.
First, add the location property, which takes a Place for its value (see Listing 8).
Listing 8. Adding the location property to the event
<!-- Event 1: DrupalCamp Toulouse -->
<div itemscope itemtype="http://schema.org/Event">
...
<div itemprop="location" itemscope itemtype="http://schema.org/Place">
ENSEEIHT, Toulouse, Haute-Garonne, FR
</div>
...
</div>
|
The Place can have a name and a postal address. A postal address is an item with its own properties, such as the street address,
the city (addressLocality), state (addressRegion), and addressCountry, which should be the two-letter ISO 3166-1 alpha-2 country code.
Listing 9
breaks these properties out using span tags.
Listing 9. Using span tags
<!-- Event 1: DrupalCamp Toulouse -->
<div itemscope itemtype="http://schema.org/Event">
...
<div itemprop="location" itemscope itemtype="http://schema.org/Place">
<span itemprop="name">ENSEEIHT</span><br />
<span itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
<span itemprop="streetAddress">Rue Sylvain Dauriac</span><br />
<span itemprop="addressLocality">Toulouse</span>,
<span itemprop="addressRegion">Haute-Garonne</span>,
<span itemprop="addressCountry">FR</span>
</span>
</div>
...
</div>
|
You can format the address for the other event in the same way.
Placing the locations on the map
You're ready to add the markers to the map. First, parse the microdata on the
page into an object that you can use. To do this, specify which type of top-level item is
the source of the data that you want. Top-level items in microdata can be seen as root items; they are items that are not a property of any other item but do have properties of their own. Specify
http://schema.org/Event. This returns an object for each of the
Events (see Listing 10).
Listing 10. Parsing the microdata into an object
<script type="text/javascript">
$(function() {
map = $('#map_canvas');
map.gmap().bind('init', function () {
// Process the microdata for each Event into an object.
map.gmap('microdata', 'http://schema.org/Event', function(result, item, index) {
window.console.log(result);
});
});
});
</script>
|
You can see the object that is created from the microdata in the JavaScript console in browsers such as Firefox and Chrome. Each item in microdata is translated into an object with its own properties object, which contains any of the itemprop values from that item. Because properties in microdata can be multivalued, itemprop values are handled as arrays (see Figure 6).
Figure 6. Examining the object in the JavaScript console of Chrome
This object has all of the properties you need to get the geocoordinates of the location, which you need to place the marker for the event. To get this information, extract the parts of the address from the object and combine them into an address string that Google will understand. You can then use Google's Geocoder to get the coordinates for this address. The jQuery UI Map library provides a wrapper function that you can use to run the Geocoder request.
Note that if you get an undefined error, double check to make sure that you have all of the required properties on all of the Events that are listed (see Listing 11).
Listing 11. Running the Geocoder request for the address
<script type="text/javascript">
$(function() {
map = $('#map_canvas');
map.gmap().bind('init', function () {
// Process the Microdata for each Event into an object.
map.gmap('microdata', 'http://schema.org/Event', function(result, item, index) {
// Traverse from the Event to the Place and from the Place to the
// Address to get the properties.
var placeName = result.properties.location[0].properties.name[0];
var streetAddress =
result.properties.location[0].properties.address[0].properties.streetAddress[0];
var city =
result.properties.location[0].properties.address[0].properties.addressLocality[0];
var state =
result.properties.location[0].properties.address[0].properties.addressRegion[0];
var country =
result.properties.location[0].properties.address[0].properties.addressCountry[0];
// Join the address parts into a comma-separated string.
var address = [placeName, city, state, country].join(', ')
// Run the Geocoder request for the address.
map.gmap('search', {'address': address } , function(result, status) {
window.console.log(result);
// We will place the marker here.
});
});
});
});
</script>
|
If you look at the result in the console, you will see that the service has returned one or more locations that match your address. You will only use the first one as this is the best match. The object has a geometry property that contains the geocoordinate information that you need (see Listing 12).
Listing 12. Placing the marker
<script type="text/javascript">
$(function() {
map = $('#map_canvas');
map.gmap().bind('init', function () {
// Process the Microdata for each Event into an object.
map.gmap('microdata', 'http://schema.org/Event', function(result, item, index) {
// Traverse from the Event to the Place and from the Place to the
// Address to get the properties.
var placeName = result.properties.location[0].properties.name[0];
var streetAddress =
result.properties.location[0].properties.address[0].properties.streetAddress[0];
var city =
result.properties.location[0].properties.address[0].properties.addressLocality[0];
var state =
result.properties.location[0].properties.address[0].properties.addressRegion[0];
var country =
result.properties.location[0].properties.address[0].properties.addressCountry[0];
// Join the address parts into a comma-separated string.
var address = [placeName, city, state, country].join(', ')
// Run the Geocoder request for the address.
map.gmap('search', {'address': address } , function(result, status) {
if (status == google.maps.GeocoderStatus.OK) {
// Create a LatLng object.
var lat = result[0].geometry.location.lat();
var lng = result[0].geometry.location.lng();
var latlng = new google.maps.LatLng(lat, lng);
// Place the marker.
var markerOptions = {
'bounds':true,
'position': latlng
};
map.gmap('addMarker', markerOptions);
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
});
});
});
</script>
|
When you reload the page, you should see the location markers as in Figure 7.
Figure 7. Markers placed on the map
The map is even more helpful if users can click on the marker to get event details, such as the date and a link to the event. Adding this functionality isn't hard to do.
First, extract the additional information that you want about the event from the parsed microdata (see Listing 13).
Listing 13. Getting the supplementary information
<script type="text/javascript">
$(function() {
map = $('#map_canvas');
map.gmap().bind('init', function () {
// Process the Microdata for each Event into an object.
map.gmap('microdata', 'http://schema.org/Event', function(result, item, index) {
var eventName = result.properties.name[0];
var logo = result.properties.image[0];
var url = result.properties.url[0];
var start = result.properties.startDate[0];
...
});
});
</script>
|
Before you add the marker to the map, create a block of HTML with the event details (see Listing 14).
Listing 14. Creating the HTML for the info window
// Run the Geocoder request for the address.
map.gmap('search', {'address': address } , function(result, status) {
if (status == google.maps.GeocoderStatus.OK) {
// Create a LatLng object.
var lat = result[0].geometry.location.lat();
var lng = result[0].geometry.location.lng();
var latlng = new google.maps.LatLng(lat, lng);
var eventDetails = '';
eventDetails += '<div class="iw">';
eventDetails += '<img src="'+logo+'"></img>';
eventDetails += '<h2><a href="'+url+'">'+eventName+'</a></h2>';
eventDetails += new Date(start).toDateString();
eventDetails += '</div>';
// Place the marker.
...
|
Then add the event details to the marker you're creating. Use jQuery's
.click
to say which function you want to run when someone clicks the marker. In the function, use the
openInfoWindow
helper function provided by the jQuery UI Maps library to build the window and simply pass
in your HTML as the content for that window (see
Listing 15).
Listing 15. Adding the Info Window as a click event
// Place the marker.
...
map.gmap('addMarker', markerOptions).click( function() {
map.gmap('openInfoWindow', { 'content': eventDetails }, this );
});
|
Reload after this task is done and then click the marker. You'll see the window pop up with the event details (see Figure 8).
Figure 8. Click event on marker
To improve the appearance, add a few CSS rules. I added these in the downloadable example code (see Download). In the end, your map resembles Figure 9.
Figure 9. Final map
Microdata makes it easier to use the data on your page in dynamic ways, such as on Google maps, using jQuery plugins. The microdata specification talks about the generic scripts which you can use as plug-ins on different, unrelated sites. Microdata also makes it easier to combine data from multiple different sites to create new applications. In the next article, I will demonstrate how you can use Drupal to create such an application.
| Description | Name | Size | Download method |
|---|---|---|---|
| Article source code | jquerymicrodatapt1_source.zip | 464KB | HTTP |
Information about download methods
Learn
- Make HTML5 microdata useful, Part 2: Next generation aggregation with microdata (Lin Clark, developerWorks, March 2012): Create a decentrally managed site with microdata and Drupal that enables a collaborating group of site owners to easily hook up their sites and share content on a centralized site in the second part of this series.
- ISO 3166-1 (Wikipedia): Find two-letter alpha-2 country codes.
- Schema.org: Learn more about this collection of schemas, which are HTML tags that webmasters can use to mark up their pages in ways recognized by major search providers.
- Getting started with schema.org: In these tutorials, learn how to place schema.org terms on the Schema.org site.
- Itemtype URL: Find the properties that you can use on a schema.org item by visiting the itemtype URL (http://schema.org/Movie, for example).
- The Semantic web, Linked Data and Drupal, Part 1: Expose your data using RDF (Lin Clark, developerWorks, April 2011): Make your web data more interoperable and your data sharing more efficient. An example shows how to use Drupal 7 to publish Linked Data by exposing content with RDF.
- The Semantic web, Linked Data and Drupal, Part 2: Combine linked datasets with Drupal 7 and SPARQL Views (Stéphane Corlosquet and Lin Clark, developerWorks, May 2011): Learn to use the existing Linked Data available today on the web of data, and how to enrich a Drupal 7 site with data coming from different endpoints.
- Scientific American article on the Semantic web: Read this seminal article by Tim Berners-Lee, James Hendler, and Ora Lassila.
- Linked Data: Read the ReadWriteWeb interview about linked data with Tim Berners-Lee.
- Linked Data Design Issues: Learn more about linked data from Tim Berners-Lee.
- Rich snippets (microdata, microformats, and RDFa) - Webmaster Tools Help: Learn more about Google Rich snippets and how to label your web content to indicate clearly the data type, such as a restaurant name, an address, or a rating.
- Implement Semantic web standards in your Web site (Rob Crowther, developerWorks, May 2008): Create a simple social networking site using PHP and MySQL, which implements Semantic web standards such as hCard and Friend of a Friend (FOAF) as part of a semantic Uniform Resource Identifier (URI) scheme.
- FOAF Vocabulary Specification 0.98: Explore the FOAF language, defined as a dictionary of named properties and classes using W3C's RDF technology.
- Dublin Core Metadata Initiative (DCMI): Learn about this open organization engaged in the development of interoperable metadata standards that support a broad range of purposes and business models.
- SIOC (Semantically-Interlinked Online Communities) Core Ontology Specification: Learn the main concepts and properties required to describe information from online communities (such as message boards, wikis, or weblogs) on the Semantic web.
- SPARQL Explorer for http://dbpedia.org/sparql: Try a demonstration query interface available on the web.
- developerWorks Web development zone: Find articles covering various web-based solutions.
- New to XML? Get the resources you need to learn XML.
- XML area on developerWorks: Find the resources you need to advance your skills in the XML arena, including DTDs, schemas, and XSLT. See the XML technical library for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
- IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
- developerWorks on Twitter: Join today to follow developerWorks tweets.
- developerWorks podcasts: Listen to interesting interviews and discussions for software developers.
- developerWorks on-demand demos: Watch demos ranging from product installation and setup for beginners to advanced functionality for experienced developers.
Get products and technologies
- jQuery UI Map: Download the latest version and use jQuery click events on a map and markers to populate a map from microformats, RDFa or microdata on your site.
- Google's Rich Snippets Testing Tool: Test for Rich Snippets.
- Google's Rich Snippets Testing Tool: Test your schema.org markup.
- Live Microdata testing tool: Get another tool, created by Opera developer Philip Jägenstedt, for testing microdata.
- IBM product evaluation versions: Download or explore the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
- developerWorks profile: Create your profile today and set up a watchlist.
- XML zone discussion forums: Participate in any of several XML-related discussions.
- The developerWorks community: Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.

Lin Clark is a Drupal developer specializing in Linked Data. She is the maintainer of multiple Drupal modules, such as Microdata and SPARQL Views, and is an active participant in the W3C’s HTML Data Task Force and Drupal's HTML5 initiative. She attended Carnegie Mellon University and is finishing a research masters degree at the Digital Enterprise Research Institute at NUI Galway. More information is available at lin-clark.com.




