Wednesday, June 10, 2009

SharePoint jQuery: Setting View Column Width

A very common request for changes to SharePoint list views is how to set the column width. This is not possible to do using the ootb "List Settings", and the common suggested fix is to use SharePoint Designer (SPD) and convert the view into an "XSLT Data View": How can I manage columns widths in list views? Most large companies do, however, prevent the use of SPD.

With jQuery there is no need to use SPD or to convert the view. In the following example jQuery will change the width of the two columns "Status description" and "Type of Work" by changing their CSS style attribute.

Start by looking up the HTML markup for the two table headers using View-Source. I'm using jQuery filters that look for the TH elements using a CSS class selector and a content filter. Then add a Content Editor web-part (CEWP) to your page and enter this script in the source editor:

<!--ADJUST TABLE COLUMN WIDTH-->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" 
type="text/javascript"></script>
<script type="text/javascript">
$(function(){
$("TH.ms-vh2-nograd:contains('Status description')").css("width", "150px");
$("TH.ms-vb:contains('Type of Work')").css("width", "150px");
});
</script>

Note that not all column header text containers are TH elements, that the CSS class name varies by column type and that the jQuery contains(text) selector content filter is case-sensitive. I've also used the shorthand to the $(document).ready() function in the script.

You can also speed up the selector by explicitly specifying the jQuery context, such as the id of an enclosing table DOM element - typically "#MSO_ContentTable". Thanks to Paul Grenier for this tip over at the EndUserSharePoint cross-posting. Also consider using the :first filter to stop looking for elements after finding the first matching column.

You can be even more specific and have different layouts for different views of the list, as the id of the inner table representing the view is the combination of the GUIDs for the list and the view. The jQuery context would then apply to a specific view. I would recommend using the SharePoint JavaScript ContextInfo object for the view to get the GUIDs using the ctx.listName and ctx.view properties.

Next time, I'll show how to create a frozen header row that will stay fixed even when scrolling.

[UPDATE] As the most common problem people have with this is getting their jQuery selector right, please see this post by Chris O'Brien on useful jQuery tools.

49 comments:

storefront2000 said...

Very nicely done! I'd like to cross-post this and your next article on freezing column headers at EndUserSharePoint.com, if you don't mind.

Regards,
Mark

Mark Miller
Founder and Editor,
EndUserSharePoint.com

Kjell-Sverre Jerijærvi said...

Hi Mark

As I love the stuff found at EndUserSharePoint.com, cross-posting is OK with me.

Innovation through sharing, and flexibility and power to the users is our goal.

storefront2000 said...

Posted. http://www.endusersharepoint.com/?p=1743

I hope you'll consider writing a series for us. Thanks. -- Mark

Dhawal Mehta said...

Very useful find.
It will be veryuseful for demanding clients :)

punaro said...

How do you get this to work for a document library? We have long filenames and want to shorten the name column in a particular lib. It appears that each column is defined by a TH wrapping a DIV wrapping a TABLE which has width:100% and DisplayName="Name". Not sure if my jquery selector is wrong, or if this just won't work.

Kjell-Sverre Jerijærvi said...

Yes, some of the columns have complicated innerHtml, such as choice columns that use TD and not TH. So most likely your selector is wrong: e.g. if the shown header is not a text node, then you must make the selector filter match against something else.

You can also use a parent child selector that avoids knowing the CSS class of the column:
$("tr.ms-viewheadertr th:contains('Type of Work')")

It doesn't matter that some inner element har width=100% it will just use 100% of the width of its container element.

There are also some useful jQuery debugger to be found, just google and chose.

joehouck said...

Being a novice I’m unlikely to modify column width in this manner. Is there a simple explanation for what determines width when I import a spread sheet? For sure something happens in the process… not all similar lists I import look alike. What causes one text field to be different from another?

Kjell-Sverre Jerijærvi said...

The column width is by default set to auto when there is no explicit width settings. In auto mode, the total width of the table combined with the number of columns and the size/word-wrapping of the content inside the column cells, will determine the distribution - in an unpredicable manner. That is just the way HTML tables work.

Anonymous said...

Brilliant! Excellent solution! Thanks so much.

Anonymous said...

Hi,
I'm using MOSS2007 SP2. I added the script to my custom list and it didn't work. Any ideas?
Thanks

Kjell-Sverre Jerijærvi said...

Make sure your selector is correct. The tag and class varies for different types of columns. User alert() to show the count of elements found by your selector.

Anonymous said...

Very clever! Could you also set row/cell color conditionally as well?

Don Chaney

Kjell-Sverre Jerijærvi said...

Yes, once you have the table object, the rows and cells are available through the TR[] and TD[] indexers, or use the jQuery selectors such as :odd and :even. Have a look at this tutorial: http://docs.jquery.com/Tutorials:Zebra_Striping_Made_Easy

سحر الموسى said...

is it posiible to upload a video explaining how to do this exactly..

thanx

Kjell-Sverre Jerijærvi said...

No video here anytime soon. There should be some jQuery related videos at e.g. EndUserSharepoint.

Anh-Thu said...

Ok. I am really lost. I have very limited knowledge on the use of SharePoint but have been asked if I can adjust the column. What is the first step I need to do? Add the CEWP and copy the code and change the column name to reflect mine? I thinking I am missing a step. Please help.

Robin Majumdar said...

This is great stuff and much appreciated. I managed to do it for various columns (using the CEWP method)...

but how about if I would want it to work for ALL lists and libraries in a site collection? (I have full access to the server farm...)

Cheers and thanks for sharing!

Robin

Anonymous said...

Do i need to save the js file in the layouts folder? I have tried adding a content editor webpart to the page and but it is still not working.

Anonymous said...

Nothing changes after I add this code to my WSS 3.0 site. Am I searching the wrong TH class/selectors?

Viewing the source this is what is rendered for my TH tag:

>TH nowrap scope="col" class="ms-vh2-nograd">Column X<

The column in my code:
$("TH.ms-vh2-nograd:contains('Column X')").css("width", "100px");

Kjell-Sverre Jerijærvi said...

add some alert() statements to your script and make sure that your function is A) called and B) that your selector finds something by showing its count

أحمد بن عمر باجابر said...

wow
it really helps

thank you

Anu said...

Hi, Thanks for this great post. i am working with MOSS 2007. Unfortunately, I am not able to get this working and cannot figure out what is wrong. Could you please help me.

kind regards,

Anu

Kjell-Sverre Jerijærvi said...

@Anu: sorry, I only have time to answer specific questions that I already knows the answer to.

Anonymous said...

This works correctly when viewed in Firefox but not in IE8. Any thoughts?

Anonymous said...

AMAZING! Now I want to learn JQuery. :)

Kjell-Sverre Jerijærvi said...

IE8 has stricter null reference handling, try using compatibility mode. Anyway, jQuery selectors should work fine even when no elements are found.

Duncan said...

Hi,

I've been trying to center align the headings as well but am having no luck. Please point me in the right direction...

tia
duncan

Anonymous said...

Hi
Unfortunately It did not work. Any pointers pleeeeeease....! I have WSS 3.0

This is what I have in my HTML source..


TH nowrap scope="col" class="ms-vh2" div style="width:100%;position:relative;left:0;top:0;" TABLE style="width:100%;" Sortable="" SortDisable="" FilterDisable="" Filterable="" Name="Title" CtxNum="1" DisplayName="Title" FieldType="Text" ResultType=""

This is what i have in my CEWP source.



script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript">/script
script type="text/javascript">
$(function(){
$("TH.ms-vh2:contains('Title')").css("width", "150px");
});
/script

You mentioned to use alert(). where should I use alert()? inside $(function(){}? I want to see selector found something. Please help... Many thanks.

Kjell-Sverre Jerijærvi said...

The title field, as some other field types, have more complicated markup, so you need to make sure that your selector looks for the text "Title" within the correct element name.

Anonymous said...

Thanks. It is working with
$("TR.ms-viewheadertr TH:contains('Title')").css("width", "200px");
instead of TH.

One problem i still have..
I can not decrease the width after certain size. I can increase the width to any amount. I want the title to wrap once it is beyond 200px. but the column remains wide enough to hold the longest 'Title' in the list in oneline.
Please suggest...

Kjell-Sverre Jerijærvi said...

The table cell will always expand to fit the longest word in the cell, it won't do word breaking.

Anonymous said...

ok, thanks.
Is there any way i could achieve this? word wraping in the column data?

Anonymous said...

Works great on all the columns however, I can't seem to modify the width of columns that are of type "multi-line text".

For example, i used a similar code the user above and although it does sets the width, nothing changes. Any ideas? Thanks!

$("TH.ms-vh2-nograd:contains('Column X')").css("width", "100px"); //<-- Does not work!

DaveWinters said...

Thanks for sharing your expertise.
I can get the content web part to work. But our SP sites are using ssl. ie https://my.domain.com/mysubsite/Lists/My%20List/AllItems.aspx?PageView=Shared

I get the 'security warning' about content not delivered securely. when I answer 'No' the columns are displayed as expected and if I answer 'Yes' the script is ignored. Can this be overcome so my users don't have to choose? or Do I have to drop back to SPD?
Thanks

Kjell-Sverre Jerijærvi said...

Use HTTPS to include your scripts, or turn of the warning for the IE8 zone of your SP site. Don't mix HTTP and HTTPS content in a page.

DaveWinters said...

Thanks, that worked perfectly (you knew it would). I didn't realize it was that easy to just change the src url to https.
<script src="https://ajax.googleapis...

So much to learn, so little time. This opens a whole new world.
-- Dave

Anonymous said...

Does any have the CEWP code to increase the width of a multiline enhanced text box? I tried the posted code but have not had any success.
Thanks for your Help.

See my HTML source below:

TR
TD nowrap="true" valign="top" width="190px" class="ms-formlabel"H3 class="ms-standardheader"
nobrTitlespan class="ms-formvalidation" */span/nobr
/H3/TD
TD valign="top" class="ms-formbody" width="400px"
!-- FieldName="Title"
FieldInternalName="Title"
FieldType="SPFieldText"
--
span dir="none"
input name="ctl00$m$g_59e02d8b_44c1_4cf4_858d_cc3373d69c5c$ctl00$ctl04$ctl00$ctl00$ctl00$ctl04$ctl00$ctl00$TextField" type="text" maxlength="255" id="ctl00_m_g_59e02d8b_44c1_4cf4_858d_cc3373d69c5c_ctl00_ctl04_ctl00_ctl00_ctl00_ctl04_ctl00_ctl00_TextField" title="Title" class="ms-long" /br
/span

/TD
/TR
TR
TD nowrap="true" valign="top" width="190px" class="ms-formlabel"H3 class="ms-standardheader"
nobrmulti/nobr
/H3/TD
TD valign="top" class="ms-formbody" width="400px"
!-- FieldName="multi"
FieldInternalName="multi"
FieldType="SPFieldNote"

jigar said...

Try this ..

$(function(){

$("table[name=Category]").css("width","300px");

});

Vinny Stackhouse said...

Nice simple solution. Perfect and Thank you!!!

This one is going to help me really control quite a few unruly standard views.

Short-Term said...

Sorry, I'm a newbie and struggling to get this to work.

I have a List (Pager Log) with several columns: Date | On Call Person | Paged by | Time of Day | Spent Time (Hour(s)) | Reason | Action Taken

I want to specify the size for the "Action Taken" column.

I did Edit page on the Pager Log list, and inserted the Content Editor web part. I inserted your code in the Source dialog as follows:


[script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"][/script]
[script type="text/javascript"]
$(function(){
$("TR.ms-viewheadertr TH:contains('Action Taken')").css("width", "250px");
});
[/script]

But no luck. No matter what I change that value to for 'Action Taken' it looks the same when I load the page.

I have no idea where that stuff like "TR.ms-viewheadertr TH:contains" is coming from. I See TR tags on the page, but no TH, and I don't see anything in the source refering to "ms-viewheadertr".

Can you help?

Kjell-Sverre Jerijærvi said...

You should download Firebug for Firefox, or use the IE developer toolbar, this will allow you to click on the column and see the HTML structure and CSS class hierarchy of the column. That will help you get the selector right, as view-source isn't always right.

Short-Term said...

Okay, got it working...sort of. It works in Firefox, but not in IE8.

I saw the other commment about that, but do not understand it:

"IE8 has stricter null reference handling, try using compatibility mode. Anyway, jQuery selectors should work fine even when no elements are found."

What does it mean to "try using compatibility mode?" And, what does that last sentence refer to?

Short-Term said...

I should add that the fields I'm trying to control are "multiple lines of text" type fields.

Again, it seems to be working in Firefox, but not in IE8.

Kjell-Sverre Jerijærvi said...

The phrase "no elements found" means that the jQuery selector found no HTML tags, but then jQuery is build to handle such empty (null) sets.

IE8 can be switched into IE7 compatibility mode: http://blogs.msdn.com/b/ie/archive/2008/08/27/introducing-compatibility-view.aspx

If it works in Firefox, but not IE8, that is because the CSS and/or HTML is rendered different. jQuery was built to handle different browsers in different versions, but it might be that SP2010 outputs different HTML+CSS. Use the IE Developer Toolbar to compare the "live" HTML and CSS.

Anonymous said...

You saved my rear end! Thank you. This is the best & easiest solution out there for this particluar problem

Anonymous said...

Great post! When trying this for the first time I would suggest making the 'test' column really big (25% of your screen size). This will allow you to see if the script is really working. If you are trying to decrease the size of a column the script may not appear to work because there is room on your screen for the column to auto expand.

Clem said...

If anyone is interested, here is how to do it with javascript:

function setTableWidth() {
var e = document.getElementsByTagName('th');
for(var i=0;i<e.length;i++)
{
if (e[i].innerText.indexOf('Column Title') != -1)
{ e[i].style.width = '500px';
}
}
}

Wizmaster said...

Hi can someone show how the code would look if we wanted to apply the code to specific views? Sorry my coding is not that good.

Robin Majumdar said...

Wizmaster: A bit late, but for others asking: each view is essentially a unique ASPx page on which you can edit the page, add the CEWP (content editor web part) with the code to manipulate the columns present in that view.