login

Table links published on Feb 9, tagged with html, javascript, website

Often you might want to present a table of items, each of which links to its own page. Typically you might add an additional cell with a link to go to the item-specific page.

Wouldn’t it be better if the entire row was itself clickable? Well, I did the googling, and here’s one easy way I’ve found to accomplish that.

You’ll need a little jQuery:

$(function() {
  $('tbody.link tr').click(function() {
    window.location = $(this).find('a').attr('href');
  }).hover(function() {
    $(this).toggleClass('pointer');
  });
});

Of course, you can choose to put this only on pages that need it, but it’s not very heavy and if it’s on your site-wide template, you can quickly apply this method to any table you want by just adding a class to the tbody tag (all of your tables do have thead and tbody tags, right?)

For that hover callback to have the desired effect and let your users know they should click on the row, you’ll need a little bit of css as well:

.pointer { cursor: pointer; }

You could put this css change right in the javascript, but I find this pointer class comes in handy throughout my site anyway.

Finally, for any tables which you want to behave this way, just use markup like the following:

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody class="link"><!-- 1. add the class -->
    <tr>
      <td>
        <a href="/items/1"></a><!-- 2. add the link(s) -->
        Item_1
      </td>
      <td>
        The first item
      </td>
    </tr>
    <tr>
      <td>
        <a href="/items/2"></a>
        Item_2
      </td>
      <td>
        The second item
      </td>
    </tr>

    <!-- ... -->

  </tbody>
</table>

Notice the content of the “Name” field is outside of the link tag and the link itself has no content. This ensures no actual link will be visible to confuse users, all they have to do is click anywhere on the row.

For a real example, checkout the archives page.

Live Search (part 2) published on Jan 30, tagged with javascript, website

In my last post I went over setting up sphinx full-text search using an xml data source from a yesod application as well as hooking into sphinx to return search results for a given query as a JSON data feed.

In this (shorter) post, I’ll go over the front-end javascript that I used to implement a fairly simple search-as-you-type interface.

Object oriented

Now, I could have easily defined some simple functions in the global namespace to execute the search, display the results, then attach an event handler to the changes to the input box, but I’d rather not.

Javascript can be used fairly effectively in an object oriented way. No, I’m not doing any inheritance or method overloads, but I do want to try and group all my logic in an instance of some object. This will let me store some values in instance variables (properties) for use between methods as well as give me a namespace for all my stuff.

Here’s the structure:

var Search = {
    execute: function(qstring) {
        // actually execute the search and call display as the success 
        // callback
    },

    display: function(results) {
        // update the page with the contents of the search results.
    },

    attach: function() {
        // attach a listener for changes to the input element and fire 
        // off the search when appropriate.
    }
};

Our feed is accessed at /search/j/query-string and returns something like this:

[
  {
    "slug":    "some_post",
    "title":   "Some post",
    "excerpt": "... some excerpt with matches in it ..."
  },
  {
    "slug":   "other_post",
    "title":  "Other title",
    "excerpt":"... other excerpt with matches ..."
  },
  ...
]

Given that, our execute and display functions should look like this:

    execute: function(qstring) {
        var search = this;
        var url    = "/search/j/" + encodeURIComponent(qstring);

        $.getJSON(url, function(data) {
            search.display(data);
        });
    },

    display: function(results) {
        var html = "";

        $.each(results, function(id, result) {
            html += '<div class="result">'
                  + '<h3><a href="/posts/' + result['slug'] + '/">' + result['title'] + "</a></h3>"
                  + '<div class="result-excerpt">' + result['excerpt'] + '</div>'
                  + '</div>';
        });

        // assume this property exists for now
        this.results.html(html);
    },

Our attach method will handle a few things:

  1. Store selectors for the input element and the results container as properties on our object.

  2. Attach a listener to the input element that fires every time a character is entered.

  3. Check that the entered search term is non-empty, big enough, and has actually changed – to prevent a “needless” search.

    attach: function() {
        this.search  = $('#search');
        this.results = $('#results');

        var search = this;

        this.search.keyup(function() {
            var $this = $(this);

            var newVal = $this.val();
            var oldVal = $this.data('old-value');

            if (newVal.length >= 3 && newVal != oldVal) {
                search.execute(newVal);
            }

            $this.data('old-value', newVal);
        });
    }

I use jQuery’s data function to store the input’s current value between each event to see if it’s changed since last time.

Note that we also have to store a reference to this outside of the keyup callback, since calling this inside that closure means something else (the element itself).

With all that in place, a search page that uses this object would look something like this:

<input id="search">

<div id="results"></div>

<script>
    $(function() {
        Search.attach();
    });
</script>

That’s it, simple and effective. Go ahead, try it out.

Hackday published on Feb 28, 2011, tagged with boston, javascript

The idea was that 100 developers, 50 designers, and 50 “others” would all get together Friday night, team up and develop a phone or web app to make life better in the city of Boston.

Each team would have until 2:30 Sunday afternoon to get something demo-able. Then they would present and be judged.

The Team

Friday night was a bit of a social mixer with drinks, food, and mingling up until about 7PM. Then, anyone with an idea was asked to come forward and give a 30 second pitch. Those that didn’t could then go stand with the idea they liked best.

We decided to team up with a Harvard Business School student who wanted to help her Mom get less parking tickets. The issue was that the signs were so confusing in Boston, that she didn’t know she couldn’t park there.

We picked up a designer and a rails dev and got started.

The App

We created CanHazParking which aggregates your location, the current time, and the many confusing parking laws in the area to give you a simple “can haz” or “no can haz” answer.

The app was written using ruby on rails and jQuery. We used a few open APIs, lots of jQuery, html scraping, and one nifty email hack to get the parking information.

I focused mainly on the Snow Emergency logic and helped out a bit with Street Cleaning (I determine if even addresses are on the right or left side of the vehicle).

Throughout the weekend we had varying levels of functionality with some unfortunate breakages just about demo time (pretty sure I contributed to that, sorry guys).

Despite these issues, we took the prize for “Best Boston-centric” application. For this we get Blue Man Group tickets as well as blurbs in the various boston.com articles about the event. The first of such articles can be seen here.

Many thanks go to my team members: Beth, Dave, Kara, and Ken. Certainly couldn’t have had so much fun and been so successful without such a smart and easy-going team.

Ajax published on Jan 29, 2011, tagged with javascript, website

I always thought Ajax (and JavaScript for that matter) was some crazy web technology, some new way of programming the web, some big scary thing that would be really difficult to learn or use.

It’s not. It’s actually just a defined, convenient way of using existing tools to accomplish some goal. I won’t say it’s perfect, or the be-all-end-all of web-tech, but it did come in handy for me in one case.

I’d like to share the experience, shed some light on this methodology and perhaps make it easy for someone else to add it to their bag of tricks.

What is Ajax?

Ajax stands for Asynchronous JavaScript and XML. It’s a means of making static web pages dynamic.

The way it works is this:

You’ve got some page that loads up in a user’s browser presenting some information. Once the content is served to the user that’s it. It’s there, static and stale.

You want to periodically update some information on that page, but how?

JavaScript could update tag contents, but the source code for your awesome JavaScript is served out along with the content and it’s just as stale. It has no idea what new information it needs to present on your behalf.

Well, what if we could phone home, tell the JavaScript to call back to the server and ask for updated information, then update the page accordingly.

That is Ajax.

You basically develop two webpages: the first, is the normal static html page that the user requests. The second is a dynamic page which serves an XML document (or JSON) which provides updated info through server-side logic.

The first page comes complete with JavaScript capable of requesting the second page on an interval and updating the first page with the new information it finds in the XML.

Pretty slick.

An Example

I recently wrote a “subsite” for the Haskell web framework Yesod. That’s a lot of jargon, but just think of it as a plug-in.

It lets you control an instance of mpd running on the same server via a web page.

More information (and the full source) can be found here but I’ll try and distill out the generic Ajax involved.

Let’s say I have a “Now Playing” page which shows the currently playing Artist, Album, and Title. I want to update that information as the track changes without the user having to constantly refresh the page.

Here’s how you do that:

The main page is served as normal html with some handy id’s so we can find specific tags when we want to update them.

This page will also include all the JavaScript to make the request and do the updates, but I’m going to save that information until after the XML part.

<!-- http://server/pages/nowplaying.html -->
<!DOCTYPE html>
<html>
    <head>
    <script>
        // There be lots of JavaScript here, but I'll get to that 
        // later...
    </script>
    </head>
    <body>
        <p id="artist">Medeski Martin &amp; Wood</p>
        <p id="album">Tonic</p>
        <p id="title">Thaw</p>

    <!-- our main ajax entry point will be this function, call it on 
         document load to kick things off -->
    <script>window.onload = timedRefresh;</script>
    </body>
</html>

Behind the scenes, you’ll need that second page which is just XML. It’ll have to be driven by server-side code to serve updated information each request.

<!-- http://server/nowplaying.xml -->
<?xml version="1.0" encoding="utf-8"?>
<xml>
    <status>OK</status>
    <artist>Dave Weckl Band</artist>
    <album>Multiplicity</album>
    <title>Mixed bag</title>
</xml>

The status tag is an extra fail-safe in-case your server runs into trouble coming up with updated info. You’ll see we write our JavaScript to be conditional on this tag’s value.

With the XML page available at any moment to provide updated information, here’s the JavaScript which you would need to put in the user-facing page to accomplish the constant screen updates.

// this object will make the request for the updated xml and provide an 
// easy means for parsing it
var xmlhttp = new XMLHttpRequest();

// when it receives a response from your server this code will run
xmlhttp.onreadystatechange = function()
{
    // if the state is 4 and the status is 200, that means we're ready 
    // to parse the response
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
    {
        // parse the response into a workable document
        xmlDoc = xmlhttp.responseXML;

        // use a helper function to get the contents of specific tags 
        // and deal with them appropriately
        xStatus = xmlHelper(xmlDoc, "status");

        // make sure our server thinks all's ok
        if (xStatus == "OK")
        {
            // get the new info from the xml response
            xArtist = xmlHelper(xmlDoc, "artist");
            xAlbum  = xmlHelper(xmlDoc, "album" );
            xTitle  = xmlHelper(xmlDoc, "title" );

            // another helper function updates a specific tag on the 
            // current page by its id value
            docHelper(true, "artist", xArtist);
            docHelper(true, "album" , xAlbum );
            docHelper(true, "title" , xTitle );
        }
    }
}

// the helper to get a specific tag from the xml:
function xmlHelper(_xmlDoc, _tag) {
    return _xmlDoc.getElementsByTagName(_tag)[0].childNodes[0].nodeValue;
}

// the helper to set (or get) the value of a tag in this document by  
// its id:
function docHelper(_set, _id, _value) {
    if (_set) {
        document.getElementById(_id).innerHTML = _value;
    }
    return document.getElementById(_id).innerHTML;
}

// this function actually makes the request, phoning home for updated 
// now playing information
function getNowPlaying() {
    xmlhttp.open("GET", "http://server/nowplaying.xml", true);
    xmlhttp.send();

    // loop again
    timedRefresh();
}

// on document load, we call this function which will start the 
// never-ending loop of updates
function timeRefresh() {
    var delay = 1000; // seconds * 1000
    setTimeout("getNowPlaying();", delay);
}

And that’s it my friends. Constantly updating screen content without compulsive refreshing cluttering up your server logs.

Update: JSON and jQuery

So, the above all works fine and dandy. Nowadays though, seems all the cool kids are doing this with JSON and jQuery.

JSON is a structured data response that can better play the role initially delegated to XML. It’s nicer because it can be worked with in JavaScript without parsing.

Using jQuery just removes boilerplate code and makes a lot happen in just a few lines.

Combine this with the awesome JSON support built into Yesod, and I was able to get my MPD controller updating via JSON with much cleaner code than the XML version.

For those that are interested, here is how you set up a page in Yesod to reply with either the normal HTML or a JSON response depending on what the client asks for.

This means I don’t need a separate status.xml to deliver the updated info, just call the current page url but ask for JSON this time.

-- note: nowPlaying returns a 'Maybe NowPlaying' data type which holds 
-- (Just) all the information about the currently playing song or a 
-- 'Nothing' if there's, well, nothing playing.
getStatusR :: YesodMPC m => GHandler MPC m RepHtmlJson
getStatusR = do
    mnp <- nowPlaying
    defaultLayoutJson (htmlReply mnp) (jsonReply mnp)

    where
        -- here's what's returned if the client requests HTML:
        htmlReply mnp = do
            addJulius [$julius|
                // add your jQuery here (see below).
                |]

            case mnp of
                Just np -> [$hamlet| 
                    <h1>MPD
                    <div #artist>#{npArtist np}
                    <div #album>#{npAlbum np}
                    <div #title>#{npTitle np}
                    |]

                Nothing -> [$hamlet| Nothing playing? |]

        -- and what's returned if the client requests JSON data:
        jsonReply mnp = case mnp of
            Just np -> jsonMap
                [ ("status"  , jsonScalar $ "OK"       )
                , ("artist"  , jsonScalar $ npArtist np)
                , ("album"   , jsonScalar $ npAlbum  np)
                , ("title"   , jsonScalar $ npTitle  np)
                ]

            Nothing -> jsonMap
                [ ("status", jsonScalar $ "ERR"               )
                , ("error" , jsonScalar $ "MPD threw an error")
                ]

So now my JavaScript can be simplified (don’t forget to source some jquery.js in the head of your page):

function getNowPlaying() {
    var delay = 1000; // seconds * 1000

    if (delay != 0) {
        $.getJSON(window.location.href, {}, function(o) { // <- Ajax jQuery style...
            if (o.status == "OK") {
                // update each div witht he info in "o"
                $("#artist").text(o.artist);
                $("#album").text(o.album);
                $("#title").text(o.title);
            }
        });

        // and, loop again
        setTimeout("getNowPlaying();", delay);
    }
}

$(function() { getNowPlaying(); })

Pretty cool, huh?