Optimizing the output of the Content Query Web Part
Content Query Web Part, Customization, SharePoint 2010, Tips & Tricks
Content Query Web Part is one of the most powerful Web Parts provided with the SharePoint 2010 platform. To support all of its functionality however, it produces some additional HTML markup that you might not need in your website. Find out how to get more control of the output of the Content Query Web Part.
Is it a plane? No, it’s the Content Query Web Part!
The SharePoint 2010 platform includes a number of standard Web Parts that you can use as building blocks in your solutions. One of those Web Parts is the Content Query Web Part (CQWP) that you can use to create dynamic content aggregations. But CQWP offers you even more than only the ability to roll-up content. Using the CQWP in SharePoint 2010 you can dynamically filter content, display the results grouped and in columns and, once you’re done, you can publish the query results to an RSS feed! With all those capabilities it’s not that hard to image what rich solutions you can build using a single Web Part.
Analyzing the output of a CQWP
The Content Query Web Part is a very powerful building block for custom solutions. To support all of its functionality however, it outputs some additional HTML. If you ever examined the output of a CQWP that aggregates some content, you might notice some additional HTML markup surrounding what you have configured in your Item Style.
Consider the following Item Style:
<xsl:template name="mavention-TitleAndDescription" match="Row[@Style='mavention-TitleAndDescription']" mode="itemstyle">
<xsl:variable name="SafeLinkUrl">
<xsl:call-template name="OuterTemplate.GetSafeLink">
<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
</xsl:call-template>
</xsl:variable>
<article>
<h2><a href="{$SafeLinkUrl}"><xsl:value-of select="@Title"/></a></h2>
<p><xsl:value-of select="@Description"/></p>
</article>
</xsl:template>
Assuming the CQWP would return a few items in the query, you would expect the output similar to following:
<article>
<h2><a href="article1.aspx">Article 1</a></h2>
<p>Article 1 Description</p>
</article>
<article>
<h2><a href="article2.aspx">Article 2</a></h2>
<p>Article 2 Description</p>
</article>
<article>
<h2><a href="article3.aspx">Article 3</a></h2>
<p>Article 3 Description</p>
</article>
...
Instead, here is what the CQWP outputs out of the box:
<div id="cbqwpctl00_ctl23_g_d077f02d_54d2_4db4_888b_3b4c143a3559" class="cbq-layout-main">
<ul class="dfwp-column dfwp-list" style="width:100%" >
<li class="dfwp-item">
<article>
<h2><a href="http://mavention/blog/Pages/inconvenient-sandboxed-web-templates-navigation.aspx">Inconvenient Sandboxed Web Templates and Navigation</a></h2>
<p>Web Templates are the new recommended approach for templating sites. Combined with Sandboxed Solutions they empower power users and offer them great flexibility and productivity. There is however one inconvenience with regard to how SharePoint Navigation Providers deal with Sandboxed Web Templates.</p>
</article>
</li>
<li class="dfwp-item">
<article>
<h2><a href="http://mavention/blog/Pages/inconvenient-templated-controls-customized-pages-sharepoint-2010.aspx">Inconvenient Templated Controls and customized pages in SharePoint 2010</a></h2>
<p>Using Templated Controls is a great way for separating UI from functionality. Unfortunately as soon as you customize the Master Page or Page Layouts that uses a Templated Control things are likely break. So what is exactly happening behind the scenes and, what’s more important, how should you deal with it?</p>
</article>
</li>
<li class="dfwp-item">
<article>
<h2><a href="http://mavention/blog/Pages/easy-fixing-broken-publishing-pages-mavention-publishing-page-settings.aspx">Easy fixing broken Publishing Pages with Mavention Publishing Page Settings</a></h2>
<p>When working with Publishing Pages it happens once in a while that you apply a broken Page Layout to a Publishing Page and since you cannot navigate to that page to change the layout back the only option seems to either recreate the page with a different layout or fix the layout. There is however another – easier way. Find out how to easily change the layout of a broken Publishing Page using the Mavention Publishing Page Settings solution.</p>
</article>
</li>
</ul>
</div>
Did you notice the additional div, ul and li tags added to the output HTML?
As I mentioned before the additional markup, that the CQWP outputs, is used for properly supporting rendering items in columns and groups. But what if you don’t use it and all you want is your HTML only?
Optimizing the output of the Content Query Web Part
Although the Content Query Web Part adds some extra HTML markup to the output, the great news is, that it all can be removed if you don’t need it. Because the CQWP uses XSLT to render the output, all you need to do to remove the undesired markup is to edit the XSL files used by the Content Query Web Part.
The Content Query Web Part uses three XSL files to render the results of a content aggregation: ContentQueryMain.xsl, Header.xsl and ItemStyle.xsl all of which are stored in the Style Library/XSL Style Sheets folder in the root of your Site Collection. All of the additional markup that is used, whenever you don’t use grouped view or columns, is located in the ContentQueryMain.xsl file, so this is the only file that we will have to modify.
One more thing
The Content Query Web Part can be used for many different purposes within a site. On top of that, the XSL files, that are used by the CQWP are not exclusive to CQWP and are used by other components as well. To ensure that we are not introducing breaking changes, we will customize the ContentQueryMain.xsl file in a way which supports both out-of-the-box and optimized rendering.
Customizing the ContentQueryMain.xsl file to optimize the output of the CQWP
In order to support both out-of-the-box and optimized way of rendering, the output by CQWP we have to be able to distinct which rendering mode should be applied when. One way of doing this, is to prepend the names of Item Styles with a prefix. By checking the name of the Item Style used at the given moment we would be able to determine whether it’s a custom Item Style built by us or an out of the box Item Style that could be used elsewhere on the site and which should use the out of the box rendering mechanism.
Let’s start off by defining the prefix as a global variable so that we can access it everywhere in the XSL file. Add the following code snippet in line 36:
<xsl:variable name="ItemStylePrefix" select="'mavention-'" />
In order to check if the currently used Item Style is built by us, we have to retrieve its name. If you have every examined the XML returned by the CQWP, you know, that the name of the currently used Item Style is stored within the Style attribute on every Row node. To retrieve the name of the Item Style used by the CQWP add the following code snippet in line 37:
<xsl:variable name="ItemStyle" select="/dsQueryResponse/Rows/Row[1]/@Style" />
If you would request the value of the ItemStyle variable at this moment, you would see the following output:
The first place that renders some additional output is the CQWP div in line 45. To avoid rendering this extra div for our custom templates let’s wrap the lines 45-71 with the following code snippet:
<xsl:choose>
<xsl:when test="starts-with($ItemStyle, $ItemStylePrefix)">
<xsl:choose>
<xsl:when test="$IsEmpty">
<xsl:call-template name="OuterTemplate.Empty" >
<xsl:with-param name="EditMode" select="$cbq_iseditmode" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="OuterTemplate.Body">
<xsl:with-param name="Rows" select="$Rows" />
<xsl:with-param name="FirstRow" select="1" />
<xsl:with-param name="LastRow" select="$RowCount" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- lines 45-71 go here -->
</xsl:otherwise>
</xsl:choose>
If the currently used Item Style is one built by ourselves we suppress the standard rendering logic and move straight to rendering the body. One exception to this is the scenario when the query returned 0 items (code snippet line 4). In such case we call the default Empty XSL Template (code snippet line 5).
Another modification that we need to apply, is within the OuterTemplate.Body XSL template: this is exactly the place where additional markup for supporting columns and groups is being rendered. To prevent the additional HTML markup from being rendered we have to wrap lines 109-160 with the following snippet:
<xsl:choose>
<xsl:when test="starts-with($ItemStyle, $ItemStylePrefix)">
<xsl:for-each select="$Rows">
<xsl:variable name="CurPosition" select="position()" />
<xsl:call-template name="OuterTemplate.CallItemTemplate">
<xsl:with-param name="CurPosition" select="$CurPosition" />
</xsl:call-template>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<!-- lines 109-160 go here -->
</xsl:otherwise>
</xsl:choose>
The last modification that we have to apply is within the OuterTemplate.CallItemTemplate XSL template, which by default wraps every row in the li tag. To remove the unnecessary HTML for our Item Styles let’s wrap lines 182-204 with the following snippet:
<xsl:choose>
<xsl:when test="starts-with($ItemStyle, $ItemStylePrefix)">
<xsl:apply-templates select="." mode="itemstyle">
<xsl:with-param name="CurPos" select="$CurPosition" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<!-- lines 182-204 go here -->
</xsl:otherwise>
</xsl:choose>
If we refresh the page now and examine the source we will see clear HTML source just as we expected.
Summary
Content Query Web Part is a rich building block that offers powerful content aggregation and presentation functionality. To support its capabilities, the CQWP produces additional HTML markup, that might not always be desired. The output produced by the Content Query Web Part can be easily optimized by modifying the ContentQueryMain.xsl file.




June 21st, 2012 at 10:44 am
Nice post Waldek. This surely cleans up the output of the CQWP a lot. Thanks!
One thing though… you might want to put the rendering of the RSS feed outside the in the OuterTemplate. This ensures that it's also rendered for your custom style if you enable the feed.
June 24th, 2012 at 11:38 am
Thanks for the feedback! As for the RSS: isn't it a bit like the columns and groups things? If you use it, sure, you should add it, but if you don't, why would you? ;-)
August 13th, 2012 at 1:23 pm
another great post!
August 15th, 2012 at 1:38 pm
Hi Waldek,
I have implemented this code, and it has removed all the extra li tags in my itemstyle. However, after creating my tabular itemstyle and implementing the solution above, the footer for the site, now appears in the content area. I am assuming that a rogue closing div tag has been generated somewhere. Have you encountered this problem before?
August 15th, 2012 at 6:23 pm
Not really, no. I suggest you try to validate your HTML to see if a closing tag is missing somewhere indeed.
August 17th, 2012 at 11:41 am
Hi Waldek,
It would seem that my LastRow variable is not being read by the itemstyle. I followed the instructions exactly as in James Sandbox http://www.jwc3.net/2012/05/passing-lastrow-xsl-parameter-to-custom.html … The itemstyle is completely ignoring the LastRow variable and the associated contents i.e. the closing table tag. Given that I implemented the solution above, are there any other areas that I would have to insert the LastRow declaration?
August 21st, 2012 at 1:19 pm
Never mind. We needed to declare the LastRow variable in an additional place on the contentquerymain.xsl. All working now.
August 24th, 2012 at 2:53 pm
Probably the easiest way to track it is to search for references to CurPos or CurPosition and ensure that LastRow is being references there as well.
November 9th, 2012 at 5:22 pm
Just wanted to thank you also for sharing this – and in such nicely documented style.
I'm in the process of tweaking our CQWP usage (to display news publishing page snippets on main portal page) and your code examples are a great start.
November 29th, 2012 at 7:23 am
I have one question if anything changes in the site,list library , the change should be reflected in the home page (site change)as a link.How we can do this by CQWP.
November 29th, 2012 at 7:17 pm
I'm not sure I understand what you are trying to achieve. Could you explain it in a little more detail?
January 2nd, 2013 at 10:02 am
i am not getting output for
By:| Comments:|Published Date:
How could i get that?
January 4th, 2013 at 7:12 am
I'm not sure I understand your question. Could you elaborate?
January 14th, 2013 at 6:14 am
Hi
This method works well and i can see my style of HTML but it doesn't work if i add "group by" option in CQWP. it doesn't actually renders my groups tags. Would you please help me how can i use this technique to render clean HTML along with grouping option.
January 14th, 2013 at 7:57 am
One of the pieces that are being cleaned up is grouping. If you need to support it, there is little point in cleaning the standard HTML. Otherwise you would have to implement similar logic as to what's available out of the box.
March 1st, 2013 at 7:52 pm
I've gone through some of your post and they all have amazing information.
I'm not sure my problem is related to this post or not. It's about the output text not being rendered as word wrapped in CQWP.
I have a page in my authoring site and in the publishing site I configured the CQWP to display the content of this page. The content is getting displayed but it's not word wrapped and text goes beyond the area of the webpart and hence that part of the text invisible. Can you please throw some pointers to resolve this rendering issue?
thanks!
March 1st, 2013 at 7:59 pm
It seems like it's a CSS-related issue. My guess is that some of the CSS styles applied to that particular piece of content cause it not to reflow.