Showing posts with label _spBodyOnLoadWrapper. Show all posts
Showing posts with label _spBodyOnLoadWrapper. Show all posts

Thursday, June 6, 2013

SharePoint 2010 and the Chrome JavaScript Bug

This issue is also known as the missing scrollbar bug, because the vertical scrollbar does not appear in Chrome when the problem happens.  However the scrollbar is just a symptom of a larger problem going on with the page and this article provides a work around.

The Problem

When viewing SharePoint 2010 from the Chrome browser, intermittently the page will load but fail to call it’s JavaScript initialization functions leaving the page only partially functional.  Abnormalities include:

  • Parts of the ribbon bar may be unresponsive
  • The vertical scrollbar is apparently missing
  • Many native SharePoint JavaScript libraries don’t load including: sp.js and sp.core.js
  • Any JavaScript functions registered to execute after page load with the ExecuteOrDelayUntilScriptLoaded, or the _spBodyOnLoadFunctionNames methods do not get called
The Cause

From what I can tell, the problem is caused by this piece of JavaScript found in the SharePoint page source that references the search box.

image

Search Initialization Code
  1. // append an onload event handler
  2. var S3031AEBB__onload = document.body.onload;
  3. if (typeof document.body.onload == 'function') {
  4.     document.body.onload = function () {
  5.         S3031AEBB__onload();
  6.         document.getElementById('ctl00_PlaceHolderSearchArea_ctl01_S3031AEBB_InputKeywords').name = 'InputKeywords';
  7.     }
  8. }
  9. else {
  10.     document.body.onload = function () {
  11.         eval(S3031AEBB__onload);
  12.         document.getElementById('ctl00_PlaceHolderSearchArea_ctl01_S3031AEBB_InputKeywords').name = 'InputKeywords';
  13.     }
  14. }

This code is crudely appending an initialization function to the body tag onload attribute.  By looking at the SharePoint body tag we can see why this is a sensitive area because it is where the _spBodyOnLoadWrapper function is registered.  The _spBodyOnLoadWrapper function is responsible for starting a chain of events that initializes the behavior of the page once it has finished downloading.

SharePoint 2010 Body Tag
  1. <body scroll="no" onload="if (typeof(_spBodyOnLoadWrapper) != 'undefined') _spBodyOnLoadWrapper();" class="v4master">

Why does this only break Chrome?  I suspect what is happening is that the Chrome browser has a different way of interpreting the code and corrupts the body.onload attribute.  Decided by a race condition, this happens sometimes before the page has the opportunity to execute the body load event.  When this happens, the _spBodyOnLoadWrapper function never executes and therefore a dozen client systems usually running in the background of a SharePoint page never initialize.

The Work Around

I read some people had success by removing the search box completely from the masterpage.  This did not work for me and the possibility of removing the search box is not always an option for many environments.  I also saw many solutions which required including the jQuery library on the page.  While I have nothing against jQuery, I don’t like this solution because it relies on including an entire external library on every page load unnecessarily.  My solution accomplishes the same task using SharePoint’s native Microsoft Ajax.

Put this code at the bottom of the master page file just before the closing body tag.

Work Around Code Block
  1. <script type="text/javascript">
  2.     /*****************
  3.      *  
  4.      * Code to handle the SharePoint / Chome bug
  5.      *
  6.      *****************/
  7.  
  8.     function chromeNudge() {
  9.         /// <summary>
  10.         /// If SharePoints body onload handler has not fired yet
  11.         /// this function calls it manually
  12.         /// </summary>
  13.         if (!_spBodyOnLoadCalled) {
  14.             if (window.console) {
  15.                 window.console.log('Chrome Bug: _spBodyOnLoadWrapper did not fire, calling manually.');
  16.             }
  17.             _spBodyOnLoadWrapper();
  18.         }
  19.     }
  20.  
  21.     function chromeNudgeDelay() {
  22.         /// <summary>
  23.         /// If the current browser is Chrome, set a Timeout
  24.         /// to call chromeNudge to at that time evaluate
  25.         /// whether the onload wrapper needs a "nudge"
  26.         /// </summary>
  27.         if (navigator && navigator.userAgent && /chrome/.test(navigator.userAgent.toLowerCase())) {
  28.             setTimeout(chromeNudge, 250);
  29.         }
  30.     }
  31.  
  32.     // call chromeNudgeDelay after MS Ajax init event (aka body load)
  33.     Sys.Application.add_init(chromeNudgeDelay);
  34.  
  35. </script>

The chromeNudgeDelay function executes after the body load event when MS Ajax calls it.  It first checks that the browser is Chrome.  If it is, it then sets a timeout to delay a quarter of a second before calling chromeNudge.  This timeout gives the page the opportunity to load correctly without intervention.  The chromeNudge function then executes and examines the page to see if _spBodyOnLoadWrapper did already execute.  If it has not, chromeNudge calls the _spBodyOnLoadWrapper function itself, and the page loads as it is supposed to.

I consider this to be a hack, but I like it better than rewriting Microsoft’s code in this case.