Back to where we came from

I’ve had a small, but noticeable problem with my home audio interface.

Yes, I’m well aware that I often seem to have small problems just begging for a resolution, but neglected nonetheless for some period of time until… a blog post is finally written.

Anyhow, this particular problem stems from the dynamic tables that are oh-so-cool in giving us super-long tables that are filled dynamically by the backend.  It’s somewhat of a good problem to have, in fact.

Here’s the deal.  You execute a search and you’re staring at your results.  Were you to press F5 now, you’d see the exact same page starting back at you (provided you hadn’t scrolled down at all).  This is the case because a search always refreshes the whole page, or more specifically, requests a new URL in the browser’s address bar.

That’s great and all, but then you decide that you want to sort on a different field.  So you do.  And because the table is dynamic, the sort occurs without refreshing the entire page.  Now, if you were to press F5, you’d be looking at the same search results but the original sort order would be restored.

The truth is that this situation still occurs today, and pretty much occurs in many DHTML apps unless you do some cool work to alleviate it (like I did at work, for a different but similar situation).  However, there’s more to the story.

Now, some links are smart enough to (essentially) ask the table about its condition and then make a request to the server based on the table’s answer.  So for example, if you click a link to add the results to a playlist, then the results will get added with the correct sort order.  And this could be done for every link on the page.

Except… they would have to become javascript links, vs. the straight HREF’s they are now.

Why does this matter?  Well let’s take allmusic.com as an example.  It used to be that some links – particularly those that were presented in list format – were javascript links.  If you attempted to open them in a new tab, well, any number of things could happen.  Recently allmusic made some changes and now many of their links are straight HREF’s, meaning you can open in a new window or new tab or whatever and it works as expected.

In our home audio interface we have the similar, occasional requirement of wanting to open a link in a new tab.  But if the link is a JS link, then you’ve got a problem.

It’s because of this that I decided that I didn’t want to present all of links as JS links.  But I still wanted to be able have the links reflect the state of the main table.

The solution I came up with is creative, though not the prettiest.

Basically, we leave the links as they are.  But, if the table changes for whatever reason, then we go out and explicitely rewrite links to reflect the changes.  This isn’t an ideal solution since it involves some overhead on the client.  But I can’t think of any other way to have regular HREF links which reflect changing properties elsewhere in the page.

So the act of initiating this update is fired by the table’s scroll handler.  I decided that this was the best place to start the update, as the scroll handler has the singular task of determing which rows/tiles are in the viewport and hence need to be brought in from the backend.  Recall that every time you scroll, it’s necessary to make sure that you’re actually looking at something.  This is the job of the scroll handler; he does some calculations, determines what should be there, then starts to calls to put those things “there”.  Since the scroll handler is so integral to the operation of our dynamic tables, I thought he should be the guy who starts the HREF updates.

Now, it’s entirely possible that there are hundreds, even thousands of links on a page at any point in time.  Fortunately, the majority of those links are contained in the scrolling table itself.  It’s on oversimplification, but I can say that those links are already aware of the state the table is in.

That leaves a handful of other links scattered around the interface.  Meaning, the actual process of updating those links is not particularly CPU intensive.  And I even have some nifty “process control” code that makes sure that only one update process can run at a time, and only the most recent one will run.

(fine, I’ll explain how it’s done.  The scroll handler starts the update process as an async process (using a JS timer).  It’s entirely possible that the scroll handler will fire again while the update process is still running – unlikely, but still possible.  So before an update process is started, a random process ID is generated and stored with the table’s other properties.  The update process is then “launched” and is told its process ID.  Once the update process starts to run (and while it’s running) it checks that the table’s record of the update process ID matches the processes own ID.  If there’s a mismatch – which will occur if a newer process is spawned – then the process halts and exits).

Anyhow, the end result is that a few things happen:

  • you can move to another page (zones, lists) and return to your browse results and the table will be positioned where you left it
  • you can resort your results, move to another page, then return to your browse results and the table will be positioned where you left it

Probably the only things that’s left, then, is to store the selections.  Sometimes you make some selections, but then you want to drill into a record and get more info.  This requires you to abandon your selections, or perhaps open the drill details in a new tab.  It would be nice, perhaps, if you could move around and not worry about your selections, knowing they will be there when you return to the browse results.  This is much harder to do though… I mean, there are ways around it using cookies, but I’m trying to avoid those methods.