Nik's Technology Blog

Travels through programming, networks, and computers

Learning jQuery 1.3 - Book Review

My first exposure to jQuery was using other developer's plugins to create animation effects such as sliders, and accordion menus.
The highly refactored and compressed production code isn't the easiest to read and understand, especially if you want to alter the code to any great extent.
After reading a few tutorials, I thought I'd buy a book and get more involved with the jQuery library.

As an ASP.NET developer used to coding with intellisense, I was pleased that jQuery has been incorporated into Visual Studio to allow ease of developing.
I browsed through the jQuery books on Amazon and opted to buy "Learning JQuery 1.3" by Jonathon Chaffer and Karl Swedberg after reading the user reviews.

I've now read most of the book and can highly recommend it.  The book assumes the reader has good HTML, CSS knowledge as well as a familiarity with JavaScript and the DOM, but this enables the book to quickly move onto doing useful, everyday tasks with jQuery.

The first six chapters of the book explore the jQuery library in a series of tutorials and examples focusing on core jQuery components.  Chapters 7 to 9 look at real-world problems and show how jQuery can provide solutions to them, and the final two chapters cover using and developing jQuery plugins.

Web developers should be aware of web accessibility and SEO issues with using client-side scripting and it is good to see the book highlighting the concepts of progressive enhancement and graceful degradation where appropriate.

"the inherent danger in making certain functionality, visual appeal, or textual information available only to those with web browsers capable of (and enabled for) using JavaScript.  Important information should be accessible to all, not just people who happen to be using the right software." - Learning jQuery 1.3,  page 94

After a brief introduction into the world of jQuery, what it does and how it came about the book moves quickly on to selectors, which are a fundamental part of how jQuery selects element(s) from the DOM.  It also covers jQuery's chaining capability, which coming from other programming languages looks odd at the outset, but quickly proves to be a very powerful technique.

The authors then move on to talk about events.  What I particularly like about the way jQuery handles events is that the behavioural code can be cleanly separated away from the HTML mark-up without having to litter tags with onclick and onload attributes.

The examples show how to add functionality on top of your HTML by binding events to elements on the page, which when triggered cause jQuery to modify the HTML to bring the page to life.  Techniques are introduced by example, then slowly refactored and improved while introducing new jQuery methods along the way, which is a breeze to follow and learn.

The fourth chapter covers effects such as fading in and out and custom animations, and jumps straight in to cover a useful example of how text size can be increased on-the-fly for ease of reading.  The intro also mentions an important usability example of effects.

jQuery effects "can also provide important usability enhancements that help orient the user when there is some change on a page (especially common in AJAX applications)."- Learning jQuery 1.3,  page 67

Chapter 5 is all about DOM manipulation and covers jQuery's many insertion methods such as copying and cloning parts of the page, which it demonstrates with another useful example in the form of dynamically creating CSS styled pull quotes from a page of text used to attract a readers attention.

AJAX is the next topic, which interested me enough to create a little tool to load in an XML RSS feed and create a blog category list from the data.
The chapter covers the various options of loading partial data from the server including appending a snippet of HTML into the page, JSON, XML and how to choose which method is the most appropriate.

Table manipulation is next on the agenda and the book discusses how to sort table data preventing page refreshing using AJAX as well as client-side sorting, filtering and pagination.

Chapter 8 delves into forms, using progressive enhancement to improve their appearance and behaviour.  It also covers AJAX auto-completion as well as an in-depth look at shopping carts.

Shufflers and Rotators are next and the book starts out by building a headline news feed rotator which gets it's headlines from an RSS feed, typically used by blogs.  It also covers carousels, image shufflers and image enlargement.

Chapter 10 and 11 examine the plugin architecture of jQuery and demonstrate how to use plugins and build your own.  I successfully produced my first jQuery plugin from reading this book.  You can check out my tag cloud plugin and read about how I originally built it before turning it into a plugin that other developers can use.

Create a jQuery Tag Cloud from RSS XML Feed

I previously created a jQuery Blogger Template Category List Widget to retrieve blog categories from a Blogger.com RSS feed and create a list of links which click through to Blogger label pages.

I've now taken this code a step further and modified it to calculate the number of times each category/tag occurs enabling me to create a tag cloud from the data, like the one below.

 

Before I explain the code I wrote to make the tag cloud I'll go through the solution to a bug I found with the original categories code.

You may recall this snippet of code where I iterate through each post and then each category of each post, finally, when all the categories have been added to the array I sort them prior to de-duping them.

$.get('/blog/rss.xml', function(data) {
//Find each post
        $(data).find('item').each(function() {
//Get all the associated categories/tags for the post
            $($(this)).find('category').each(function() {
                categories[categories.length] = $(this).text();
            });
        });
        categories.sort();

I later refactored the code removing the $(data).find('item').each iteration which wasn't required since find('category') will find them all anyway.

I then discovered that the JavaScript .sort() function was case-sensitive which resulted in lower case categories being placed at the end of the list, causing problems when I de-dup them.

So the rewritten snippet of code became:

$.get('blog/rss.xml', function(data) {
     //Find each tag and add to an array
     $(data).find('category').each(function() {
         categories[categories.length] = $(this).text();
     });
     categories.sort(caseInsensitiveCompare);

where caseInsensitiveCompare refers to a JavaScript compare function:

function caseInsensitiveCompare(a, b) {
    var anew = a.toLowerCase();
    var bnew = b.toLowerCase();
    if (anew < bnew) return -1;
    if (anew > bnew) return 1;
    return 0;
}

Creating the Tag Cloud jQuery Code

I start off as before fetching the XML, adding all the categories/tags from the RSS feed to a JavaScript array, then sorting them.

But I needed a way to store, not only the tag name, but the number of times that tag is used on the blog (the number of times the category appears in the feed).  For this I decided to use a multi-dimensional array which would essentially store the data in a grid fashion e.g.

Tag Name Count
ASP.NET 5
Accessibility 2
Blogging 15
jQuery 2

 

The de-dup loop from my previous categories script now performs two jobs, it removes the tag duplicates and creates a count of each tag occurrence.

Once the multi-dimensional array has been populated, all that's left to do is iterate through the array creating the HTML necessary to build the tag cloud, followed by appending it to a DIV tag with an ID="bloggerCloud" on the page.

Note the calculation I perform to get the tags appearing a reasonable pixel size ((tagCount * 3) + 12).

$(document).ready(function() {
    var categories = new Array();
    var dedupedCategories = [];
    $.get('blog/rss.xml', function(data) {
        //Find each tag and add to an array
        $(data).find('category').each(function() {
            categories[categories.length] = $(this).text();
        });
        categories.sort(caseInsensitiveCompare);
        //Dedup tag list and create a multi-dimensional array to store 'tag' and 'tag count'
        var oldCategory = '';
        var x = 0;
        $(categories).each(function() {
            if (this.toString() != oldCategory) {
                //Create a new array to put inside the array row 
                dedupedCategories[x] = [];
                //Store the tag name first 
                dedupedCategories[x][0] = this.toString();
                //Start the tag count 
                dedupedCategories[x][1] = 1;
                x++;
            } else {
                //Increment tag count
                dedupedCategories[x - 1][1] = dedupedCategories[x - 1][1] + 1;
            }
            oldCategory = this.toString();
        });
        // Loop through all unique tags and write the cloud
        var cloudHtml = "";
        $(dedupedCategories).each(function(i) {
            cloudHtml += "<a href=\"/blog/labels/";
            cloudHtml += dedupedCategories[i][0] + ".html\"><span style=\"font-size:" + ((dedupedCategories[i][1] * 3) + 12) + "px;\">";
            cloudHtml += dedupedCategories[i][0] + "</span></a> \n";
        });
        $('#bloggerCloud').append(cloudHtml);
    });
    return false;
});

Since building this script I've now gone one step further and created a jQuery plug-in based on this code.  For more details and the source code see my jQuery Blogger.com Tag Cloud Plugin page.

jQuery Blogger Template Category List Widget

Blogger is a hosted blogging service which allows you to publish your blog to your own URL and create your own custom HTML templates to match your website design. 
I have been using Blogger for this blog for several years, and have been trying to find a good way of displaying a list of categories on each blog page.

As yet I haven't found an official way of creating a category list using the Blogger mark-up code, so I decided to write my own widget to do the job for me.

When I say category list I mean a list of all the blog tags/labels in your blog, each linking to a page with posts categorised using that particular tag, just like the examples below.

Blog Categories

Because Blogger is a hosted blogging service you can't use a server-side language to create the category list for your HTML template, instead you must rely on client-side JavaScript.

Thankfully the Blogger service publishes XML files to your website along with the post, archive and category HTML pages.  These are in ATOM and RSS formats and are there primarily for syndication, but XML files are also fairly straight-forward to parse using most programming languages and contain all the category data we need to build a categories list.

I chose to use the jQuery library because it makes the process even easier.

The Blogger XML Format

From the Blogger ATOM XML snippet below you can see that each blog item can have multiple category nodes.  This means that the code must loop through each blog post, then loop through each category of each post to create our category list, but it also means that we will have duplicate categories, because more than one post can have the same category.

<item>
  <guid isPermaLink='false'></guid>
  <pubDate>Thu, 14 May 2009 18:30:00 +0000</pubDate>
  <atom:updated>2009-05-15T11:35:03.262+01:00</atom:updated>
  <category domain='http://www.blogger.com/atom/ns#'>C Sharp</category>
  <category domain='
http://www.blogger.com/atom/ns#'>ASP.NET</category>
  <category domain='
http://www.blogger.com/atom/ns#'>Visual Studio</category>
  <title>Language Interoperability in the .NET Framework</title>
  <atom:summary type='text'>.NET is a powerful framework which was built to allow cross-language support...</atom:summary>
  <link>http://www.nikmakris.com/blog/2009/05/language-interoperability-in-net.html</link>
  <author>Nik</author>
  <thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total>
</item>

The jQuery Code

The jQuery code is fairly easy to follow, but here is a quick explanation.  After the DOM is available for use, I create two JavaScript arrays, one to hold the categories and one to hold our de-duped category list.  Then I load in the Blogger RSS feed and iterate through each blog post adding each category to the categories array.
Once it reaches the end of the RSS feed, I need to sort the array into alphabetical order so that I can de-duplicate the categories list I just populated, which is what the next jQuery .each() function does.
All I have left to do is loop through the de-duped categories list, create the HTML link for each category and the append the HTML unordered list to the page.

$(document).ready(function() {
    var categories = new Array();
    var dedupedCategories = new Array();
    $.get('/blog/rss.xml', function(data) {
        //Find each post
        $(data).find('item').each(function() {
            //Get all the associated categories/tags for the post
            $($(this)).find('category').each(function() {
                categories[categories.length] = $(this).text();
            });
        });
        categories.sort();
        //Dedup category/tag list
        var oldCategory = '';
        $(categories).each(function() {
            if (this.toString() != oldCategory) {
                //Add new category/tag
                dedupedCategories[dedupedCategories.length] = this.toString();
            }
            oldCategory = this.toString();
        });
        // Loop through all unique categories/tags and write a link for each
        var html = "<h3>Categories</h3>";
        html += "<ul class=\"niceList\">";
        $(dedupedCategories).each(function() {
            html += "<li><a href=\"/blog/labels/";
            html += this.toString() + ".html\">";
            html += this.toString() + "</a></li>\n";

        });
        html += "</ul>";
        $('#bloggerCategories').append(html);
    });
    return false;
});

 

Update your Blogger Template HTML to Show Categories

The only HTML you need to add to your Blogger template is a call to jQuery, and this script in the head of your page, plus an empty HTML DIV tag, in the place where you want your categories list to appear.

<script type="text/javascript" src="/scripts/jquery.js"></script>
<script type="text/javascript" src="/scripts/blogcategories.js"></script>

<div id="bloggerCategories"></div>

You can see the script in action on my blog, or see this code rewritten to create a tag cloud.

Blogger.com has changed their feed syndication

It seems that Blogger has changed the type of syndication feed they use during the last month (Jan – Feb 2008), I discovered this when my Atom feed XSLT transformation broke when I published my last post.
I originally wrote XSLT to transform the previous feed type for my homepage blog updates, which assumed the following heirachy with the Atom 0.3 namespace:

<feed>...<entry>...</entry></feed>

Whereas the latest feed has changed to use both Atom and openSearch namespaces and the following structure:

<rss>...<channel>...<item>...</item></channel></rss>

The root node seems to suggest it is the RSS 2.0 standard, using the Atom namespace, which is peculiar, notice the openSearch namespace too...

<rss xmlns:atom='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' version='2.0'>

Here's my updated XSLT to convert the new Blogger.com format.



<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">

<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="channel">
<div id="FeedSnippets">
<xsl:apply-templates select="item" />
</div>
</xsl:template>


<xsl:template match="item" name="item">
<xsl:if test="position()<6">
<h4>
<xsl:value-of select="title"/>
</h4>
<p>
<xsl:choose>
<xsl:when test="string-length(substring-before(atom:summary,'. ')) > 0">
<xsl:value-of select="substring-before(atom:summary,'. ')" />...<br />
</xsl:when>
<xsl:when test="string-length(substring-before(atom:summary,'.')) > 0">
<xsl:value-of select="substring-before(atom:summary,'.')" />...<br />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(atom:summary,0,200)" />...<br />
</xsl:otherwise>
</xsl:choose>
<strong>Read full post: </strong>
<a href="{link}">
<xsl:value-of select="title"/>
</a>
</p>
<hr />
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Insert a Blogger Atom Feed into an ASP.NET Web Page

I've been busy recently migrated my homepage (and several others) from classic ASP to ASP.NET. My homepage displays the latest 5 posts with a summary and a link to the full blog post.
I eventually found a tutorial using XSLT explaining how to achieve this after discovering that XmlDataSource XPATH doesn't support namespaces!
I've tinkered with the XSLT that Arnaud Weil posted in his blog to achieve the following objectives:

  1. Limit the amount of posts returned by the transformation.
  2. Show a summary of the post.
  3. Show a summary that tries hard not to cut words in half when generating a snippet.
  4. Produce XHTML valid code.

Here's the source of my XSLT...

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!--<xsl:output method="html"/>-->
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/atom:feed">
<div id="FeedSnippets">
<xsl:apply-templates select="atom:entry" />
</div>
</xsl:template>


<xsl:template match="atom:entry" name="feed">
<xsl:if test="position()&lt;6">
<h4><xsl:value-of select="atom:title"/></h4>
<p>
<xsl:choose>
<xsl:when test="string-length(substring-before(atom:summary,'. ')) &gt; 0">
<xsl:value-of select="substring-before(atom:summary,'. ')" />...<br />
</xsl:when>
<xsl:when test="string-length(substring-before(atom:summary,'.')) &gt; 0">
<xsl:value-of select="substring-before(atom:summary,'.')" />...<br />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(atom:summary,0,200)" />...<br />
</xsl:otherwise>
</xsl:choose>
<strong>Read full post: </strong><a href="{atom:link[@rel='alternate']/@href}"><xsl:value-of select="atom:title"/></a></p>
<hr />
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Is there still a place for site newsletters in the web 2.0 world?

More and more sites are adopting XML syndication technologies such as RSS and ATOM which users can subscribe to.

Pull Technology

Rather than being a push technology like email newsletters, RSS is a pull technology. The subscriber is in full control of the subscription, the publisher does not have a relationship with the subscriber, or need to know their email address. This makes unsubscribing very easy and because you don't need to supply an email you do not need to worry whether your details will be sold on by unscrupulous companies.

RSS Adoption

XML syndication has been around for over 5 years or so, but in the early days the RSS readers available weren't up to scratch, so it took a while for the technology to gather momentum. Nowadays there are plenty of good readers, such as Bloglines, Google Reader etc, which are very polished products that support all the major formats.

RSS Advertising

The last nail in the email newsletters coffin will be the adoption of RSS advertising into the mainstream. Currently Google and Yahoo! are performing tests with advertising on these syndication formats. As soon as these are released the already strong relationship Google has with publishers will allow it to rapidly make RSS very lucrative for website publishers.

Syndication Analytics

Until recently publishers syndicating their content via RSS had a hard time analysing their circulation, that's where companies such as Feedburner have found a niche and continue to provide publishers with additional services on top of basic subscription tracking.

Syndication SPAM

Of course syndicating your content is just another method of publishing. First you had paper, then HTML now XML. You can't irradiate SPAM with RSS, people can set up SPAM blogs etc, but it's the subscribers who are in control of their subscriptions. So as a publisher you know that your 500 subscribers reported by your RSS analytics product of choice are actively reading your content or else they'd simply click to unsubscribe from within their RSS reader application. Compare that to a database of registered subscribers dating back several years; are those users viewing your newsletter in their preview pane and pressing delete rather than unsubscribing via an unsubscribe link?

Content is King

The old adage that 'content is King' is truer than ever with RSS syndication. The problem with giving such power to the subscriber is that your content needs to be top-notch in order to keep your subscribers subscribing. Even though there are guidelines specifying opt-out and unsubscribe methods and practices, which newsletter senders must adhere to, the fact is unsubscribing from RSS is far easier and is not reliant on differing geographic data protection laws.

To Feedburn or not to Feedburn?

I've decided to try out Feedburner. We use RSS to syndicate content at work and have to use server log file analysis to track them, web-beacon based web analytics packages are good for websites, but you can't add Javascript to feeds, which are pure XML. We've tried using .NET to database the hits we were getting on the feeds, but after a short while of testing we were seeing our database growing quickly in front of our eyes, not to mention consuming our precious CPU cycles.

Feedburner not only takes away the hassle of analysing web feed statistics and subscribers, but adds a lot of other functionality too.

My main initial issues with Feedburner were the following:

* What if Feedburner went bankrupt? All the sites syndicating my feed would be using the feedburner URL (unless I pay for the Pro service). How would I be able to change this back to my own URL or another Feedburner type URL? (hopefully saying goodbye to Feedburner would also still hold true if they went bankrupt?) [UPDATED: On June 1st 2007 Google purchased Feedburner, therefore making bankruptcy much less likely :-) ]

* I can't redirect any current traffic from my old Blogger Atom feed on my shared Windows hosting as I don't have access to IIS through my control panel. The file is an .xml file, and I can't use .htaccess for obvious reasons. I would need to use an ISAPI rewrite tool I suppose, which I probably wouldn't be able to get installed in a hosted environment.

* If I want to later upgrade to the Pro service, I would surely have to keep my Feedburner URL even though I could have a URL hosted on my site with this package just so I keep all my subscribers using the same feed URL. (I guess I could use the "saying goodbye to Feedburner" process above?)

Despite these issues, I've decided that the pros of knowing my subscribers etc out way the cons and I'm now syndicating through Feedburner!

I am wondering however, how Feedburner manage to host so many blogs. I assume they have some serious kit to handle the many requests they get. I would be interested to know what the Feedburner IT infrastructure looks like.

Google Reader Mobile Interface - Good, But Room To Improve

I'm a big fan of Google Reader. I tried the first Google Reader interface not long after it was released and couldn't get on with it. It didn't have much going for it. It was hard to use and read articles from, and it was buggy although it was a beta release.
Since the interface was changed however it has progressed in leaps and bounds. It is now my feed reader of choice. I didn't particularly like the Bloglines interface either.

Mobile Feed Reading

I'm a busy person and I don't get much time during the day to catch-up on the news I want to hear about. So after purchasing a Windows Mobile MDA with unlimited 3G Internet access I was on the look out for a decent mobile feed reader.
Even though browsing normal websites with mobile devices is possible, it's not a rewarding experience because not many sites are optimised for the small interface.
Thankfully Google Reader has a fairly decent mobile interface, that includes a mobile proxy to reformat normal web pages to make them much more readable on small mobile devices.
Google have managed to shoe-horn most of the functionality into the mobile version, but it is a lot more buggy than the normal web version. Perhaps some of the bugs I come across are down to buggy web feeds, but Google should be able to find a way around most problems.

Google Reader Mobile User Interface Enhancements

Here are some of the bugs/bugbears I have with the mobile interface (in no particular order)

  • There should be a link straight through to the article on the site in question, not just the summary page in Reader. This will reduce the amount of clicks if you know you want to read the article even without reading a summary first.
  • Why do we need the more... Link when we now have the mark these items as read... Link?
  • When you star an item it reloads the whole page. Can it not return to the list on news items?
  • Some blogs cause http errors, produce no article when you click through, feature loads of links that you need to scroll through to get to the content, or show the summary as being the same as the blog title.
  • I would like the option of being about to read only a subset of my feeds from the mobile interface.

..and finally...

I use the new shared items widget on my blog to let my users know what I've been reading lately. From the web interface you can quickly share feed items with a simple click. This functionality is missing from the mobile interface however. So the work around for the time being is to star each item I want to share with my readers and click the share button on each item when I get on a desktop machine.

The interface is improving all the time, so hopefully Google will listen to its users, because I know I'm not the only one who has views on some of these topics.