Cleaning Up the SharePoint Content Query Webpart Output w/ Option to Render in Columns
I want to start off by giving credit where credit is due because nothing truly is from scratch anymore. If you need something – chances are someone out there has already been through all the headaches to get there or at least has done something similar and gives you that little nugget of information that helps you get over the mountain. There were 2 articles that I found that really sparked this whole coding adventure.
- Waldek Mastykarz wrote a really great blog on Optimizing the output of the Content Query – This was the basis for what I was trying to do originally.
- Peter Holpar provided the code for adding columns in this great article – Displaying results in multiple columns using the Content Query Web Part – the other direction
For the most part the output of the SharePoint content query web part (CQWP) is sufficient. But in some cases the additional unordered list HTML might cause some styling headaches if you need things to be in the same parent container or need all of the items to be rendered one right after the other; not in separate <li>’s. I wanted to give the option to clean up the output and render the items in columns but not take away any of the normal functionality of the CQWP – mainly the grouping.
- CQM-v3.0 – replace your whole /Style Library/XSL Style Sheets/ContentQueryMain.xsl file with the contents of this – every line that I’ve added or edited is commented with detail
- Item Style – use this as a template for any item styles you want to clean up and output into columns – just make sure all the item styles you want to ‘clean up’ have the prefix ‘clean-‘
- CQM-O365-v1.0 – this is the O365 version
- clean-CQWP-columns – originally I had the CQM file setting an inline width on the columns – this was ok but ran into some projects since that required responsive styling so I removed the inline styles from the CQM and added this in an external JS file…\the function when called needs a breakpoint set where the columns collapse on mobile devices – if you never want them to collapse you can set that value to 0 (zero)
- SummaryLinkMain – replace your whole /Style Library/XSL Style Sheets/SummaryLinkMain.xsl file with the contents of this
- update SummaryLinkMain.xsl – fixed error on Summary Links Web Parts when the itemstyle is added to the itemstyle.xsl (because of missing templates that are being called)
- added O365 support
- added responsive JS
OOTB vs New ‘CLEAN’ Output
This is an example of OOTB rendering of items. Seems fine right?
The HTML however is a bit too much for just this…
Take a look at how much cleaner the HTML is for the same items – using the new item style.
Here is the output the item style produces with 4 columns.
…and with grouping.
…and with more than 1 column set in grouping.
Item Style Explanation
In Peter’s article about building columns all the calculations were done in each item style. His was fine as far as code length goes but as I was adding variables for grouping the item style eventually swelled to about 150 lines long. I decided to break all the calculations out into templates for the HTML surrounding the actual item output. So all of that is done in the ContentQueryMain.xsl by passing variable data back to the main xsl.
You will specify the number of columns you want in your output by changing this parameter number. (It’s in 2 places in the item style – where it calls the clean-item-header template and the clean-item-footer template)
<xsl:with-param name="columns" select="4" />
The hidden variable that stores the value of the site column @GroupByName updates the XML of the CQWP to be used in the ContentQueryMain.xsl document to help with grouping variables. This must remain in each of your item styles so that the field name can be updated in the CQWP edit panel.
How I Kept the Grouping Functionality
With the detailed articles I credited above and all my comments in the xslt documents I didn’t feel the need to go into full detail on what is going on behind the scenes with optimizing the output of the CQWP. But I DID want to talk about how I was able to keep the grouping functionality.
The way the column output (without grouping set in the CQWP) works, is it counts the TOTAL number of items, divides it by the number of columns set and splits the items up appropriately by calculating it’s current position. So the challenge here was to be able to count the items PER GROUP to set as the TOTAL for each, divide each of those by the number of columns and then use the current position again to split up into separate columns within each group.
Unfortunately using position() alone wasn’t going to cut it because items in separate groups kept their position sequentially from the previous groups. Like this…
What it should be is this…where the position of the item resets to 1 and continues per group.
There were 3 main pieces to this puzzle that made all of this possible – the KEY, a variable to count TOTAL ITEMS PER GROUP and a variable to count TOTAL PRECEDING GROUPS ITEMS.
The KEY that I’m targeting is the @GroupByName attribute of each ROW node – (remember @GroupByName comes from the hidden variable in the item style that allows you to specify the site column you are grouping on – it becomes an attribute in CQWP xml).
The TOTAL ITEMS PER GROUP template uses Muenchian grouping to count items per group. It references the KEY as it cycles through the items and only counts the items if they have the same @GroupByName category. We call this template later during the item build.
To count the TOTAL PRECEDING GROUP ITEMS I created a template to be called later so that I could pass the name of the @GroupByName of the current item. Counting the TOTAL PRECEDING GROUP ITEMS allows us to take the current position of our item and subtract the number of items in the previous groups so that we can mathematically reset the position to 1 in EACH group. So for example if the current position is 4 but is in the second group we would take 4 and subtract the 3 items in the previous group to get 1, if the current position is 9 but is in the second position in the third group we would take 9 and subtract the 7 items in the previous groups to get position 2 in the third group.
The rest of the magic is done in the header and footer templates when called from the item style. Those templates use variables passed from the item style as well as the global variable $cbq_isgrouping that is set at the top of the ContentQueryMain.xsl to determine which total item value or current position values to use.
Please comment with any questions, suggestions, success stories of using it, etc.