Time Well Spent

(March 2009 Issue)





Time Well Spent

Use ASP.NET AJAX to Track Time Spent on a Page


By Matt Bohn


A frequent requirement for a Web application is for it to collect metrics to determine how users are interacting with it. One solution is to install Web site statistics software that analyzes Web request logs that are available on your Web server. This works well to determine such metrics as page-hit reporting and visitor trends over time. However, one metric that often is not included in traditional request log reporting is the ability to track how long a user stayed on a Web page. By determining how long a user spends on a given page, you can make better decisions to improve your application.


For example, when a user is filling out an online application, you can determine which steps take the longest and focus on improving the usability of those steps. Or you might be running an e-commerce site and you d like to know which product pages users spend the most time on as an indicator of their level of interest. A user might view Product A and immediately leave the page, but when they view Product B they might stay longer and scrutinize all the information on the page to make a purchase decision. If you could track how long they had been on the page, you could offer them the opportunity to chat with a sales agent after they had been on the page for a certain length of time increasing the likelihood of a sale.


The goal of this article is to demonstrate how you can provide more detailed tracking in your Web applications using ASP.NET AJAX. To do this, we ll specifically demonstrate how to track the length of time a user spends on a page as mentioned in the previous scenarios. The techniques illustrated then can be adapted and used to track a variety of user activity metrics.


To track the length of time a user spends on a Web page, you must be able to capture when the page is loaded and when the user leaves the page. The first task is quite simple; a page load can be tracked by logging it in the PageLoad event of the desired page or by using the JavaScript body onload event to make a call to the server using ASP.NET AJAX. However, capturing when the user leaves a page is more complex. Internet Explorer and FireFox support a JavaScript event named onunload that may be included in the page body tag. This event is fired when a user has clicked a link, typed in a new URL, or closed the browser window, which results in the current page being unloaded. Unfortunately, not all browsers support this event. To provide a complete solution, a JavaScript timer can be set up that will ping the server every n seconds with an AJAX request. In conjunction with the onunload event, the page ping provides a solution that can track the time a user spent on a page in most JavaScript and AJAX-capable browsers.


Setting Up the Web Site

We ll begin by creating a new ASP.NET Web site named UserPageTrackingWebSite_VB and add a new master page named MasterPage.master. Once we have the master page added, we ll set up in the project three additional pages (Page1.aspx, Page2.aspx, and Page3.aspx) that use MasterPage.master as their master page. See Figure 1 for the layout of the project at this step. The master page will contain the JavaScript necessary to capture when a user enters and leaves a page. Each page that uses the master page will automatically have these events tracked.


Figure 1: The project layout.


For the sake of this example, we ll handle all events from JavaScript. The next step is to add the JavaScript code to the master page to capture the page load event, capture the page onunload event, and initialize a timer to ping the server every twenty seconds. To do that we ll add a pagePing JavaScript function that is executed by each of these events. It is called when the page loads by using the body onload event and is called on exit using the onunload event. A recurring ping is established using the setTimeout function in our pagePing function to execute pagePing again after twenty seconds has elapsed. Figure 2 illustrates this code in the master page.


<%@ Master Language="VB" AutoEventWireup="true"

CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>


   User Page Tracking










Figure 2: A recurring ping is established in MasterPage.master.


If you run the code as it appears in Figure 2, you will see an alert box upon page load, an alert every 20 seconds, and an alert if you navigate away from the page. Granted, displaying an alert for each of these events is not the desired result. However, it does illustrate that JavaScript code can be executed for all the events we need to capture, and, if we can execute JavaScript code, we can make an ASP.NET AJAX request to send the data back to the server. You also can see that we are passing a different pingType parameter based on which event is executing the function. This will be used in subsequent steps to tell the Web service which event is occurring.


Setting Up the Database

Before exploring how to make the ASP.NET AJAX request back to the server for the page load, ping, and page exit events, let s create a new database to store the necessary reporting data. Let s create a new SQL 2005 database named UserPageTrackingDB. In this database, create a table named tblPageEvents. This table will store the URL, the session id, the type of the last event that occurred, the timestamp for when the page loaded, the timestamp for the last recurring ping, and the timestamp for page exit. See Figure 3 for the appropriate table schema (note that PageEventID is the primary key and is configured as an auto increment identity column).


Figure 3: The tblPageEvents table in our SQL 2005 database.


Setting Up the Web Service to Receive Event Data

We now have JavaScript events set up in the master page and a database set up to hold the information about the events. The next step is to create a new Web service to receive event data from the client. To do this, add to the Web site a new Web service named UserPageTrackingService.asmx. This will create a file named UserPageTrackingService.vb in the App_Code folder. In this file, add a Web method named RecordPageActivity, as shown in Figure 4.


Imports System.Web

Imports System.Web.Services

Imports System.Web.Services.Protocols

Imports System.Data.SqlClient

Imports System.Configuration



  (ConformsTo:=WsiProfiles.BasicProfile1_1)> _

 DesignerGenerated()> _

Public Class UserPageTrackingService

 Inherits System.Web.Services.WebService


 Public Sub RecordPageActivity(ByVal Url As String, _

  ByVal EventTypeID As Integer)

     Dim connStr As String

     connStr = ConfigurationManager.ConnectionStrings _


     Using conn As New SqlConnection(connStr)

         Dim command As SqlCommand

         command = _

         New SqlCommand("sproc_AddUpdatePageActivity", _


         command.CommandType = _


         command.Parameters.AddWithValue("@Url", Url)

         command.Parameters. _

           AddWithValue("@LastEventTypeID", EventTypeID)

         command.Parameters.AddWithValue("@SessionID", _





     End Using

 End Sub

End Class

Figure 4: Create the UserPageTrackingService.vb file.


You ll notice in Figure 4 that the code calls a stored procedure named sproc_AddUpdatePageActivity. This procedure has a Url parameter, a LastEventTypeID parameter, and a SessionID parameter. SessionID is necessary to determine a series of requests initiated by the same user. LastEventTypeID tells the stored procedure whether a page load event occurred, a ping occurred, or a page exit occurred. In this example, an EventTypeID of 1 indicates a page load, 2 indicates a ping, and 3 indicates a page exit. You ll notice that the parameter passed to the JavaScript pagePing event in Figure 2 corresponds to the parameters used by the Web service and stored procedure. To complete the code to store the data, you ll need to create the stored procedure sproc_AddUpdatePageActivity, as shown in Figure 5. Note how we record a timestamp in one of three columns (EnterPage, LastPagePing, or Exit page), depending on which event type has been passed to the stored procedure.


USE [UserPageTrackingDB]


CREATE PROCEDURE [dbo].[sproc_AddUpdatePageActivity]

 -- Add the parameters for the stored procedure here

 @Url nvarchar(255),

 @LastEventTypeID int,

 @SessionID nvarchar(50)




 DECLARE @EventID int;

 SELECT TOP 1 @EventID=PageEventID

 FROM tblPageEvents

 WHERE [email protected] AND [email protected]



 IF (@LastEventTypeID = 1) BEGIN

   -- This is the page entry so record a page entry

   INSERT INTO tblPageEvents

    (Url, LastEventTypeID, SessionID, EnterPage)


    (@Url, @LastEventTypeID, @SessionID, getdate());


 ELSE IF (@LastEventTypeID = 2) BEGIN

   -- This is a page ping so record the ping by

   -- updating last record added for this

   -- Url/SessionID

   UPDATE tblPageEvents SET

   LastPagePing=getdate(), LastEventTypeID=2

   WHERE [email protected];


 ELSE IF (@LastEventTypeID = 3) BEGIN

    -- This is a page exit so record the exit by

    -- updating the last record added for this

    -- Url/SessionID

    UPDATE tblPageEvents

    SET LeavePage=getdate(), LastEventTypeID=3

    WHERE [email protected];



Figure 5: Create the stored procedure sproc_AddUpdatePageActivity.


With the Web service now coded to save the information to the database, we can add code to the master page to call our RecordPageActivity Web method. To do this we must add a ScriptManager containing a ServiceReference and set the path of the ServiceReference to the location of our Web service .asmx file. We then can call the Web service from the pagePing JavaScript function in our master page. Figure 6 shows what MasterPage.master should contain after this step (note the changes to pagePing).


<%@ Master Language="VB" CodeFile="MasterPage.master.vb"

Inherits="MasterPage" %>


 User Page Tracking






        path="~/UserPageTrackingService.asmx" />









Figure 6: MasterPage.master with a ScriptManager and Web service call from JavaScript.


Now if you open a page that uses MasterPage.master, stay on the page for a few seconds, then navigate away, your activity will be logged to the database. Each row in tblPageEvents contains the necessary information to determine the time that was spent on the page for a given request. Note that for browsers that do not support the body onunload event, the LeavePage column will be null but if a user stays long enough for a ping to occur, the time of the last ping can be used to determine how long they stayed on the page. Therefore, the time spent on the page is determined by calculating the difference between LeavePage and EnterPage when LeavePage is not null. When LeavePage is null, the time spent on the page is calculated by determining the difference between the LastPagePing and EnterPage columns.


Figure 7 illustrates a SQL query we can run to obtain the time in seconds spent on each page. Note that the coalesce SQL function returns the first parameter passed to it that is not null. We could even take this a step further and determine the average time spent on a given page, or use the SessionID we recorded in the database to see the progression of pages a user visited and how long they stayed on each one.


Figure 7: SQL query calculating time on a page.



This completes our demonstration of how to track the length of time a user spent on a page. We saw how we can use the JavaScript body onload and onunload events to capture when a user enters and leaves a page. For browsers not supporting onunload, we used the JavaScript setTimeout function to ping the server at regular intervals to tell us the user is still on the page. From these JavaScript events, we made calls to a Web service using the Web service capabilities available with ASP.NET AJAX. The Web service, in turn, recorded the URL, timestamp, and session information in the database. These same techniques can be adapted to track any user activity that can be captured by JavaScript.


C# and VB.NET source code accompanying this article is available for download.


Matt Bohn is Chief Architect at Smooth Fusion, Inc. (http://www.smoothfusion.com) in Lubbock, TX. He primarily develops and architects solutions using ASP.NET and C#. His projects have included e-commerce applications, marketing Web sites, community Web sites, and bank check-scanning software. Prior to working at Smooth Fusion Matt worked as a software engineer at IBM. Matt is a Microsoft Certified Application Developer and Microsoft Certified Technology Specialist. You can contact Matt via e-mail at mailto:[email protected] or visit his blog at http://fusionovation.com/blogs/mbohn/.




Hide comments


  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.