7

I have javascript code included in my sharepoint site. When I load the page the first time, it is executed, but when I load it the second time, it is not running. It get's loaded in correct order but the functions inside are not executed (but only on second load)

The first time I load the page by entering the url in the webbrowser, the second time I load the page by clicking the link inside sharepoint website (same url). It seems to me that this problem occurs because it is not a complete pageload. Looks like some ajax magic to me.

This is how I have implemented the js ressources: In my aspx file I have those lines:

<asp:content contentplaceholderid="PlaceHolderAdditionalPageHead" runat="server">
<SharePoint:ScriptLink language="javascript" name="~site/SiteAssets/js/jquery.min.js" OnDemand="false" runat="server" Localizable="false" LoadAfterUI="false"/>
<SharePoint:ScriptLink language="javascript" name="~site/SiteAssets/js/newoverview.js" OnDemand="false" runat="server" Localizable="false" LoadAfterUI="true"/>
<SharePoint:CssRegistration ID="CssRegistration3" Name="<% $SPUrl:~site/SiteAssets/css/newoverview.css%>" runat="server"/>
<SharePoint:RssLink runat="server"/>

jquery.min.js

function $_global_jquery() {
    //jQuery code goes here
}

$_global_jquery();

newoverview.js

_spBodyOnLoadFunctionNames.push("runAfterEverythingElse");
function runAfterEverythingElse(){
   /*  my code */ 
   html = 'test';
   jQuery('table[summary="test"]').html(html);  
}

Take note that my code is working, as I can see it working when loading the page the first time!

The question
Why is the javascript not executed when opening the page inside sharepoint?

Peter
  • 225
  • 1
  • 3
  • 10
  • Does it run too soon if you put your html modification code in side jQuery ready? I.e., $(function() { // your code } – Jussi Palo Feb 18 '16 at 07:22
  • @JussiPalo No, I had that before but it made no difference. – Peter Feb 18 '16 at 07:34
  • You most probably have the Minimal Download Strategy Feature enabled, disabling that is quickest way to "workaround" this issue unless you really need/want to have that enabled. – Jussi Palo Feb 18 '16 at 07:43

4 Answers4

5

If you have Minimal Download Strategy enabled, you need to do it like this:

$(function () {
      ExecuteOrDelayUntilScriptLoaded(function () {
          if (typeof asyncDeltaManager != "undefined")
            asyncDeltaManager.add_endRequest(runYourCode);
          else runYourCode();
      }, "start.js");

  });

function runYourCode() {
    //your code
     return false;
}

Source

Jussi Palo
  • 8,196
  • 1
  • 20
  • 36
  • The code is broken now, when I add it like this. Do I need a file called start.js or what is start.js? And do I need the return false? Do I need to modify ScriptLinkattributes (LoadAfterUI etc.)? – Peter Feb 18 '16 at 10:02
  • start.js is related to MDS, so it should be there if you use MDS. – Jussi Palo Feb 18 '16 at 12:45
  • The code doesn't get executed now (even on first load). But it is loaded in correct order: start.js -> jquery.min.js -> newoverview.js. – Peter Feb 18 '16 at 13:34
0

I see newoverview.js is included with attribute LoadAfterUI = true.

However, runAfterEverythingElse() is a <body> 'onload' event (if I understand the point of '_spBodyOnLoadFunctionNames' correctly).

So is it possible that there is a conflict between the body 'onload's firing, and the time the UI is considered 'ready', and there's a timing issue somewhere?

To test the theory out, get rid of the _spBodyOnLoadFunctionNames altogether by replacing newoverview.js's contents with:

   jQuery(function() {
       /*  my code */ 
       html = 'test';
       jQuery('table[summary="test"]').html(html);
   });

and optionally change newoverview.js's LoadAfterUI to false - this attribute shouldn't make any difference

oflahero
  • 101
  • I tried your suggestion but still the same error occurs. I changed LoadAfterUI to false and changed the content of newoverview.js. Code works. But only on first load. – Peter Feb 18 '16 at 13:31
  • Sorry Peter. Frustrating. – oflahero Feb 18 '16 at 13:40
  • A page load is a page load - it's starting from scratch each time. Therefore there should be no difference between first and second, unless:
    1. a cookie has been set the first time that affects load 2, or;
    2. the link you speak of from the sharepoint URL isn't a straightforward link. Verify this link is a vanilla <a href="your-page-URL"...> rather than some sneaky JS doing something else.
    – oflahero Feb 18 '16 at 13:40
  • I can see that it is not a straightforward link. The page is not doing a complete reload, it is more like a partial-load. Menu on left side does not change, but the content-area is loaded again or changes. Try it yourself: open a sharepoint 2013 website and click documents or tasks on left menu. This is what I do. And most important: my javascript file is loaded (but not executed)! – Peter Feb 18 '16 at 13:46
  • Typical MS/Sharepoint: overcomplicating everything unnecessarily In that case, I would just use a script tag in your ASPX file itself, at the bottom:
       <script type="text/javascript>
       jQuery(function() {
           /*  my code */ 
           html = 'test';
           jQuery('table[summary="test"]').html(html);
       });
       </script>
    
    

    and remove the 'push' onto _spBodyOnLoadFunctionNames (and even the ScriptLink of newoverview.js)

    – oflahero Feb 18 '16 at 14:07
  • <script> tag is not allowed in aspx in sp2013. – Peter Feb 19 '16 at 06:53
0

Long answer

Finally this is my solution to make jQuery, DOM Manipulation and MDS work alltogether in Sharepoint 2013.

aspx file

<asp:content contentplaceholderid="PlaceHolderAdditionalPageHead" runat="server">
    <SharePoint:ScriptLink language="javascript" name="~site/SiteAssets/js/jquery.min.js" OnDemand="false" runat="server" Localizable="false"/>
    <SharePoint:ScriptLink language="javascript" name="~site/SiteAssets/js/mycustom.js" OnDemand="false" runat="server" Localizable="false"/>
    <SharePoint:CssRegistration ID="CssRegistration3" Name="<% $SPUrl:~site/SiteAssets/css/newoverview.css%>" runat="server"/>
    <SharePoint:RssLink runat="server"/>
</asp:content>

mycustom.js

function myCode(){
    // jQuery code goes here
    // you can use $ namespace
}

function preCode(){
    Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(myCode);
}

if(typeof _spPageContextInfo != "undefined" && _spPageContextInfo != null){
    var jsURL = _spPageContextInfo.siteServerRelativeUrl + "/sharepointsite/siteassets/js/mycustom.js";
    RegisterModuleInit(jsURL, myCode());
}else{
    ExecuteOrDelayUntilScriptLoaded(preCode, "sp.init.js"); 
}   

Explanation

Issue 1: Partial Load (MDS)
Sharepoint Minimal Download Strategy is a feature which does a lot of caching for you and - to achieve that - does partial site loads. Which has the sideeffect of unexecuted javascript. The mycustom.js file makes use of _spPageContextInfo which will be defined when the site got loaded using mds. When that happens we need to register our script to mds to make it load - RegisterModuleInit. The script will be loaded again and JS code will be executed then.

Issue 2: Multiple events after document.ready
We can not use document.ready for dom manipulation as sharepoint is doing a lot of things after document.ready. The last event triggered is Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded which is wrapped in a function because it can't be called directly as further Sharepoint JS files need to be loaded before.

a side-note
The aspx file is loading the JavaScript files by using SharePoint:ScriptLink. I removed LoadAfterUI="true" to avoid future side effects and to have unified settings for all javascript files. Thus it defaults to LoadAfterUI="false".

Credits

  1. ASPX: ScriptLink.LoadAfterUI
  2. JS: blogs.c5insight.com (Kyle Wright)
  3. JS: josharepoint.com (José Quinto)
Peter
  • 225
  • 1
  • 3
  • 10
0

Short answer

There is an alternate answer beside my long answer. But this answer is shorter, more reliable and faster. The only downside of this method: the code is executed very often.

function myCode() {
    // custom code (e.g. jQuery) goes here
} 
$("#DeltaPlaceHolderMain").bind("DOMSubtreeModified", function(event) {
      myCode();
});

Explanation

The bind method is listening for dom modification on the element with id #DeltaPlaceHolderMain. Partial reloads are doing dom mods and thus the code is being executed!

Peter
  • 225
  • 1
  • 3
  • 10