Lottery Post Journal

How to get rid of the dreaded pound sign

This is my next installment of technical blog entries dealing with technology-related techniques I've created during my re-architecting of Lottery Post.  Lottery Post is built from Microsoft technologies, including ASP.NET 2.0 (running on IIS 6.0), SQL Server 2005, and Microsoft's Atlas technologies, soon to be called the Microsoft AJAX Library, ASP.NET 2.0 AJAX Extensions, and ASP.NET AJAX Control Toolkit.

This entry deals with an issue that some people may not know about, others may not care about, and the rest know exactly what I'm talking about:  the dreaded pound sign (#) in a web page URL (link).

Normally when a new URL is entered into a web browser's Address line, the browser jumps to a new page.  However, when a pound sign (plus an identifier name) is added to the end of a web page's URL, the web browser jumps to a new location within the page.

For example, if you're scrolling down a long web page and need to go back to the top of the page quickly, some pages include a "TOP" link, which takes you to the top of the page.  The way MOST web sites do that is to include a "name" tag in the HTML code near the top of the page, like this:  <a id="top"></a>  (or it could be <a name="top"></a>).  You don't actually see the tag, but it is there as a bookmark in the page.  Then, the "TOP" link is coded like this:  <a href="https://www.lotterypost.com/#top">TOP</a>.

When you click on the TOP link, it adds "#top" to the URL in the browser's Address line, and the page jumps to the tag named "top", which is located at the top of the page.  So, to the user, they have a nice feature:  a way to quickly jump to the top of the page.

But, there are a couple of annoyances with this process that as web users and developers, we have learned to live with.  The annoyances, as I see them, are:

  1. When the link is clicked and the pound sign and tag name (like "#top") are added to the URL, the web browser has actually created a new page in its history chain.  The web page itself is not reloaded, but now when we click the Back button it essentially stays on the same page, and jumps back to where we were when we clicked the TOP link.  In my view, the purpose of the Back and Forward buttons are to move back and forward between pages, not positions on a page.  So, the pound sign wrecks the functionality of the Back and Forward buttons.
  2. The second annoyance affects web developers.  When a URL containing a pound sign is examined through program code on the server, the pound sign, and everything after it, is truncated.  There are techniques for obtaining everything after the pound sign, such as this one, but most developers don't know about the techniques, or would have a hard time implementing them.
  3. The third annoyance is most dear to my heart, as the developer of the Lottery Post forum software, and that is to be able to permanently identify a link to a piece of content (in this case a forum post), and if that content is in the middle of a page, jump directly to it.  Before coming up with the technique I'll describe below, the way I did that was to add the dreaded pound sign to the end of the URL, making the page jump directly to the content when the page loads.  But that meant that a post's PermaLink (permanent link), a link that is supposed to be valid forever, has a pound sign in it.  Yuck!  A PermaLink is supposed to identify a piece of content, not identify a bookmark location on a page.  What if the user can select other formats for viewing posts, and it is undesireable to have a pound sign (jump) in the URL?  For example, what if that post is the only thing on the page?  Obviously the pound sign is not ideal in that case.

So annoyance #3 prompted me to create a new technique.  I needed a way to create a PermaLink to every forum post, without having a pound sign in the PermaLink, and I needed to create a technique that would mimic the pound sign, and have the page jump down to the post after the page loads.

The solution was pretty straight-forward, and in front of my face for a long time, but just required putting a couple of pieces together.  The genesis of the solution came from a technique I had seen to jump to the top of a page using JavaScript, without using the pound sign.

In Lottery Post, I don't use the code <a href="https://www.lotterypost.com/#top">TOP</a> to jump to the top of a page, I use <a href="javascript:scrollTo(0,0)">TOP</a>.  That JavaScript function call scrolls the browser window to the coordinates specified, in this case "0, 0", which is the coordinates of the top-left corner.  (You can see an example of this technique in action on the Pick 6 Wheels page — check out the "Top" buttons along the right side of the page.)

There are many benefits to using scrollTo() over the pound sign, including:

  • The performance is much quicker
  • In Internet Explorer you don't hear that "click" sound that indicates going to a new page
  • There is no pound sign inserted into the URL, so the Back button functionality is not wrecked
  • As a developer, there is no need to insert a <a id="top"> tag into the HTML.

The downside of using scrollTo() is that you need to know the pixel coordinates of where you want to jump to, which for a long time ruled out my usage of the function for anything other than going to the top-left (since it would always be coordinate 0, 0).

But after noodling on this for a while I came to realize it would not be such an enormous challenge to eliminate that pound sign after all.  I would need two things:

  1. A way to locate a specific element's y-coordinate on the page (since x will always be 0, the left side of the page) and jump to that coordinate, and,
  2. A way to kick off step 1 right after the page loads.

All of this would be created in JavaScript.  Since JavaScript is required in order to use Lottery Post (and most other forum web sites) anyway, this did not create any new requirements on the part of users.

So the first function I created was called ScrollToElement(), which finds the y-coordinate of the element passed to it, and jumps there.  Passing the object itself to the function is better than passing an ID and having the function lookup the element in the DOM, because (among other things) it allows constructs such as < ..... onclick="ScrollToElement(this)">.

function ScrollToElement(obj) {
   var posY = 0;

   while (obj != null) {
      posY += obj.offsetTop;
      obj = obj.offsetParent;
   }

   window.scrollTo(0,posY);
};

This is a pretty common technique.  It works by getting the y-coordinate of an object, relative to its containing element.  If the containing element is anything other than the whole page itself, it loops again, adding the coordinate of the containing element's containing element, and so on, until it reaches the whole page level, in which case the containing element is null.

Most HTML pages written will have containing elements, as they are used to create page layouts.  Unfortunately, there is no property of an element that gives its coordinates relative to the entire page, only to the containing element, so this jury-rig is necessary.

The best place to put this function is in the <head />.  Lottery Post uses its own library of common functions that is included on each page, and this function is a good one to include in there.

So now we need a way to kick it off.

First, we want to be sure that the entire page is loaded before attempting to jump to the element, because otherwise the function may run before that element is loaded, and no jump would occur.

So the first thing I did was to create a really good generic JavaScript function for adding tasks to the page's onload() event.  I can't assume that this will be the only thing kicked off when the page finishes loading.  So here it is:

function AddLoadEvent(func) {
  var oldonload = window.onload;

  if (typeof window.onload != 'function') {
      window.onload = func;
  }
  else {
      window.onload = function() {
        if (oldonload) {
            oldonload();
        }

        func();
      };
  }
};

Someone who is pretty familiar with JavaScript will be able to go through the code for this function and understand what's happening in there.  For those who aren't JavaScript hacks, it basically looks at what is currently setup for the page's onload event, and if there is currently an onload function setup, it dynamically creates and assigns a new function to onload, which calls the old function, and then calls what we added to it.  If there was nothing already assigned, it just assigns our function as the onload function.

The function can be added to the page in any of the normal methods, using the <script /> tag, preferably in the page's <head />.

Then, all that's needed is to write the AddLoadEvent() function (with ScrollToElement() as the argument) somewhere in the body of the page.  That can be accomplished using the ASP.NET RegisterClientScriptBlock method in either the page's Load or PreRender subroutine:

Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), "ScrollToPost", "AddLoadEvent(function(){ScrollToElement(document.getElementById('ElementID'));});", True)

ElementID is substituted with the DOM element ID of the block tag containing the content we want to scroll to.  For example, many times <div /> tags are used to surround a post's content.  The div tag would be given an id attribute, and that id would be substituted for ElementID.

When those three pieces are put together on a page, the pound sign can finally be eliminated!

Using that technique, Lottery Post now has good PermaLinks to individual posts like this: https://www.lotterypost.com/thread/141544.htm?get=679422

Notice that there is no pound sign at the end, yet if you click the link it behaves exactly like it was there.  After the page finishes loading, the web browser jumps down the page to the post referenced in the "get=xxxxxx" portion of the URL.

This technique has great application for forum owners, but it can also be used by anyone who wants to avoid the annoyances of the dreaded pound sign.

0 Comments:

Post a Comment

<< Home