JavaScript script to access twitter search API and update a news ticker at the bottom of the browser window. Very easy to embed and customise.
See example: click here.
Also packaged as a Rails plugin:
script/plugin install git://github.com/osahyoun/twitter_ticker.git
script/generate twitter_ticker
Include /javascripts/twitter_ticker.js in your layout (after prototype and scriptaculous). Somewhere in a view use:
<%= display_twitter_ticker(:query => 'whales') %>
See README for more options.
It has been so long since I wrote part 1 to this tutorial, I've since lost the demo app I was creating. I had to start again, and the views and controller code are different. Best if you get the accompanying code from GitHub and review the controller/view code directly before continuing with this second part.
git://github.com/osahyoun/ajaxed-tabbed-navigation-using-rails.git
To recap, In Part 1 we created a simple controller with some custom routes for displaying a bunch of related content pages. In our views with created a simple tabbed navigation.
Our Enhanced User-Experience will be slapped over the current basic common or garden version of our site. We don't want to ignore those without JavaScript enabled, nor do we want to write JavaScript directly into the view layer/html (messy, hard-to-maintain and not very portable). Never mix your drinks.
For JavaScript assistance I'll be using the very fine Prototype JavaScript library.
Working in application.js we'll create a class for handling the Ajax behaviour. Let it be called Tabs:
var Tabs = Class.create({
});
To start, I'll set an event listener on the tabbed menu (a basic ul list of links), so we can catch those mouse clicks when they occur:
var Tabs = Class.create({
initialize: function(){
$$('ul#nav').first().observe('click', (
function(event) {
if (event.element().tagName == 'A')
event.stop();
alert(event.element());
}
}).bind(this) );
}
});
// In other words: Fetch ($$()) from the dom all ul elements with an id of 'nav', and pluck the first() from the array.
// Now observe() the element for any clicks.
// If you detect a click, hand me some click info as 'event'.
// If the element() that created the 'event' has the same tagName as a link then stop() the link from successfully making an HTTP request, and alert() us to the element() which caused the event in the first place.
Before we can check if our listener works, we need to instantiate an instance of the Tabs class. We should do this when the web page has loaded. Prototype makes this easy with:
// Add this to the bottom of application.js
document.observe('dom:loaded', function () { new Tabs(); });
// In other words: Observe the dom, and create a new instance of Tabs() when the dom has loaded.
Run this in your browser and click on a tab. I hope you get an alert window, containing the link you clicked on.
Moving on. Next we'll create a method which writes to the dom the class name 'active' to the parent element of any clicked link (it should be an li element). We'll call this method whenever a link is clicked:
initialize: function(){
$$('ul#nav').first().observe('click', (
function(event) {
if (event.element().tagName == 'A') {
event.stop();
var element = event.element();
this.make_link_active(element);
}
}).bind(this)
)
},
make_link_active: function(link) {
$$('ul#nav li').invoke('removeClassName', 'active');
link.up(0).addClassName('active');
}
// In other words: Please fetch all li elements, children to ul#nav.
// With each li element remove the class 'active', if it exists.
// When you're done, traverse one element up(0) from the link I gave to you and addClassName('active') to it.
Save and run. Did it work? I hope yes, because now for the Ajax. Prototype’s Ajax support is exceptional, and has lots to offer. We're going to use just one method offered by the Ajax class (Ajax.Updater)
fetch_content: function(url) {
new Ajax.Updater('content', url, {
method: 'get',
onLoading: function() { $('spinner').show(); },
onComplete: function(){ $('spinner').hide(); }
});
}
// In other words: Ajax Update div#content with whatever content the server sends in answer to your request.
// Send your request to the url I gave to you.
// onLoading show() us a spinner.
// onComplete hide() the spinner.
Behind the scenes, there's a lot going on. It shouldn't be this easy, but it is. Thank-you Prototype.
Notice the spinner graphic. I've inserted it directly into the html with 'display:none' embedded into the element. For custom spinners visit ajaxload.
Run our new code, click on a tab... whoops!... notice the server is sending back view content plus layout! Let's fix that. In the mammals controller we'll determine whether to render a layout with the view depending on the request type.
class MammalsController < ApplicationController
layout :determined_by_response
............
protected
def determined_by_response
if request.xhr?
return false
else
'application'
end
end
That should be all. Our Tabs class is small, with only three methods, but it does the job. The tabbed navigation works with and without JavaScript. Our JavaScript is tucked away in its own file, far away from the html, where it remains easily maintainable for any future improvements/functionality we may want to add. You can also share your Tabs class with friends and family, who need only include the file into their HTML pages for Ajax goodness on their tabbed lists (so long as their menu is a list with an id 'nav', and their server can respond appropriately to the Ajax requests.
Perhaps in a future iteration, we can pass parameters to the class allowing it to deal with different types of menu structure.
Tested only on FF.
Developing a horizontal tabbed menu with Ajax functionality with Ruby on Rails, provides us with a fine example of why unobtrusive JavaScript is important, and worth bothering with.
I'll quickly set up a skeleton of a rails app with an 'about us' section showing several related pages with information about something or another.
$ rails tabs
$ script/generate controller about page_1 page_2 page_3 page_4
class AboutController < ApplicationController
#specify layout
layout 'application'
#I don't need to define any methods, Rails will simply go looking for the called page in the 'about' folder under 'views'
end
My layout 'application' is minimal so as to keep things simple.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
# be sure to include javascript defaults, we'll be using Prototype
<%= javascript_include_tag :all, :cache => true %>
# ...and a style sheet
<%= stylesheet_link_tag 'screen' %>
<title>Tabs</title>
</head>
<body>
<div id="content">
# Tabbed navigation is appropriately represented in HTML as an unordered list.
<ul id="tabnav">
# To help keep my template clean, I'll call a helper method (yet to be written), for generating a link.
<%= link('page_1', 'Page One') %>
<%= link('page_2', 'Page Two') %>
<%= link('page_3', 'Page Three') %>
<%= link('page_4', 'Page Four') %>
</ul>
<div id="tabContent">
<%= yield %>
</div>
</body>
</html>
Here is my helper method in about_helper.rb:
# helpers/about_helper.rb
# Method takes a page id and label and generates a link inside a list element, attaching a class name of 'active' on list element for active link.
def link(id, label)
"<li#{ ' class="active"' if controller.action_name == id}>#{link_to(label, send('about_' + id + "_path"))}</li>"
end
I'll need to write some routes before we have something that works:
# config/routes.rb
ActionController::Routing::Routes.draw do |map|
def map.controller_actions(controller, actions)
actions.each do |action|
self.send("#{controller}_#{action}", "#{controller}/#{action}", :controller => controller, :action => action)
end
end
map.controller_actions 'about', %w[page_1 page_2 page_3 page_4]
end
And now for a lick of paint:
/* screen.css */
body {
font: 100% verdana, arial, sans-serif;
background-color: #fff;
margin: 50px;
}
ul#tabnav {
text-align: left;
margin: 1em 0 1em 0;
font: bold 11px verdana, arial, sans-serif;
border-bottom: 1px solid #6c6;
list-style-type: none;
padding: 3px 10px 3px 10px;
}
ul#tabnav li {
display: inline;
}
ul#tabnav li.active {
border-bottom: 1px solid #fff;
background-color: #fff;
}
ul#tabnav li.active a {
background-color: #fff;
color: #000;
position: relative;
top: 1px;
padding-top: 4px;
}
ul#tabnav li a {
padding: 3px 4px;
border: 1px solid #6c6;
background-color: #cfc;
color: #666;
margin-right: 0px;
text-decoration: none;
border-bottom: none;
}
ul#tabnav a:hover {
background: #fff;
}
Within the 'about' folder under views, you'll find several pages created earlier that need some dummy content. After adding some lorem text I kick-start mongrel and visit http://0.0.0.0:3000/about/page_1:

Clicking through each tab works like a charm. I now have a simple, but functionally sound tab menu device. I'll consider adding a behavioral layer on top of what I've created, in order to provide Ajax functionality, and will write about it very soon in part two.
I'll create a demo app called 'leaf recipes', a resource for salad lovers, where people can share favourite salad recipes:
$ rails salad
$ cd salad
Now for the recipe resource. My first iteration will be simple, all I want is a title and a description:
$ script/generate scaffold recipe title:string description:text
$ rake db:migrate
Now some content. I'll do this through the console:
$ script/console
>> Recipe.create(:title => 'Crunchy Romaine Toss')
>> Recipe.create(:title => 'Roquefort Pear Salad')
>> Recipe.create(:title => 'Cranberry Spinach Salad')
>> exit
Fire up webrick and visit http://localhost:3000/recipe, which displays an index view, listing the three salads I just created.
I want my JavaScript widget to access my recipe resource with the same URL, but instead of returning HTML, I want it to spit out some JavaScript which the client browser can interpret. I need to visit the recipe controller:
app/controllers/recipes_controller.rb
I'm interested in the index method:
def index
@recipes = Recipe.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @recipes }
end
end
The respond_to block is ready to respond to requests for html and xml. My widget's going to want JavaScript. I'll add it to the block:
def index
@recipes = Recipe.find(:all)
respond_to do |format|
format.js # index.js.erb - this is where I'll craft my JavaScript
format.html # index.html.erb
format.xml { render :xml => @recipes }
end
end
I'll create a corresponding view template:
app/views/recipes/index.js.erb
var txt = ''
txt += "<div id='salads_widget'>";
txt += "<h2>Fine green salads from salads.com</h2>";
txt += "<ul>";
<% for recipe in @recipes do %>
txt += "<li>";
txt += "<%= escape_javascript(link_to recipe.title, recipe_url(recipe)) %>";
txt += "</li>"
<% end %>
txt += "</ul>"
txt += "</div>"
document.write(txt);
Actual JavaScript code is minimal. Past txt += all we have is HTML markup with some erb template code for looping through the @recipes instance variable assigned in the recipes controller and displaying the title attribute within an HTML list. All of this is assigned to a JavaScript variable I've named txt which is eventually written to the client's web page with document.write(txt).
Our (very basic) JavaScript widget is ready for public consumption. Here's my 'copy and embed' snippet:
<script type="text/javascript" language="JavaScript" src="http://localhost:3000/recipes.js"></script>
It is clear to see from the URL that the recipe resource is being called. The .js at the end is for the repsond_to block, letting it know to reply with JavaScript.