This post is about a specific issue when running the SP.UI command ‘addStatus’ on page load as well as short discussion on the JavaScript page life cycle.
Initial Problem
When running the SP.UI.Status.addStatus
command upon page load, the status message was being hidden almost immediately in Chrome but worked as expected in IE.
Scenario
We have a concept of ‘archived’ sites. A control is embedded on the page layout for site home pages (default.aspx) which renders javascript which should display a ‘this site is archived’ message. The message should be displayed using SP.UI.Status.addStatus
and was initially implemented as follows (the script is being rendered dynamically using an ASP literal control, I’ll just be considering the final output):
This worked as expected in IE, however the status message would appear for a split second and then disappear in Chrome.
Investigation
The first piece of the the puzzle: What is happening?
After getting deep with Chrome DevTools I found the script responsible for hiding the status message. SharePoint utilises the document.onreadystatechange
handle to run a function called fnRemoveAllStatus
. I think you can guess what it achieves. Why this is being run at this point is beyond me. Importantly, I don’t want to prevent it running in case it serves a purpose that I’m unaware of.
The second piece of the the puzzle: How’s that work?
If a function is assigned to document.onreadystatechange
it will be run as many as four times (depending on when in the cycle the assignment occurs), once for each transition between the following sequence of states:
- ‘uninitialized’
- ‘loading’
- ‘interactive’
- ‘complete’
Good practice would have the function check for the current state and only act once, when in the correct state. Naturally this logic is absent from the fnRemoveAllStatus
function.
The third piece of the the puzzle: What is running when?
$(document).ready
vs $(window).load
vs ExecuteOrDelayUntilScriptLoaded
The difference between these options in regards to what we have just discussed is that $(document).ready
and ExecuteOrDelayUntilScriptLoaded
run when document.readyState
is ‘interactive’ whereas $(window).load
runs when document.readyState
is ‘complete’.
Laying the final puzzle piece: Why’s it working in IE but not Chrome?
When running the code in IE, rather than executing the script block during the ‘interactive’ readyState is was being executed after transitioning to the ‘complete’ readyState and this meant that is was running after the fnRemoveAllStatus
call as we desire. I believe this happens because the sp.js file is being added via a script link control with ‘LoadAfterUI’ set to true which is only understood by IE. I haven’t investigated this last comment, if I am wrong please leave a comment about it below.
Solution
So, the solution is rather simple once you have this understanding. Wrap the command in $(window).load
to ensure it occurs after the fnRemoveAllStatus
method is called during the transition into the ‘complete’ state. Like this:
NB: ExecuteOrDelayUntilScriptLoaded
will also load scripts which are marked to load on-demand. If you are testing in Chrome you may begin to believe it unnecessary to use ExecuteOrDelayUntilScriptLoaded
when using $(window).load
as all scripts have loaded by then. This is true for browsers other than IE. In IE we must use this function to ensure that the script is loaded at all.
As a final note I’d like to add that apart from this specific case I would suggest using $(document).ready
rather than $(window).load
as it will mean that the page loads faster (unless of course your script requires all resources to be loaded before acting. e.g. you are working with images of undefined sizes).
Thank you Paul.
You saved a lot of hours for me!
Cheers!
Thanks very much, Paul! Your complete and thorough explanation helped me not only to resolve tricky status behavior issue, but gained deeper understanding of page js lifecycle in connection with SharePoint. Best wishes!
🙂
Brilliant. Exactly what i was looking for.
Thank you ! Just saved me a lot of time !
Thank you so much! People on Jquery 3.0+ in which the “load()” function has been deprecated, use $(window).on(“load”, function() {} );