Link resolvers are the backbone of any robust electronic resource collection, connecting users with the full-text items or abstracts that they need for their research. They are often the last piece of the library-controlled Web that users see before being shuttled off to a vendor database. Yet they are consistently the least user-friendly, ugliest, most neglected part of the eResource toolkit.

Grand Valley is a SerialsSolutions shop: Summon Discovery Service, 360Link link resolver, and the SerialsSolutions ERMS. While Summon locks us out of changing the user interface, 360Link offers some options for customization. Yet our installation was essentially “out-of-the-box” three years after we launched. In one of our first regular usability tests, it became clear that the poor stock design of the link resolver was an impediment to our users (Figure 1). Our users could not tell which of the links they were supposed to click, since they relied more on visual cues than on text. We observed most users clicking on the name of the database, the last place you want them to click. (The article link is where we want them to go.)

Old, stock design of the 360Link link resolver.
Figure 1: Our stock 360Link Installation

However, when I got under the hood and started making changes to the interface, I realized that I had my work cut out for me. While SerialsSolutions offers the ability to add a custom CSS file, their HTML was so full of syntax errors and depreciated practices that it would be very difficult to make any significant changes without asking SerialsSolutions to make changes on their end. My experience with this kind of request led me to seek another solution, and I realized I could make any of the changes I wanted with jQuery, a lightweight but powerful Javascript library1.

I had three goals in redesigning our link resolver:

  1. Clean up SerialsSolutions bad HTML
  2. Change the layout to improve usability and conformity with Grand Valley’s branding
  3. Enable progressive enhancement, so that these changes would not lock users into needing Javascript

Inline Styles, we hates them.

360Link is full of nested tables, inline styles, and duplicate IDs. Class names are applied inconsistently, and any custom CSS will just be putting a coat of paint on a bad layout. My first step was to address the obvious HTML errors and remove them.

First, I added jQuery and my own script, 360Link-Reset.js, in the footer section of the 360Link’s administrative interface under Branding2. Those are all the changes we need to make to 360Link, although you should certainly include all of your library’s branding.

The first thing I did was remove all of the pesky cruft that SerialsSolutions left in the HTML: inline styles, unnecessary classes, and duplicate IDs. This was handled by the simple jQuery function .removeAttr. Tables were the worst offenders, with initial table tags that look like this in a stock 360Link installation:

<table class="CandyWrapper" border="0" cellspacing="0" cellpadding="0" align="left" id="CandyWrapper">

I needed to remove the border, cellspacing, cellpadding, style and align attributes of all tables, so I added these lines to jQuery:

$("table").removeAttr( "align" );
$("table").removeAttr( "cellspacing" );
$("table").removeAttr( "cellpadding" );
$("table").removeAttr( "border" );
$("table").removeAttr( "style" );

Now, jQuery will remove these attributes once the page loads so the browser will see the following line3:

<table class="CandyWrapper" id="CandyWrapper">

I then repeated the same same procedure with table rows and cells, paragraphs, and spans. Now my 360Link page was devoid of inline styles.

We now have removed all of SerialsSolutions offending inline styles and replaced them with our own. Now let’s start making some more drastic changes.

Everything is in the wrong place

Even replacing our styles won’t let us make serious interface changes to the link resolver, since all of the good content is wrapped up inside of tables (and tables within tables). We need to scrap this table-based layout and replace things with standards-based HTML. The trick is that the information we want to display to our user is locked up within those tables, and it changes with every display of the link resolver. So we need a mechanism to pull the data from the page, store it while we erase the tables, and then place it inside of our new standards-based HTML. Fortunately, jQuery is up to the task. I’ll show you how I replaced the ridiculous citation display table with a standards-based div.

First, we need to grab the data from page. jQuery gives us a lot of options here, and fortunately, SerialsSolutions has used unique IDs for each of the table cells that display citation information. Here I’ll detail those used for articles, which have the format variable set to either “Journal” or “JournalFormat”:

  • Author Name (Last, First) = span.fn
  • Journal Title = #CitationJournalTitleValue
  • Article Title = #CitationJournalArticleValue
  • Journal Volume # = #CitationJournalVolumeValue
  • Journal Issue # = #CitationJournalIssueValue
  • Journal Date = #CitationJournalDateValue
  • Article Page Range = #CitationJournalPageValue
  • Journal ISSN = #CitationJournalIssnValue

Since these unique IDs change depending on the format of the item retrieved by the link resolver, we first need to make sure we are dealing with a Journal. The first line of our new function is:

if (format == "Journal" || format == "JournalFormat") { 

This way the code will only be executed if the variable “format”, which is set by a script loaded by SerialsSolutions, matches either “Journal” or “JournalFormat.” We’ll eventually want to do a check for every value SerialsSolutions returns for the format variable4. [fn: ] Now we’ll grab the values SerialsSolutions has retrieved from their database for these items. I’ll show you how to grab the Journal Title and you can apply the idea to any of the variables you need to grab. The jQuery method text() will grab the textual value shown to the user on the screen and store it as a variable of our choosing.

var journalTitle = $("td#CitationJournalTitleValue").text();

This line first declares a javascript variable, journalTitle, and then assigns it the value of the text inside the table cell with the ID CitationJournalTitleValue. That’s it. We can repeat the process for each of the variables we need on the page, although first we want to add one step to clean up any excess white space in our variable with the jQuery method trim():

journalTitle = jQuery.trim(journalTitle);

Once we’ve finally retrieved and cleaned up all of our variables, we can replace the existing table with a more semantic HTML div with our citation. Feel free to use whichever citation format you want, although we use a bit of a hybrid since we want to display ISBN and ISSN numbers. First, I want to create the div in HTML to make sure it will look the way I want:

<div id="citation">
    <span id="CitationJournalAuthorValue">Smith, Bob. </span> <!-- our js var authorName -->
    <span id="CitationJournalDateValue">(2011). </span> <!-- our js var journalDate -->
    <span id="CitationJournalArticleValue">An Awesome Article.</span> <!-- our js var articleTitle -->
    <!-- etc -->

The final table has the class of SS_CitationTable, and it is the only table with this class, so the following line will replace the table with our div. We’ll replace our dummy values with our javascript variables, like so:

$("table.SS_CitationTable").replaceWith( '<div id="citation"> <span id="CitationJournalAuthorValue">' + authorName + '</span>. <!-- etc --> </div>');

The jQuery method will replace the table with the class SS_CitationTable with the HTML we’ve included. And with that, we’ve removed one of the pesky tables with some some semantic, standards-based HTML.

One thing we had SerialsSolutions add years ago was a link to export the citation on this page to RefWorks. However, the function was never used because the link was all the way at the bottom of the page, rather than up with the citation where it logically belongs. With jQuery, we were finally able to move it from the bottom of the page and tie in in at the end of our citation to allow students to take advantage of this tool. Getting the value of this link was a little more difficult than citation values, because SerialsSolutions did not include a unique ID to the custom links, and in fact, abuses tables and classes in the custom links area. Since our RefWorks link is always the last link on the page with the class of AnchorButton, we could get the href value with the following method:

var refworkslink = $("a.AnchorButtom:last").attr("href");

However, it is always possible that SerialsSolutions could slap in another anchor link with that class after the RefWorks link. So a better method is to tie this into the static value of the link text, which always says just “RefWorks”:

var refworkslink = $("a.AnchorButton:contains('RefWorks')").attr("href");

The “contains” selector is an incredibly useful tool for grabbing values from vendor pages and rewriting their code. jQuery also supports CSS3 selectors, so you should be able to nab any piece of content and store it in a variable, no matter how crummy the vendor’s code is.

In which we start to hate SerialsSolutions default stylesheet

Now that we’ve moved some things around, we’re ready to start making things pretty with some CSS. But we still have the crummy SerialsSolutions-supplied CSS to content with. I decided to simply replace SerialsSolutions styles with my own, rather than write styles to counteract theirs. Since our replacement code will only be loaded when javascript is available, I chose to remove their styles and load our own styles through jQuery. With jQuery’s remove() method, it was easy.

First, I wanted to remove all of the stylesheets loaded in the element. These are the stylesheets that SerialsSolutions loads automatically. One simple jQuery method will do this:

$("head link").remove()

Now we need to load our own stylesheets. To make it easy to load multiple stylesheets, I created a variable called cssPath, which is simply the path to all CSS files. I first loaded a reset stylesheet that resets the styles on all HTML elements to better ensure cross-browser support, and then I load the custom stylesheet I have created for our link resolver using the append() method, which appends data at the end of an element (in our case, the element):

$("head").append( '<link rel="stylesheet" type="text/css" href="' + cssPath + 'linkresolver.css" />');

The great benefit to this method, rather than adding your styles to SerialsSolutions “Custom CSS”, is that our styles will only be loaded when javascript is available. Users who cannot use javascript for some reason will simply get the terrible HTML SerialsSolution ships with 360Link as well as the default styles, and users with Javascript will get the benefits not only of a rewritten page, but also of better styles to match.

If you have a lot of visitors who don’t have javascript available, you may want to write a stylesheet for SerialsSolutions default styles and load it through the 360Link control panel. Then remove it with jQuery. Users with javascript will still get all of the custom rewrites and better styles we just developed, but users without javascript will have the benefit of some improved styles for the default layout. At the very least, make some changes to the “Article” link to make it more visible.


Rewriting vendor websites with jQuery can be a great tool for solving certain problems. It’s great for rapid prototyping of an idea or getting something workable in front of real users quickly, without having to wait for the vendor to make changes or for your development team to build a custom interface using APIs. In these cases, it can be a great first step along the way to a larger development project, but it wouldn’t be a long-term fix by itself. However, there are times when vendor has so apparently abandoned development on a product (like 360Link), that jQuery might be your best option for making dramatic changes. (360Link does have an API, but good luck finding documentation on it.)

This is not necessarily a rock-solid, reliable solution for long-term changes to vendor products, however. For starters, your code is dependent on the bad practices the vendor uses. If the vendor changes their code, even changes IDs or classes, your code will break. In addition, you’re limited to the data the vendor already provides on the page itself. Unlike an API, you don’t have the option of grabbing data that the vendor has stored in a database; you can only grab data that is already displayed on the page. Finally, you must remember that not everyone has javascript, so you shouldn’t require javascript for your changes to at least be functional.

New, jQuery enabled design of the 360Link link resolver.
Figure 2: Our current 360Link page

Styling FTW!

We’ve tweaked our layout based on user testing and feedback, and so far things are going well. We’ve also packaged up the scripts we’ve used to make these changes and distributed them on Github, so feel free to grab the source and just plug and play, or feel free to make (or contribute!) changes. Just this week I gave a little talk at Code4Lib Midwest in Chicago on the progress we’ve made using jQuery to “enhance” hosted vendor websites. I focus mostly on 360Link, but I also talk about some work we’ve done with getting CONTENTdm to use remotely-hosted video (and HTML5 video, at that) as well as some progressive enhancement work we’ve done with our institutional repository. You can grab the slides from that talk on Slideshare.

  1. As an added bonus, you can make immediate changes to 360Link when employing this method. Since 360Link updates only once a day, any changes you make through the SerialsSolutions administrative console take 24 hours to be applied. But since the only real change you’ll make to the console is to include a javascript file which handles all of the interface changes, changes to the javascript file are applied immediately.
  2. Including your own scripts in the footer section allows the browser to render the page before loading scripts, as well as allows users to see content even if the script fails. You should do this whenever you can.
  3. if you View Source in your browser, you will still see these attributes. But if you use Firebug or Safari and open the Web Inspector, you’ll see that the DOM as rendered does not include these attributes.
  4. You can see the variables I’ve discovered both in the source code for 360link-reset.