Using ASP.NET 3.5's ListView and DataPager Controls: Creating an SEO-Friendly Paging Interface
By Scott Mitchell
A Multipart Series on ASP.NET's ListView and DataPager Controls |
---|
This article is one in a series of articles on ASP.NET's ListView and DataPager controls, which were introduced with ASP.NET version 3.5.
|
Introduction
The GridView, FormView, and DetailsView controls all contain built-in paging functionality. By setting a few properties, it's possible to have any of these controls automatically include a paging interface. The ListView, however, does not include built-in paging functionality. Instead, Microsoft decoupled the paging logic from the ListView and moved it into a separate Web control - the DataPager. Paging Through Data with the ListView and DataPager Controls, an earlier article in this series, explored how to use the DataPager to implement a paging interface for the ListView.
By default, the DataPager renders Buttons, LinkButtons, or ImageButtons in the paging interface for the Next, Previous, First, Last, and numeric page number buttons. When clicked, these buttons cause a postback, at which point the appropriate set of records are bound to the ListView. Unfortunately, search engines cannot crawl a site using postbacks; instead, they rely on links they find on your site. Consequently, a search engine will only index the first page of data displayed by a ListView, because it cannot reach the subsequent pages. Also, users cannot bookmark a particular page of data.
The good news is that it is quite easy to modify the DataPager's default behavior. This article shows how to configure the DataPager to use hyperlinks and the querystring to page through a ListView's data (rather than postbacks) to create an SEO-friendly paging interface. Read on to learn more!
The Problem With Postbacks
ASP.NET Web Forms traditionally use postbacks to perform some action. A postback occurs when the user clicks a Button, LinkButton, or ImageButton control, or when they perform some client-side action that has been configured to trigger a postback (such as selecting a different item from a DropDownList control whose
AutoPostBack
property has been set to True). A postback submits the Web Form, prompting the browser to re-request the same page and to send along the name/value
pairs of the <input>
elements within the <form>
. The ASP.NET page is then re-rendered, with the resulting HTML sent back to the browser
for and redisplayed.
The postback model works well for many scenarios. One of its shortcomings, however, is that it does not result in an SEO-friendly website. SEO, or Search Engine Optimization, is the practice of improving the quality or quantity of traffic that arrives to a site from search engines. An SEO-friendly website is one whose HTML is structured and laid out in such a way that increases the site's visibility to search engine. Search engines use automated programs to index a website. These "crawlers" typically start by visiting the site's homepage and then burrowing into other web pages on the site that are linked from the homepage. Crawlers will follow links, but they won't submit forms. If you have a page where certain content is only visible after a postback then that data will not get indexed by search engines.
By default, the DataPager renders Buttons, LinkButtons, or ImageButtons for the paging interface. By design, these controls, when clicked, cause a postback. On postback, the DataPager retrieves the requested page of data to display and binds it to the ListView. From the end user's perspective, they click on a page number (or one of the First, Previous, Next, Last buttons) and, voila, the see the appropriate page of data. The problem is that search engine crawlers cannot "click" the buttons in the paging interface and therefore only the first page of results will be indexed. (This SEO-unfriendly behavior is also inherent in the GridView, FormView, and DetailView controls' built-in paging support.)
Fortunately, the DataPager can generate an SEO-friendly paging interface control. As we'll see momentarily, by setting a single property in the DataPager we can instruct it to render hyperlinks in the paging interface and to use a querystring parameter to indicate what page of data to display.
Moving the Page Number to the QueryString
The DataPager includes a
QueryStringField
property that,
if set, instructs the DataPager to generate an SEO-friendly paging interface. If the QueryStringField
property is set, the DataPager renders the paging interface
as a series of hyperlinks (instead of rendering the paging interface as a series of Buttons, LinkButtons, or ImageButtons). Specifically, each hyperlink has its href
attribute set to the current URL with the page number passed included in a querystring field whose name is the value of the QueryStringField
property.
For example, consider a DataPager control that is configured to show First, Previous, Next, and Last buttons, and that there are five total pages of data being paged through.
Setting the DataPager's QueryStringField
property to "PageNumber" would result in the following paging interface markup when viewing the first page of data:
<a disabled="disabled">First</a>
|
Because we are viewing the first page of data, the First and Previous links are inactive. Inactive links are rendered as hyperlinks with the disabled
attribute set.
The Next link sends the user to PageName.aspx?PageNumber=2
. Here, the PageNumber querystring parameter is set to 2, but more generally it would be set
to currentPageNumber + 1
. The Last link sends the user to the fifth and final page of data.
The screen shot below shows the paging interface when rendered as links. The First and Previous links are rendered as text (and not a link) because their disabled
attributes are set.

If the user were to click the Next link they browser would request PageName.aspx?PageNumber=2
. When the page is rendered, the DataPager automatically
checks the querystring for a parameter named the same as its QueryStringField
property (in this case, "PageNumber"). Since this querystring field has a value of 2,
the DataPager will automatically load the second page of data into the ListView. This time, the paging interface will render the following markup:
<a href="PageName.aspx?PageNumber=1">First</a>
|
And that's all there is to it! Creating an SEO-friendly DataPager is a simple as setting its QueryStringField
property to some value. With this change, search
engines can index more than just the first page of data. Additionally, visitors can bookmark a specific page of data, or email the URL to a specific page of data to a friend
or colleague.
Enhancing the Paging User Interface
The paging user interface shown in the screen shot above leaves a lot to be desired. By setting a few properties and adding a sprinkle of CSS we can improve the paging interface dramatically. For starters, let's enhance the paging interface so that instead of just including First, Previous, Next, and Last links, it also includes numeric page numbers. In Paging Through Data with the ListView and DataPager Controls we saw how to specify the DataPager's paging interface via DataPagerFields. The DataPager in the screen shot above contained a single DataPagerField, namely a NextPreviousPagerField pager field. We can add numeric pages as well by using three DataPagerFields:
- A NextPreviousPagerField for the First and Previous options,
- A NumericPagerField for the numeric pages, and
- A NextPreviousPagerField for the Next and Last options.
<asp:DataPager runat="server" QueryStringField="Page" ...>
|
Note that the first NextPreviousPagerField is configured to that the FirstPageText
property is set to "<<", which will render the text <<
for (rather than First). Also, its ShowNextPageButton
property is set to False so as to hide the Next button. (The Last button is hidden by default.) Similarly,
the second NextPreviousPagerField is configured to suppress the Previous page button and to display the text >> for the Last button.
The screen shot below shows the new paging interface when viewed through a browser.

We can gussy up the appearance with a pinch of CSS. The NextPreviousPagerField and NumericPagerField have properties that allow you to assign a CSS class to the numeric page links,
to the current numeric page link, and to the First, Previous, Next, and Last links. After setting these properties, along with setting the RenderNonBreakingSpacesBetweenControls
property to False so as to omit a space between each link in the paging interface, and adding the necessary CSS rules, the resulting paging interface has been transformed to the following:

Note how the current page number is highlighted. In the above screen shot, the user is viewing page 1, which is why the "1" link has a red background and white foreground, whereas the other links in the paging interface have inverted colors (a red foreground on a white background). Also, when hovering your mouse over a paging link its border and text turns black, subtly highlighting it.
The download available at the end of this article has the complete CSS rules and property settings needed to implement the above paging interface.
QueryStringField
and RenderDisabledButtonsAsLabels
- A Tale of an ASP.NET Bug
As we explored earlier in this article, when rendering links the DataPager adds the
disabled
attribute to hyperlinks that are not clickable, such as the
First and Previous links when viewing the first page of data. Unfortunately, such markup is not XHTML-compliant, as the strict XHTML implementation disallows the disabled
attribute on the <a>
element. The NextPreviousPagerField class has a
RenderDisabledButtonsAsLabels
property, which sounds like the perfect solution. Unfortunately, this doesn't work in ASP.NET version 3.5 - when the DataPager's QueryStringField
property is
set the RenderDisabledButtonsAsLabels
is ignored. The good news is that this bug is fixed in .NET 4.0. (See
Using a DataPager with both a QueryStringField
and
RenderDisabledButtonsAsLabels
for a more thorough description on the problem in ASP.NET 3.5, as well as a workaround.)
In addition to breaking XHTML compliance, this bug also makes it harder to have the disabled buttons styled differently. For instance, the previous screen shot shows the first page of data, meaning that the First and Previous links are inactive, yet those links appear clickable. Granted, if you hover your mouse over them you'll quickly see that they cannot be clicked, but ideally these disabled links would be displayed differently, using a gray text color, perhaps.
My workaround in this situation was to use a touch of jQuery, which is a JavaScript library designed for enumerating, inspecting, and modifying
elements in the HTML DOM. In a nutshell, the page includes a single line of JavaScript code that executes when the page is loaded. This JavaScript code finds all
<a>
elements with the disabled
attribute that use a specific CSS class (pagerButton
), removes that class, and then adds an
alternate CSS class (pagerButtonDisabled
), which displays the inactive links using a gray text color and removing the border. (While not shown above, I have set the
NextPreviousPagerField control's ButtonCssClass
property to pagerButton
, which associates that CSS class with each of the four First, Previous, Next,
and Last links.)
Here is that single line of JavaScript code:
$('a.pagerButton[disabled]').removeClass('pagerButton').addClass('pagerButtonDisabled');
|
With this in place, the paging interface now "grays out" the inactive paging links, as illustrated in the following screen shot.

Conclusion
By default, the DataPager renders its paging interface using Button, LinkButton, or ImageButton controls. When a visitor clicks one of these paging interface buttons, a postback ensues and the requested page of data is displayed. While this model certainly works, it is does not lead to an SEO-friendly website. With this model, search engines won't index beyond the first page of data. The good news is that the DataPager is designed to render SEO-friendly paging interfaces. By setting the
QueryStringField
, the DataPager
renders its paging interface as a series of links, passing the requested page number through the querystring.
Happy Programming!
Further Readings:
Attachments