Adding sticky/floating headers to a SharePoint list

Outcome:

stickyheadersbeforeafter

Intro:

Large lists carry with them a number of challenges, one being the ease of keeping everything straight when you’re thirty scrolls deep into a page. Sticky/floating headers are an excellent addition to your SharePoint lists that keep your headers at the top of your columns no matter where you’re at in the list for ease of viewing. This solution uses a script created by Daniel Stölzner of spoodoo.com and I’ve added a reference to jquery to simplify steps for those of you without jquery built into your master page.

Let’s Do It:

As with most solutions, we have the option to apply this solution to either one or few lists using Content Editor Web Parts (CEWP) or to all lists by editing the master page. I’ll go through the steps of both methods in this post, starting with the CEWP approach.


CEWP (one or few lists)

A. Download this js file and add it to your Site Assets folder via SharePoint Designer or by uploading to Site Assets via SharePoint Online.
Note: You can also just copy and paste the script at the bottom of this post into notepad and save it as StickyHeaders.js in your Site Assets folder)

B. Edit the page of the list (settings icon –> edit page).
chrome_2018-07-20_09-22-11

C. Create a content editor web part on the list(s) you’d like to have sticky/floating headers. (Add a web part –> media and content –> content editor –> add).
chrome_2018-07-20_09-23-35

D. Edit the content editor.

edit web part.png

E. Add path to newly uploaded script (something like /[SITEURL]/SiteAssets/StickyHeaders.js) and click OK.

addscriptreferencetowebpart.png

F. Stop editing page.

stopediting

G. That’s it! Now when you scroll down your list far enough that the actual headers disappear, your floating headers should appear at the top.

stickyheadersbeforeafter

Want for format the header row with colors and font styles? Here’s how.
2018-07-20_09-51-42


Master Page

A. Download this js file and add it to your Site Assets folder via SharePoint Designer or by uploading to Site Assets via SharePoint Online.
Note: You can also just copy and paste the script at the bottom of this post into notepad and save it as StickyHeaders.js in your Site Assets folder)

B. See my post on editing a copy of your master page. We’ll be following the best practice of editing the master page just to add a reference to a custom.js file, explained at the bottom of the post. Then following the directions earlier in the same post, make sure you’ve referenced your custom.js file just before the closing tag in your master page script. You’ll also see directions on uploading your master page again.

C. Once you’ve uploaded your master page successfully, your changes should be reflected immediately.

Now every list on your site should have sticky/floating headers! Comment or tweet me if you run into any issues and I’ll happily help out.

stickyheadersbeforeafter


<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script&gt;
<script type="text/javascript">
/* Sticky headers for all Custom Lists, Document Libraries and administration lists
* ———————————————
* Created by Daniel Stoelzner
* stoelzner.daniel@gmail.com
* http://spoodoo.com
* Copyright (c) 2016 Daniel Stoelzner (Licensed under the MIT X11 License)
* v3.1.1 for SharePoint 2013 and SharePoint Online
* LastMod: 27th of May, 2016
* ———————————————
* Dependencies: jquery – http://jquery.com
* ———————————————
* Add a reference to this file in a CEWP or Script Editor Web Part or reference this file in your masterpage
*/
jQuery(function () {
if(typeof asyncDeltaManager != "undefined"){
asyncDeltaManager.add_endRequest(stickyHeaders);
} else {
stickyHeaders();
}
});
function stickyHeaders() {
window.SHListContainer = [];
function findListsAndAttachHandlers() {
jQuery("tr:has(>th[class^='ms-vh']):visible").closest("table").each(function(){
var list = new List(jQuery(this));
window.SHListContainer.push(list);
list.init();
list.webpart.data("stickyHeaderData",list);
jQuery("#s4-workspace").on("scroll.stickyeaders", {elem:list}, function (event) {
event.data.elem.update();
});
jQuery(window).on("resize.stickyHeaders", {elem:list}, function (event) {
setTimeout(function(){
event.data.elem.setWidth();
event.data.elem.update();
},50);
});
if(list.fixedHeight || list.fixedWidth){
list.webpart.on("scroll.stickyHeaders", {elem: list}, function(event){
event.data.elem.update();
});
}
if(typeof ReRenderListView == "function") {
var ReRenderListView_old = ReRenderListView;
ReRenderListView = function(b, l, e){
ReRenderListView_old(b, l, e);
jQuery("#WebPart" + b.wpq).data("stickyHeaderData").init();
};
}
});
var ribbonHeight = 0;
g_workspaceResizedHandlers.push(function () {
var newRibbonHeight = jQuery("#RibbonContainer").height();
if(ribbonHeight !== newRibbonHeight) {
jQuery(window.SHListContainer).each(function(){
this.s4OffsetTop = jQuery("#s4-workspace").offset().top;
this.update();
});
ribbonHeight = newRibbonHeight;
}
});
var ExpCollGroup_old = ExpCollGroup;
ExpCollGroup = function (c, F, y, w) {
ExpCollGroup_old(c, F, y, w);
var element = ("#tbod" + c + "_, #titl" + c);
var interval = setInterval(function () {
if(jQuery(element).attr("isloaded") == "true" || typeof jQuery(element).attr("isloaded") == "undefined") {
setTimeout(function(){
jQuery(element).closest("[id^=WebPartWPQ]").data("stickyHeaderData").init();
},200);
clearInterval(interval);
}
}, 100);
};
}
function List(list) {
this.list = list;
this.webpart = jQuery(this.list.closest("div[id^=WebPartWPQ]")[0] || this.list[0]);
this.fixedHeight = ["","auto","100%"].indexOf(this.webpart.prop("style")["height"]) + 1 ? false : true;
this.fixedWidth = ["","auto","100%"].indexOf(this.webpart.prop("style")["width"]) + 1 ? false : true;
this.init = function() {
this.s4OffsetTop = jQuery("#s4-workspace").offset().top;
this.list = jQuery.contains(document.documentElement, this.list[0]) ? jQuery(this.list) : jQuery(this.webpart.find(".ms-listviewtable").last()[0] || this.webpart.find("> table")[0]);
this.listType = this.list.find("tbody[id^=GroupByCol]").length ? "GroupedList" : this.list.hasClass('ms-listviewgrid') ? "Grid" : typeof this.list.closest("div[id^=WebPartWPQ]")[0] == "undefined" ? "SysList" : "NormalList";
this.firstRow = this.list.find("thead").length ? (this.listType == "GroupedList" ? this.list.find("tbody[isloaded=true]:visible > tr").first() : this.list.find("> tbody > tr:nth-child(1)")) : this.list.find("> tr:nth-child(2), > tbody > tr:nth-child(2)");
this.prevHeight = this.listType == "Grid" ? this.list.parent().closest(".ms-listviewtable")[0].offsetTop : this.list[0].offsetTop; //little bug in Edge: value wrong after pagination
this.sticky = this.webpart.find("tr:has(>th[class^='ms-vh']):visible").first();
this.stickyHeight = this.sticky.outerHeight();
this.webpartHeight= this.webpart.height();
if(this.listType == "Grid") {
this.list.css({
"table-layout":"fixed",
"width" :"auto"
});
}
if(this.listType == "Grid"){
jQuery("#spgridcontainer_" + this.webpart.attr("id").substr(7))[0].jsgrid.AttachEvent(SP.JsGrid.EventType.OnCellEditCompleted, (function(caller){
return function(){
caller.setWidth.apply(caller, arguments);
};
})(this));
this.sticky.find("a").on("click", this.fixSortFunctionality);
jQuery("th").hover(function(e){
if(jQuery(e.target).parents('.stickyHeader').length > 0){
jQuery(e.target).find(".clip9x6").css("visibility", e.type == "mouseenter" ? "visible" : "hidden").find("> img").show();
}
}).on("mouseleave", function(e){
if(jQuery(e.target).parents('.stickyHeader').length > 0){
jQuery(e.target).find(".clip9x6").css("visibility", "hidden").find("> img").show();
}
})
}
if(this.sticky.find("th:last-child.ms-vh-icon:has(>span.ms-addcolumn-span)").hide().length) {
this.list.addClass("addPadding");
}
this.setWidth()
this.update();
};
this.fixSortFunctionality = function(e){
if(jQuery(e.target).parents('.stickyHeader').length > 0){
var clvp = jQuery(e.target).closest(".ms-listviewtable:not(.ms-listviewgrid)")[0].clvp;
var strHash = ajaxNavigate.getParam('InplviewHash' + clvp.wpid);
var result = {};
strHash.split("-").forEach(function (part) {
var item = part.split("=");
result[item[0]] = decodeURIComponent(item[1]);
});
var prevSortField = result.SortField;
result.SortField = jQuery(e.target).closest("th")[0].thColumnKey;
result.SortDir = prevSortField != result.SortField ? "Asc" : result.SortDir == "Asc" ? "Desc" : "Asc";
var params = $.param(result);
InitGridFromView(clvp.ctx.view, true);
clvp.strHash = params.replace(/&/g, "-");
clvp.fRestore = true;
clvp.RefreshPagingEx("?" + params, true, null);
}
};
this.setWidth = throttleUpdates(function() {
this.sticky.css({
"position": "static",
"display" : "table-row"
});
var stickyChildren = this.sticky.children("th");
var firstRowChildren = this.firstRow.children("td");
jQuery.each([stickyChildren, firstRowChildren], function(){
jQuery(this).css("min-width", 0);
});
var stickyChildrenWidths = [], firstRowChildrenWidths = [];
for(var i=0; i < stickyChildren.length; i++){
stickyChildrenWidths.push(jQuery(stickyChildren[i]).width());
firstRowChildrenWidths.push(jQuery(firstRowChildren[i]).width());
}
for(var i=0; i < stickyChildren.length; i++){
jQuery(stickyChildren[i]).css("min-width", stickyChildrenWidths[i]);
jQuery(firstRowChildren[i]).css("min-width", firstRowChildrenWidths[i]);
}
this.sticky.css("position", this.sticky.hasClass('stickyHeader') ? "fixed" : "static")
});
this.update = throttleUpdates(function() {
if(this.fixedWidth) {
return;
}
this.webpartOffsetTop = this.webpart.offset().top;
if(this.firstRow.length && (this.webpartOffsetTop + this.webpartHeight – this.s4OffsetTop > 0 && (this.webpartOffsetTop – this.s4OffsetTop + this.prevHeight < 0 || this.webpart.scrollTop() > this.prevHeight))){
if(!this.sticky.hasClass("stickyHeader")) {
this.toggleSticky(true);
}
this.sticky.css({
"left": this.webpart.offset().left,
"top" : (!this.fixedHeight || this.webpartOffsetTop < (this.s4OffsetTop + 2)) ? (this.s4OffsetTop + 2) : (this.webpartOffsetTop)
})
} else {
if(this.sticky.hasClass("stickyHeader")) {
this.toggleSticky(false)
}
}
});
this.toggleSticky = function(mode){
if(this.listType == "SysList"){
var headerChildren = (this.listType == "GroupedList") ? this.list.find("tbody[id^=titl]").first().find("td") : this.firstRow.children("td");
var _stickyHeight = this.stickyHeight;
headerChildren.each(function(){
jQuery(this).css("padding-top", parseInt(jQuery(this).css("padding-top")) + _stickyHeight * (mode == true ? 1 : -1));
})
} else {
mode ? this.list.css("padding-top", this.stickyHeight) : this.list.css("padding-top", 0);
}
this.sticky.css({
"position": mode ? "fixed" : "static",
"display" : mode ? "none" : "table-row"
});
mode ? this.sticky.addClass("stickyHeader").slideDown(200) : this.sticky.removeClass("stickyHeader");
}
}
/*
* Throttle function extracted from:
* http://benalman.com/projects/jquery-throttle-debounce-plugin/
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
function throttleUpdates(t,e){function u(){function e(){n=+new Date,t.apply(u,d)}var u=this,a=+new Date-n,d=arguments;i&&clearTimeout(i),a>r?e():i=setTimeout(e,r-a)}var i,n=0,r=50;return jQuery.guid&&(u.guid=t.guid=t.guid||jQuery.guid++),u}
(function () {
if(!jQuery("#MSOLayout_InDesignMode").val() && !jQuery("#_wikiPageMode").val()){
if(jQuery.inArray("spgantt.js", g_spPreFetchKeys) > -1) {
ExecuteOrDelayUntilScriptLoaded(function () {
setTimeout(function () {
findListsAndAttachHandlers();
}, 0)
}, "spgantt.js")
} else {
findListsAndAttachHandlers();
}
if(typeof _spWebPartComponents != "undefined" && Object.keys(_spWebPartComponents).length == 1) {
ExecuteOrDelayUntilScriptLoaded(function(){
var ShowContextRibbonSections = (function fn(){
SP.Ribbon.WebPartComponent.registerWithPageManager({editable: true, isEditMode: false, allowWebPartAdder: false});
SP.Ribbon.WebPartComponent.get_instance().selectWebPart(jQuery("#MSOZoneCell_" + Object.keys(_spWebPartComponents))[0], true);
return fn
})();
ExecuteOrDelayUntilScriptLoaded(function(){
var DeselectAllWPItems_old = DeselectAllWPItems;
DeselectAllWPItems = function () {
DeselectAllWPItems_old();
setTimeout(function () {
ShowContextRibbonSections()
}, 25)
}
}, "core.js")
}, "sp.ribbon.js")
}
var style = ".stickyHeader {" +
" border: 1px solid grey;" +
" background-color: white;" +
" box-shadow: 0 0 6px -2px black;" +
" z-index: 1;" +
"}" +
".stickyHeader > th {" +
" position: relative;" +
"}" +
".ms-listviewtable th .ms-core-menu-box {" +
" top: auto !important;" +
" left: auto !important;" +
"}" +
".stickyHeader th:not([id^='spgridcontainer']) {" +
" border-bottom: 0 !important;" +
"}" +
".ms-listviewtable.addPadding {" +
" padding-right: 26px !important;" +
"}";
var div = jQuery("<div />", {
html: '&shy;<style>' + style + '</style>'
}).appendTo("body");
}
})()
}
</script>
view raw

stickyheaders

hosted with ❤ by GitHub

58 Replies to “Adding sticky/floating headers to a SharePoint list”

  1. Confused on what is file to use. I am able to get this to work but once you click edit or data view it does not work. Can someone please assist?

  2. Nate, This is a wonderful tool which worked beautifully until recently. Now when I navigate to the second page of list, it’s impossible to scroll to the bottom of the page. Instead, the page flashes in both Edge and Chrome. Is there a more recent version of the javascript?

  3. When I print the lists the headers print on each page, has anyone got a solution for this?

  4. Hi, I used the code on my TeamSite and it only worked for the admin group at first. I suspected this had something to do with permissions so I played around with them and established that the sticky headers work for any user that has the following permission checked in settings: “Manage Lists – Create and delete lists, add or remove columns in a list, and add or remove public views of a list.”. Is there any way to make it work for users with lower permissions level, or is the “Manage Lists” permission option required?

  5. will this code work on list views that are grouped? I have gotten it to work fine on ungrouped views, but not grouped.

    1. I’ve noticed it only works on grouped views once you expand one of the groupings. When all groups are collapsed, there’s no data for the headers to “bind” to.

  6. I have noticed when i do the sticky headers i loose my column json formatting. Has this happened to anyone else and can provide a work around?

  7. Can it be correct that the stickHeader does not view when setting up the Style: Default but it works when setting the Style to: Basic Table ???

    1. Each view is kind of its own page. Make sure that in both scenarios the script still exists when editing the page

  8. Hi, Using SharePoint online here. All I can get is that the content header displays the text of the script. It does nothing. Maybe I read this part wrong; C. Create a content editor web part on the list(s) you’d like to have sticky/floating headers. (Add a web part –> media and content –> content editor –> add).
    I created a webpart page, inserted the list, than I insert the content editor part. (its above the list) than i link to the script in the assets folder. It does display the script but nothing further. The list is set to classic.

    1. Comments don’t like script tags…. Just make sure your script file has < script > at the beginning and < / script > at the end (remove the spaces)

  9. I couldn’t get it to work at site level. I updated Seattle.master. I confirmed that is the aster page I am using.

  10. Terrific code but instructions on how to implement are the greatest part of the post. I had some help to orient someof my column headers vertically (270deg). Now they’re too tall for the box the sticky headers sit inside. Where is the dimension of the sticky header box defined. Hopefully I can adjust it myself until it fits my vertical column titles.

  11. So, I’ve tried to figure it out on my own, but seem to be failing at doing so. While I can implement this on our site, when I try to add a new item to the list it’s affecting the sticky/floating heading stops working. Any ideas?

    1. Update 12/20/18: This does work in edit mode, just needs to be present on every view. Edit mode is basically an alternative view.

      The headers won’t work in edit/datasheet mode – are you adding new items that way or individually? Also, the script will need to be added/present to all list views

      1. Yeah, we need to be able to utilize the edit mode to add to the list or modify the contents. So, I guess we’ll just have to do without the sticky header then.

  12. Hi Nate,

    Increased font size and found this is really fantastic. If we can change the color of the sticky header then this will give complete look.

  13. Hi Nate,

    I would like to color complete header with a specific color. Is there any way we can achieve this?

    Thanks for this post. It worked as expected.

  14. Hi Nate,

    Great product.

    I am using Chrome Version 65.0.3325.181 (Official Build) (64-bit) with SharePoint online and the sticky headers are working great.

    When I go to Print to PDF, the floating headers appear rendered in the middle of the page, and block content that I want to see.

    Is there an update to fix this? I understand it may not be possible since it’s most likely a problem with the PDF rendering engine, not the js you wrote.

    A possible solution is to read in when a print request is made with javascript, and to turn off the floating header code when a print request is made. I am looking into that now.

    Thanks for your advice!

  15. Hello Nate – Looking to see if you’ve had any luck implementing this outside of the classic view? I have some JSON I’m using to convert stoplight colors to indicators that get’s lost in classic view.

      1. Hi. I tried replacing the content with V3.2.0.. unfortunately after I refresh the script is getting displayed on top the page instead of the headers. Please let me know what could be the issue.

        1. If you’re able to see the script itself on your page, it’s likely not wrapped in < script> tags

  16. Hi, I used the js but I had problems the moment the header makes the “fix” the size of my changes you know what is this?

  17. This worked but there is one catch. If I have Headings written in CWPE then both floating menu and heading disappers

  18. Hi, I’ve downloaded this version, stickyHeaders_3.2.0.js to a mapped drive that was provided by the page at ” Design Manager: Upload Design Files “. Approved and published a major version for my custom master page that references to the stickyheaders js. But this will only work for Views (of the same document library) that I have added a Script Editor web part to a View’s Edit Page and the headers sticks and works well.

    However, only Site Collection aministrator like me can see the effect. Permission issue? Where can I issue proper permision to all visitors?

    For each View, if I don’t add a Script Editor with the same referce to the stickyHeaders_3.2.0.js then that View will not show the stickyheader. I thought adding the script reference in the master file is just so it will work for all pages/views that uses the master file which already refereced the stickyHeaders_3.2.0.js?

    Thank you

    1. Hi Jasmine, were you able figure out a solution to this problem? I used the code on my TeamSite and it only worked for the admin group at first. I suspected this had something to do with permissions so I played around with them and established that the sticky headers work for any user that has the following permission checked in settings: “Manage Lists – Create and delete lists, add or remove columns in a list, and add or remove public views of a list.”. Is there any way to make it work for users with lower permissions level, or is the “Manage Lists” permission option required?

  19. Nate – this is so darn handy, and it was kind of you to post this. Is it possible to change the headers to be set in a bold font, too?

  20. Thank you very much. That helped a lot cos the file was opening by default in OneDrive. I think I also encountered an issue by implementing it. Everything is working fine in all the lists and views except for using the solution in a “Data sheet” view with permission below admin/moderator -> e.g. for member without delete the solution is not displayed/available. Do you have an idea what might be the reason?

    1. Hi Jakub, were you able figure out a solution to this problem? I used the code on my TeamSite and it only worked for the admin group at first. I suspected this had something to do with permissions so I played around with them and established that the sticky headers work for any user that has the following permission checked in settings: “Manage Lists – Create and delete lists, add or remove columns in a list, and add or remove public views of a list.”. Is there any way to make it work for users with lower permissions level, or is the “Manage Lists” permission option required?

    1. I’ve just added the code itself to the bottom of the post. You should be able to copy and paste it into notepad, save as StickyHeaders.js, add to your Site Assets folder and go from there. Good luck!

  21. I was able to implement this solution but others on my team are unable to see the changes when accessing the list.

    1. Make sure you set permissions on the script or site assets folder so that they can “read” the script or folder its in. As long as they can access the script itself, it will render the result wherever you’re implementing it.

      1. Thanks for the update. I figured it was a permissions issue at first but that looked fine. I just double checked with my team member and she does have access to the Site Assets library and can see the Sticky Headers js file.

        our only difference is that I am a site owner and she’s just a member.

        1. You may have already done this too, but just in case. The script will need referenced on each view of a list. Each view is like a new page in that way. You might also check member permissions on the site level where site assets is. Sometimes I’ll troubleshoot by placing a demo account in the member group, verifying the issue, then moving the demo account to the owner level to confirm it is permissions. Then you can tweak to figure out where a block might be for that member group between the list and the script. Also might explore browsers and see if you notice any blocks or differences. Just some ideas. Good luck!

      2. I did a little more digging and found something is being blocked at a higher level, so it is a permissions issue. Thanks so much for the quick response.

        1. Hi Lyn, could you please describe your solution? I used the code on my TeamSite and it only worked for the admin group at first. I suspected this had something to do with permissions so I played around with them and established that the sticky headers work for any user that has the following permission checked in settings: “Manage Lists – Create and delete lists, add or remove columns in a list, and add or remove public views of a list.”. Is there any way to make it work for users with lower permissions level, or is the “Manage Lists” permission option required?

  22. hi It’s working good. But the only issue is the labels are moved a bit closer so the values of one field appear to be under other field. Please let me know where I should change

    1. Hi Sandhya, often people encounter these oddities if they have other styling or custom branding applied to the site or page. In one of my environments, I had to reset the default font size to 1em and added *, ::after, ::before {box-sizing:content-box!important;} in my style to get my alignment back. It just takes some css tweaking to get it to work with the rest of your competing styles.

  23. Hi, thanks to your instructions I finally got it to show up for a web part. Question: is it possible to get this to work when viewing/editing the list when it isn’t added as a web part. I.e. if you go to Site Contents and then click on the list object and it opens the part when you can edit the list.

    1. Hi Miles, that’s good news. As for the “main” list vs webparts, this solution will only work for “main” lists in classic view. If you’re in classic view, you should be able to do Settings –> Edit Page –> Add a web part above the list.

  24. I have tried implementing the stickyheaders.js script but have had no luck. using a webpart it just shows the script content. Trying to use it in the master pages (edited both oslo and seatlle) has no effect that I can tell. I am trying to implememetn on an Office 365 sharepoint list. In the master pages I followed your steps for custom.js. but no luck getting the stickyheaders to work. Any help would be greatly appreciated.

Leave a Reply to JakubCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.