For quite some time I’ve been pretty annoyed (and that’s an understatement) of the strange span-tags generated by the output of pages in SharePoint 2010. Not only are they annoying they also make the markup invalid, since the span tags are omitted after the closing html tag (duh!).

Spans, spans, spans

So in order to get to the bottom of this I decided to face my fears and entered debugging mode. It only took me a few minutes to find out what was going wrong, and I didn’t even have to step (almost) through any SharePoint code to find it out. Here’s what I found and how I found it…

First I enabled the Developer Dashboard and turned on the ASP.NET page trace. I immediately noticed the following controls being added to the control tree at a suspicious location. The SPPageStateControl, a top-level control in the control tree, had 17 child controls - the exact same number as empty span tags being omitted. Probably not a coincidence.

The mysterious controls…

I checked the master page (v4.master) and could not find the SPPageStateControl. It must be inserted dynamically then, so I fired up Reflector and did an analysis of that control and found out that it’s instantiated by the WikiEditPage during the OnLoad event. And the control is inserted directly as a child to the page object. Hence the rendering of the span-tags after the closing html tag. The SPPageStateControl inherits from SPControl (which inherits from Control) and produces no output - so this one was not the bad guy.

I turned to the child controls (SaveCommandHandler etc) which all are inheriting from SPRibbonCommandHandler. And the SPRibbonCommandHandler inherits from the WebControl - if you know your ASP.NET basics then you know that a WebControl outputs a span tag, if nothing else is specified. Eureka!

Get rid of ‘em!

So, how do we get rid of these span tags then? We can’t just go into the SharePoint code and override the RenderBeginTag and RenderEndTag of the SPRibbonCommandHandlers, and we can’t change the SPRibbonCommandHandler to inherit from something better; for instance a plain ol’ Control (thanks Waldek). What do we have in our toolset then? ControlAdapters of course!

After a quick analysis of the SPRibbonCommandHandlers (and the controls inheriting from it) I’ve found out that none of them requires to render anything (I’ve not tested every scenario available so I’m not giving guarantees here). I should be safe building a control adapter that makes the SPRibbonCommandHandler not rendering anything, not even a single pesky span. These controls are only present to handle different postback scenarios and handle server-side commands from the Ribbon, so they do not need to do this span-rendering-frenzia.

Using the exact same technique that I use in my SharePoint 2010 Web Parts in Action book to register ControlAdapters (get the book if your looking for the details - or download the WSP linked to at the end of this article), I created a solution that completely removed the spans.

First I created a control adapter like in the following snippet. We just override the Render method and make it do nothing.

namespace Wictor.NoSpans {
    [Guid("78e05555-17b6-44b7-95ea-3db315058b1a")]
    public class RemoveTheSpansAdapter: ControlAdapter {
        protected override void Render(System.Web.UI.HtmlTextWriter writer) {
            //base.Render(writer);
        }
    }
}

This adapter must be registered using a Browser Definition File (.browser). This file contains the original control full name and the target control like this:

<browsers>
  <browser refID="default">
    <controlAdapters>
      <adapter
        controlType="Microsoft.SharePoint.WebControls.SPRibbonCommandHandler,                     Microsoft.SharePoint, Version=14.0.0.0,                      Culture=neutral, PublicKeyToken=71e9bce111e9429c"
        adapterType="$SharePoint.Type.78e05555-17b6-44b7-95ea-3db315058b1a.FullName$,                     $SharePoint.Project.AssemblyFullName$"/>
    </controlAdapters>
  </browser>
</browsers>

The .browser file must be deployed to the App_Browsers folder of the respective web application. This is done by a timer job, that copies the file from the feature to the Web Application App_Browsers file. That is; we’re creating a feature that targets a Web Application. [Commercial] Full code for this is available in the book[/Commercial].

All we have to do next is deploy the control adapter, browser definition file using the WSP and start the timer job via the feature. Next time the page load we will not see the seventeen annoying empty span tags! Phew!

No more spans

Download the WSP file here to get rid of your spans