If you're a Web developer, you know you should target your Web applications to be “mobile first.” This means that your Web applications should look great on a mobile device, and good, if not great, on a desktop browser. There are many techniques you can use to help you develop for mobile. One technique is to use Bootstrap to give yourself responsive styles that change based on the device being used. Another technique, and the focus of this article, is to eliminate HTML tables.

HTML tables, when not used correctly, can cause problems with small screens such as those found on smart phones. This article shows you how to rework your HTML tables to fit better on mobile devices. In addition, you'll learn to use Bootstrap panel classes to completely eliminate HTML tables.

The Problem with HTML Tables

HTML tables are used by many Web developers because they're easy to program, and provide a way for users to see a lot of information like they would on a spreadsheet. But just because something is easy to use and conveys a lot of data, doesn't necessarily mean it's the best tool. There are many reasons why an HTML table isn't suitable for user consumption.

  • A table presents too much data on the page, so the user has too much to concentrate upon.
  • A user's eyes become fatigued after staring at rows and columns of data much more quickly than when data is spread out.
  • It's hard for a user to distinguish between the data in each column because each column is uniform and nothing stands out.
  • On a mobile device, the user frequently needs to pan right and left to see all the data. This leads to an annoyed user, and is very unproductive.

HTML Table on Desktop versus Mobile

In Figure 1, you see a list of tabular product data. This renders nicely on a normal desktop browser because the user has a lot of screen real-estate and they don't need to scroll left and right to see all the data.

Figure 1: An HTML table rendered on a desktop browser
Figure 1: An HTML table rendered on a desktop browser

Look at this same page rendered on a smart phone, as shown in Figure 2. The user is only able to see the left-most column of the table. If they don't know that they can scroll to the right, they're missing some important information. On some mobile browsers, the page may render the complete table, but it's so small that it's hard to read. Either way, the user is forced to interact with their phone to view the data. They must scroll left to right, or maybe pinch or spread with their fingers.

Figure 2: The same HTML table rendered on a mobile browser
Figure 2: The same HTML table rendered on a mobile browser

Create an MVC Project

If you wish to follow along creating the sample for this article, create a new MVC project using Visual Studio. Name the project AlternativeTable. Once you have a new MVC project, add three classes into the \Models folder. The names for each of these classes are Product, ProductManager, and ProductViewModel. Instead of using a database, create some mock data in the ProductManager class. The Product class is shown in the following code snippet:

public class Product
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public DateTime IntroductionDate { get; set; }
    public string Url { get; set; }
    public decimal Price { get; set; }
}

Several lines of the ProductManager class are shown in Listing 1. You need to add a few more Product objects into the list so you can display several rows of data. Or, see the sidebar for how to download the complete sample. You can then copy the ProductManager.cs class into the \Models folder to have several product objects to display while running this sample.

Listing 1: Create some mock data in the ProductManager class

public class ProductManager
{
    public List<Product> Get() {
        return CreateMockData();
    }

    protected List<Product> CreateMockData() {
        List<Product> ret = new List<Product>();

        ret.Add(new Product()
        {
            ProductId = 1,
            ProductName = "Add Angular to MVC Applications",
            IntroductionDate = Convert.ToDateTime("07/25/2017"),
            Url = "http://bit.ly/2u1ofRk",
            Price = Convert.ToDecimal(29.00)
        });

        // MORE PRODUCTS CREATED HERE

        return ret;
    }
}

The last class is a view model that's called from the MVC Controller. Using an MVVM approach to development provides for a nice separation of concerns in your applications. It's also very easy to bind properties in your CSHTML pages to your view model classes. The ProductViewModel class is shown in the following code snippet.

public class ProductViewModel
{
    public List<Product> Products { get; set; } = new List<Product>();
    public void LoadProducts() {ProductManager mgr = new ProductManager();
        Products = mgr.Get();
    }
}

The MVC Controller

You need a MVC controller to load the data and feed that data to the CSHTML pages used to render your page of product data. Right mouse-click on the Controllers folder, select Add > Controller… Select MVC 5 Controller ? Empty from the list of templates and click the Add button. Change the name to ProductController and click the OK button. Write the following code in the Index method.

public ActionResult Index()
{
    ProductViewModel vm = new ProductViewModel();
    vm.LoadProducts();
    return View(vm);
}

This method creates an instance of the ProductViewModel class and calls the LoadProducts method to build the Products property in the view model. The CSHTML page you are going to build uses the Products property of the View Model passed to it to build the HTML table.

The HTML Table View

Create a new folder under the \Views folder called \Product. Right mouse-click on this folder name and select Add > MVC 5 View Page with Layout (Razor) from the menu. Set the name to Index and click the OK button. When presented with a list of layouts, choose _Layout.cshtml from the dialog box and click the OK button. Write the code shown in Listing 2.

Listing 2: Create a normal HTML table as a starting point

@model AlternativeTable.ProductViewModel

@{
    ViewBag.Title = "Products";
}

<h2>Products</h2>

<div class="table-responsive">
    <table class="table table-bordered table-condensed table-striped">
        <thead>
           <tr>
                <th>Product Name</th>
                <th>Introduction Date</th>
                <th class="text-right">Price</th>
                <th>URL</th>
                <th class="text-center">Delete</th>
           </tr>
        </thead>
        <tbody>
        @foreach (Product item in Model.Products) {
           <tr>
               <td> <a href="#" title="Edit Product">@item.ProductName</a> </td>
               <td> @item.IntroductionDate.ToShortDateString() </td>
               <td class="text-right">@item.Price.ToString("c")</td>
               <td>@item.Url</td>
               <td class="text-center"><a href="#"
                       title="Delete Product"
                       class="btn btn-sm btn-default">
                   <i class="glyphicon glyphicon-trash"> </i>
                </a>
                </td>
           </tr>
        }
        </tbody>
    </table>
</div>

As you can see from Listing 2, there's nothing out of the ordinary for this table. You use Bootstrap table classes to help with styling the table. You loop through the collection of product data in the Products property of the ProductViewModel class. Each time through the loop, display the appropriate data from the Product class in each <td> of the table. You should be able to run the application at this point and see your table of product data appear. If you wish to see what this page looks like on a smart phone, you can purchase Electric Mobile Studio 2012 from Electric Plum at https://www.electricplum.com/studio.aspx.

Before you continue with this article, please copy the contents of your CSHTML page into Notepad, or save it to another file. You're going to use this Razor code later in this article.

Remove Table-Responsive Class

There's one more thing to try before you move onto the next section. Remove the attribute class=“table-responsive” from the <div> surrounding the <table> element. Re-run the application and look at the result. You should now see more of the table, but the data is now wrapped within the columns. This can give you a little more data showing in the table, but you're right back to having too much data in too little space. If you have several more columns, it also forces the user to scroll left and right to see all of the data.

Reduce Table to Two Columns

If you reduce your table to two columns, you find that it fits on a small device screen just fine (See Figure 3 and Figure 4). Make the changes to the page you just created by first modifying the columns in the <thead> area, as shown in the following code snippet:

<thead>
    <tr>
        <th>Product Information</th>
        <th class="text-center">Delete</th>
    </tr>
</thead>

You don't want to remove any of the data from your product HTML table; you just want to redistribute it into two columns. Leave the delete button in the last column, but take the code that displays the Introduction Date, Price, and URL columns and put them into the first column, as shown in the following code snippet:

<td>
    <p>
        <a href="#" title="Edit Product">@item.ProductName</a>
    </p>
    <strong>Intro. Date: </strong>@item.IntroductionDate.ToShortDateString()<br />
    <strong>Price: </strong>@item.Price.ToString("c")<br />
    <strong>URL: </strong>@item.Url
</td>

You should now just have two <td> elements remaining in the <tbody> of your table. If you haven't already done so, remove the attribute class=“table-responsive” from the <div> surrounding the

. Run the application, and your screen should look similar to Figure 3, or like Figure 4 if you're running an emulator.

Figure 3: Reduce the table to two columns to see more within the limited space of a smart phone.
Figure 3: Reduce the table to two columns to see more within the limited space of a smart phone.
Figure 4: Reducing your table to two columns makes it fit well on a smart phone.
Figure 4: Reducing your table to two columns makes it fit well on a smart phone.

Using a Bootstrap Panel

Instead of using an HTML table, you might display a list of Bootstrap panel controls that contain your product data. In the sample shown in Figure 5, you see the same data, but the most important data, the product name, is displayed in the panel header area. The other information about the product is displayed within the body of the panel. The actions you can take are displayed within the panel footer area. As you can see, this list of data looks just as good on a normal desktop browser (Figure 5) as on a mobile browser (Figure 6).

Figure 5: Display product data using the Bootstrap panel classes
Figure 5: Display product data using the Bootstrap panel classes
Figure 6: The Bootstrap panel classes look very good on a mobile device.
Figure 6: The Bootstrap panel classes look very good on a mobile device.

Modify the Page to Use a Bootstrap Panel

Remove all of the code below the <h2> element on your page. Write the code in the following snippet below that <h2> element. This code is just a shell of what you're going to display, but sometimes it's easier to build up the panel if you do it a little bit at a time. After typing in the code snippet, run this page to see a Bootstrap panel.

@foreach (Product item in Model.Products) {
    <div class="panel panel-primary">
        <div class="panel-heading">
            <h1 class="panel-title">@item.ProductName</h1>
        </div>
        <div class="panel-body">
            // Display product data
        </div>
        <div class="panel-footer">
            // Display actions to perform
        </div>
    </div>
}

Add the actions for Edit and Delete by using Bootstrap glyphicons within the footer of the panel. Locate the

and add the following code within that <div> element.

<div class="row">
    <div class="col-xs-12">
    <a href="#" title="Edit Product" class="btn btn-sm btn-default"><i class="glyphicon glyphicon-edit"></i></a>
    <a href="#" title="Delete Product"class="btn btn-sm btn-default"><i class="glyphicon glyphicon-trash"></i></a>
    </div>
</div>

Notice the use of the two glyphs for the actions that the user can take. It can sometimes be hard to click on a hyperlink with your finger on a mobile device. They can also be hard to see on a mobile device. I find using large buttons with a graphic gives the user a nice big target to hit with a finger.

Build the Panel Body Using Rows and Columns

Fill in the body of the panel with the rest of the product data. The code in Listing 3 uses Bootstrap row and column classes to display the appropriate product data within the Bootstrap body of the panel.

Listing 3: Use Bootstrap columns to separate labels and product data

<div class="row">
    <div class="col-xs-4 hidden-sm hidden-md hidden-lg">Intro Date</div>
    <div class="col-xs-4 col-md-3 hidden-xs">Introduction Date</div>
    <div class="col-xs-8 col-md-9">@item.IntroductionDate.ToShortDateString()</div>
</div>
<div class="row">
    <div class="col-xs-4 col-md-3">Price</div>
    <div class="col-xs-8 col-md-9">@item.Price.ToString("c")</div>
</div>
<div class="row">
    <div class="col-xs-4 col-md-3">URL</div>
    <div class="col-xs-8 col-md-9">@item.Url</div>
</div>

Let's examine the code in Listing 3 to understand how it was put together. The column width is modified depending on the size of the browser. For anything that's a medium resolution and above, the col-md-3 class is used for the first column and a col-md-9 for the second column. As soon as a mobile device is detected, the col-xs-4 class is used for the first column and col-xs-8 for the second. If you look at the Introduction Date field, you also notice that the words used in the label change depending on the size of the browser. This is one of the great things about using Bootstrap: the ability to hide and display things using simple CSS classes.

Detect a Mobile Device and Switch Views

You've made some great changes to your Product page to make it look good on a mobile device. However, sometimes, your users insist on seeing tabular data. There's no reason you can't have the best of both worlds. When browsing on a desktop browser, you can have a page that looks like Figure 1, but when viewing on a mobile device you can have the page that looks like Figure 6.

Figure 7: Based on device detection, you display one partial page or another.
Figure 7: Based on device detection, you display one partial page or another.

To accomplish this, you're going to use partial pages and some basic device detection code, as illustrated in Figure 7. You're going to write the device detection code in a minute, but first open the ProductViewModel class and add a Boolean property named IsMobileDevice. This value is going to be set by the device detection code you write. This property helps your Index.cshtml page decide which partial page to display.

public bool IsMobileDevice { get; set; }

Create two .cshtml pages: one for the code I had you save earlier in this article, and one for the current code to display the product data. Right mouse-click on the Product folder and select Add > MVC 5 Partial Page… (Razor) from the menu. Name one of these partial pages _ProductTable.cshtml and the other _ProductPanel.cshtml. At the top of each of these partial pages, add the following line of code.

@model AlternativeTable.ProductViewModel

Create the partial page for the desktop browser by getting the Razor code you saved earlier with the original HTML table. Copy the code below the <h2> into the _ProductTable.cshtml file. Create the partial page for the mobile browser by cutting out the code below the <h2> in the Index.cshtml file and pasting that into the _ProductPanel.cshtml file. Now modify the Index.cshtml file to look like this next snippet.

@model AlternativeTable.ProductViewModel
@{ ViewBag.Title = "Products"; }

<h2>Products</h2>

@if (Model.IsMobileDevice) { @Html.Partial("_ProductPanel", Model) }
else { @Html.Partial("_ProductTable", Model) }

Run your application in your desktop browser and ensure that you see the full HTML table. Next, stop your application and set the IsMobileDevice property to a true value. Re-run the application and ensure that you see the Bootstrap panel.

Simple Device Detection Code

There are many methods to detect what kind of browser is running the method in your controller, ranging from very simple to very complex. You can even purchase a subscription to a service for device detection. In this article, you're going to write a simple method to detect a mobile browser. I've found that this method works for most mobile browsers on the market today.

You're going to want to use this device detection method on many controllers, not just the Product controller you created in this sample. Create a controller base class from which all your controllers can inherit. Right mouse-click on the \Controllers folder and add a new class named AppBaseController.

using System.Web.Mvc;
using System.Text.RegularExpressions;

namespace AlternativeTable.Controllers
{
    public class AppBaseController : Controller
    {
    }
}

Open the ProductController.cs file and change this class to inherit from this new base controller class.

public class ProductController : AppBaseController
{
    // REST OF YOUR CODE HERE
}

Next, add a new method named IsMobileDevice() to your AppBaseController class. This method looks at a couple of properties of the Request object to determine whether the browser running the code is a mobile browser. Listing 4 contains the complete code for the IsMobileDevice() method.

Listing 4: A simple device detection method

public bool IsMobileDevice()
{
    bool ret = Request.Browser.IsMobileDevice;
    string userAgent = Request.UserAgent;

    if (!ret) {
        // Use regular expression
        Regex b = new Regex(@"(android|bb\d+|meego)
        .+mobile|avantgo|bada\/|blackberry|
        blazer|compal|elaine|fennec|hiptop|
        iemobile|ip(hone|od)|iris|kindle|lge
        |maemo|midp|mmp|mobile.+firefox|
        netfront|opera m(ob|in)i|palm(os)
        ?|phone|p(ixi|re)\/|plucker|
        pocket|psp|series(4|6)0|symbian|
        treo|up\.(browser|link)|vodafone|
        wap|windows ce|xda|xiino",
       RegexOptions.IgnoreCase |
        RegexOptions.Multiline);

    ret = b.IsMatch(userAgent);

    // Check user agent for certain words
    ret = !ret ? userAgent.ToLower().Contains("iphone"): true;
    ret = !ret ? userAgent.ToLower().Contains("android"): true;
    // etc.
    }

    return ret;
}

The IsMobileDevice() method looks at the Request.Browser.IsMobileDevice property to see if MVC has detected a mobile browser. If this value is not true, you then check a string against the Request.UserAgent to see if you can find some certain keywords. If any of these keywords exist, then you have a mobile device.

If the regular expression fails, check the user agent string to see if it contains “iphone” or “android”. You can add additional checks for other types of mobile devices, such as a blackberry. Instead of hard-coding these values in this method, you might create a couple of key/value pairs in the <appSettings> section of your Web.config file to hold the string to check for the regular expression, and for each of the phone keywords.

Product Controller

Now that you have a device detection method in place, call this method and set the IsMobileDevice property you added to your ProductViewModel class. Open the ProductController.cs file and modify the Index() method in the controller to call this method and set that property as shown in the following code snippet.

public ActionResult Index()
{
    ProductViewModel vm = new ProductViewModel();
    vm.IsMobileDevice = IsMobileDevice();
    vm.LoadProducts();
    return View(vm);
}

Run the application using a desktop browser to ensure that your code detects a normal browser and returns the full HTML table. Next, try running your application from an emulator. If you don't have an emulator, you can change the user agent string using the F12 tools in your browser. You may have to search the Internet for how to change the agent string using your unique browser, as they each work a little differently. If you use Chrome, it's very easy to set the user agent string; if you use IE 11, you have to turn off the compatibility settings in order to get the user agent string to submit correctly.

Summary

Every Web developer should create applications with mobile in mind. More people browse sites with mobile devices than with desktop browsers now. Your Web application should respond accordingly. Eliminating HTML tables goes a long way to making your users happier when browsing data on their smart phones. Adding device detection, and modifying your pages based on the browser type increases your development time, but can keep your users happy.