SharePoint 2010 has issues with its scrolling because of the way it implemented the fixed ribbon. Before we look into these issues (and a workaround) let’s first try to understand what SharePoint is doing. Basically, in order to keep the ribbon fixed at the top of the page, SharePoint put’s the scroll bar on the div tag directly below the ribbon (i.e.: s4-workspace) and removes the “normal” scrollbar for webpages (i.e.: the one that is on the body tag). In order to put the scroll bar on the s4-workspace, they set the overflow property of that div tag. But, in order for an overflow property to work, the height and width must be set appropriately. So, SharePoint also runs javascript on the page load to set the height and width appropriately on the s4-workspace div tag.
The issues noted in this blog are particularly bad on Internet facing sites with SharePoint 2010. They are not that big of deal with Intranet sites. So, I’ve found myself using the techniques in this blog on Internet facing sites and not Intranets. These techniques will work for Intranets, but I don’t like to mess too much with a COTS product when I can help it. I can only hope Microsoft fixes this big issue one day.
SP Ribbon Positioning Issues
- IPad and mobile browser use: The reason IPads don’t scroll in SharePoint 2010 is because of how they interact with overflow div tags. Scrolling will work on the IPad if you use the “two-finger” technique. But, nobody will know this is a div instead of a main scroll because SharePoint makes this scroll look like the main scroll. Thus, to the normal user, it just looks like scrolling doesn’t work on your webpage.
- Anchor tags: Anchor tagging to sections of the page didn’t work. If you have one page www.mywebsite.com?#bottom that links to another page and the section it is linking too is below the scroll area, you get stuck there. It looks really unprofessional when this happens.
- Javascript reliance: A core function of a website should not be reliant on javascript (i.e.: the scrollbars).This can produce multiple issues:
- Broken javascript: If any other javascript breaks on the page then the entire site won’t scroll anymore. That is one main reason you don’t make a core function of a page rely on javascript. It is hard to control the other javascript on the page and you can get yourself into bad situations over time with these types of techniques.
- Slow loading javascript: You have to wait until all the javascript loads on the page before you can begin scrolling. This is really annoying sometimes.
Microsoft has addressed how to disable this ribbon behavior: http://blogs.msdn.com/b/sharepoint/archive/2010/04/06/customizing-ribbon-positioning-in-sharepoint-2010-master-pages.aspx
Microsoft is basically saying, you can turn this on or turn this off. So, for public facing sites, you will probably want to turn this off. However, this means you content editors won’t get the ribbon pinned to the top of the page. This will make editing content in SharePoint very, very hard.
In my opinion, the fixed ribbon positioning system in SharePoint is a really good idea. But, the “unconventional” implementation causes too many issues with scrolling. I think Microsoft could have implemented this better. Specifically, Microsoft could use the “conventional” fixed position style in css instead of this “unconventional” technique of using overflow div tags. Let me be clear: I don’t think overflow div tags are unconventional. I just think it is unconventional to use that as the main scrollbar on a page.
Solution
Turn off the ribbon positioning system that SharePoint provides and build your own one with fixed position css styles. The steps are actually pretty simple:
Steps
- Remove the “scroll=no” attribute from the Body tag
Note: At this point most people would tell you to remove the s4-workspace tag from your html. In fact, that is what the Microsoft blog mentioned earlier. However, that causes side affects. The gantt chart of task views will stop working, IE7 popups will stop working and anything else that relied on the s4-workspace to be there will stop working. Thus, I do not recommend removing the s4-workspace tag from the html of SharePoint. Instead follow these steps:
-
Add the following javascript in a script block or an attached javascript file:
//set top padding of the workspace to the height of the ribbon function setTopPadding() { var wrkElem = document.getElementById('s4-workspace'); var ribHeight = document.getElementById('s4-ribbonrow').offsetHeight; if (window.location.search.match("[?&]IsDlg=1")) { //margin works better for dialogs b/c of scrollbars wrkElem.style.marginTop = ribHeight + 'px'; wrkElem.style.paddingTop = '0px'; } else { //padding works better for the main window wrkElem.style.paddingTop = ribHeight + 'px'; } } // bind top padding reset to ribbon resize event so that the page always lays out correctly. ExecuteOrDelayUntilScriptLoaded(function () { SP.UI.Workspace.add_resized(setTopPadding); }, "init.js");
- Add the following css:
body, body.v4.master {overflow:visible !important; height: inherit; width: inherit; } body #s4-workspace {overflow:visible !important; padding-top:44px;} /*This sets up our Ribbon for a fixed position. */ body #s4-ribbonrow{ position: fixed;top:0px;z-index:1000;width: 100%;} * html #s4-ribbonrow {position:absolute;} /* Set the ribbon popups to be fixed position also */ #s4-ribbonrow .ms-MenuUIPopupBody, #s4-ribbonrow .ms-popoutMenu, .ms-cui-menu[id ^= "Ribbon."], .ms-cui-tooltip { position: fixed !important; } * html #s4-ribbonrow .ms-MenuUIPopupBody, * html #s4-ribbonrow .ms-popoutMenu, * html .ms-cui-menu[id ^= "Ribbon."], * html .ms-cui-tooltip { position: absolute !important; } /*Make sure there are no scroll bars on our popup overlays*/ .ms-dlgOverlay {width: 100% !important }
That’s it! Now you have a fixed css taking care of keeping the Ribbon on top. Why is this better you ask? The SharePoint’s solution uses javascript to create the scroll area. This means javascript is responsible for whether the site functions or not. My solution uses a fixed css property and only uses javascript to fix the height of the ribbon. Even if javascript is turned off, my solution will work. In addition, my page will work for the public view on all browser types (including IPads).
I started off writing this blog using techniques I thought of. However, I ran across two issues that were problematic. One of the issues had to deal with popus/overlays and other issue dealt with the padding at the top after the ribbon was “fixed”. So, like any good developer, I turned to Google. Thus, I have to give credit to 2 blogs that solve the same issues. In fact, both of these blogs seemed to have taken the same approach I did to this problem. Thus, I used techniques from each blog to come to a solution I feel is the best of all worlds.
The first blog that helped in this solution was: http://www.webpoint0.com/blog/fixed-width-layouts-scrollbar-ribbon-sharepoint-2010/. This blog showed me how to set the top padding of the ribbon in the most elegant way. However, the fix for the popups was not the best on this blog.
The second blog that helped in this solution was: http://kyleschaeffer.com/sharepoint/sharepoint-2010-scrolling/. This blog showed me how to deal with the popups and the popup overlays. However, the fix for the top padding of the ribbon was not as elegant as the first blog.
Once again, thank you so much to the blogs above. Their techniques along with the techniques I had already figured out created a great solution to this common problem.
Issues
Of course, with any solution that changes the core way a system works, there is bound to be issues. We’ve worked through the most common issues in the approach above, but I am confident we haven’t figured out all the issues. So, as issues come up I will post them here so people understand the trade-offs (and maybe come up with solutions for me).
Issue #1: Calendar hovers and add new
When you hover over a day in the SharePoint calendar the “add new” link comes up. This isn’t working “exactly” right in our solution. The reason is, if you scroll down on the page and then hover over a calendar item, the “add new” link shows up in the wrong calendar day. It works perfectly fine if you don’t have to scroll down (the issue only happens when you scroll). I debugged through the javascript and I found the problem. The core.js file of SharePoint has a function called MenuHtc_GetElementPosition. This function recursively loops through the elements of the html and gets the elements position with it’s scroll position to get the x and y axis of the element. However, it never takes into account the documents scroll position. This kind of makes sense because Microsoft doesn’t use the documents scroll position because it overflows the s4-workspace to do scrolls normally. However, we overrode that behavior and went back to the normal scrolling behavior of browsers. I hoped Microsoft would have realized that people would want to do this, but this is one of those functions where the SharePoint code is a little short sighted.
Experimental Solution: This solution overrides the MenuHtc_GetElementPosition function by creating a customcore.js file that gets called after the core.js file. I left the method exactly the same except for one small change. I subtracted the document.documentElement.scrollLeft from the x axis and document.documentElement.scrollTop from the y axis. This should account for our scrolling now.
Note: Microsoft should consider putting this change into their function in the core.js in the next service pack. It wouldn’t affect their “normal” technique for ribbon scrolling and it would help people that are building custom internet sites on SharePoint. I can’t think of a reason they wouldn’t add this into the product. So, if anyone from the Microsoft SharePoint product team reads this, please consider adding this in the next service pack.
-
Create a javascript file called customcore.js and put the following javascript in it:
function MenuHtc_GetElementPosition(element, relativeToElement) { var result=new Object(); result.x=0; result.y=0; result.width=0; result.height=0; if (element.offsetParent) { var parent=element; while (parent !=null && parent !=relativeToElement) { result.x+=parent.offsetLeft; result.y+=parent.offsetTop; AdjustScrollPosition(parent, relativeToElement, result); var parentTagName=parent.tagName.toLowerCase(); if (parentTagName !="body" && parentTagName !="html" && parent.clientTop !=null && parent.clientLeft !=null && parent !=element) { result.x+=parent.clientLeft; result.y+=parent.clientTop; } parent=parent.offsetParent; } //This is the custom code added to account for scrolling //when the code has been customized to not use //overflows in the s4-workspace result.x -= document.documentElement.scrollLeft; result.y -= document.documentElement.scrollTop; } else if (element.offsetLeft || element.offsetTop) { result.x=element.offsetLeft; result.y=element.offsetTop; } else { if (element.x) { result.x=element.x; } if (element.y) { result.y=element.y; } } if (element.offsetWidth && element.offsetHeight) { result.width=element.offsetWidth; result.height=element.offsetHeight; } else if (element.style && element.style.pixelWidth && element.style.pixelHeight) { result.width=element.style.pixelWidth; result.height=element.style.pixelHeight; } return result; }
-
Save the above solution in 14 hive layouts folder under your language – ex: C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\1033
In addition, save the same file as customcore.debug.js and put it in the same place (Note: if you want a minimum file you can minimize the customcore.js and leave this one around for the debug file)
-
Replace the reference to core.js
<SharePoint:ScriptLink language="javascript" name="core.js" OnDemand="true" runat="server" />
with this:<SharePoint:ScriptLink language="javascript" name="core.js" Defer="true" runat="server"/> <SharePoint:ScriptLink language="javascript" name="customcore.js" Defer="true" runat="server"/>