Question

I see this question has already been asked around a IE9 but which is adding extra columns in random rows of a html table. The root issue seems to be a IE 9 bug that is fixed in IE 10 (but i have a lot of IE9 users)

It states it usually happens with tables built via ajax but i am seeing this on regular pages that output html tables.

There is a workaround Javascript solution but the answer assumes that you were building a table with Javascript (from an ajax call). I am using a partial view (or in some cases just rendering a regular formatted html table directly on a single page) so I wanted to find out if there is a solution to prevent this UI issue on IE9 when you are simply rendering direct html on page.

I want to avoid having to literally have no white space in my actually code as that will be very hard to maintain.

Was it helpful?

Solution

This is possible. For partial views it's simpler, because you can capture the output of Html.Partial directly, modifying it before the response is written to the output stream.

In order to do that, you'd create an extension method, which could look something like this:

public static class HtmlExtensions
{
    public static HtmlString PartialIE9TableFix(this HtmlHelper helper,
        string view, object model = null)
    {
        var partialOutput = helper.Partial(view, model).ToString();
        partialOutput = Regex.Replace(partialOutput, @"/td>\s+<td",
                            "/td><td", RegexOptions.IgnoreCase);

        return MvcHtmlString.Create(partialOutput);
    }
}

As you can see, it's capturing the output of Html.Partial directly, and then performing the replacement on that. You'd use it in your view like so:

@Html.PartialIE9TableFix("YourPartial")

However, to do this for actual views requires a lot more work, and certainly a lot more care when using it. In order to do this, we actually need to capture, and modify, the response stream before it's sent to the client.

The IE9TableFixFilter below is very heavily based on code from Minify HTML with .NET MVC ActionFilter.

using System;
using System.IO;
using System.Text;

public class IE9TableFixFilter : Stream
{
    public IE9TableFixFilter(Stream response, Func<string, string> filter)
    {
        this.response = response;
        this.filter = filter;
    }

    public override bool CanRead { get { return true; } }
    public override bool CanSeek { get { return true; } }
    public override bool CanWrite { get { return true; } }
    public override void Flush() { response.Flush(); }
    public override long Length { get { return 0; } }
    public override long Position { get; set; }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return response.Read(buffer, offset, count);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return response.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        response.SetLength(value);
    }

    public override void Close()
    {
        response.Close();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        //byte[] data = new byte[count];
        //Buffer.BlockCopy(buffer, offset, data, 0, count);
        string s = Encoding.Default.GetString(buffer);

        s = filter(s);

        byte[] outData = Encoding.Default.GetBytes(s);
        response.Write(outData, 0, outData.GetLength(0));
    }

    private Stream response;
    private Func<string, string> filter;
}

The majority of code here is filling in implementations for abstract members of Stream. The important part is what's going on in the Write method.

The version of Write from the article first makes a copy of the bytes of the stream without actually using them. There's no mention there if this is for some specific reason, but it seems useless to me, so I commented those lines out.

Next up, we need to create a simple ActionFilter to apply the response filter to:

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

public class IE9TableFixFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var response = filterContext.HttpContext.Response;
        response.Filter = new IE9TableFixFilter(response.Filter,
            s => Regex.Replace(s, @"/td>\s+<td", "/td><td", RegexOptions.IgnoreCase));
    }
}

Now that's all done, I would strongly recommend that you don't apply this filter globally, instead choosing to decorate it on actions that require its use. The reason for that is because it will naturally incur some performance penalty, so it's best to be explicit about when it's actually needed. You won't need the partial extension method when using this. So simply decorate your actions to use it:

[IE9TableFixFilterAttribute]
public ActionResult Index()
{
    return View();
}

Update

To make the filter attribute more efficient, you could actually just apply it to browsers that contain the user-agent string MSIE 9.0:

public class IE9TableFixFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var request = filterContext.HttpContext.Request;
        var response = filterContext.HttpContext.Response;

        if (request.UserAgent.Contains("MSIE 9.0"))
        {
            response.Filter = new IE9TableFixFilter(response.Filter,
                s => Regex.Replace(s, @"/td>\s+<td", "/td><td", RegexOptions.IgnoreCase));
        }
    }
}

OTHER TIPS

I faced this same problem and ended up on this page looking for a solution. In my case the problem was with HTML rendered by a Repeater control with ASP.Net 4.

I inspected the code generated by the server with Charles and there was no problem with the HTML.

The last sentence in the question lead me to my own simple solution.

I want to avoid having to literally have no white space in my actually code as that will be very hard to maintain.

I simply put comments between the opening of the TR tag and the first TD tag as well as between each TD and the closing TR tag:

<tr><!--
 --><td class="cell">
    Cell 1  
    </td><!--
 --><td class="cell">
    Cell 2  
    </td><!--
 --><td class="cell">
    Cell 3  
    </td><!--
 --><td class="cell">
    Cell 1  
    </td><!--
 --></tr>

This solution means we don't have to use an extension and it won't be hard to read or maintain the code in the future.

Other than clearing white-spaces, you may try one of the following fixes:

  • CSS: td { white-space: nowrap; } if applicable!

  • Force your tables to have fixed layout:

<table style="table-layout: fixed" width="600">
  <colgroup>
    <col width="100"><col width="300"><col width="200">
  </colgroup>
  <tr height="20">
    <td>...</td>
    <td>...</td>
    <td>...</td>
  </tr>
</table>
  • If your web application already supports IE8, you can force IE9 users to use IE8 rendering engine by using: <meta http-equiv="X-UA-Compatible" content="IE=8" />
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top