Take the 2-minute tour ×
SharePoint Stack Exchange is a question and answer site for SharePoint enthusiasts. It's 100% free, no registration required.

I am mid-way through development of a corporate intranet that will be hosted in SharePoint Online. For this reason, we are restricted to using sandbox solutions. One of the last "gotchas" that I need to resolve however, is how to dynamically register JavaScript files from within my web parts.

After a lot of research, there seems to be three decent ways of achieving this which I've outlined below, each with their own list of pro's and con's. Can anyone assist in guiding me down the best path here? This approach will be used across a dozens of web parts within our system, so it's important we get this stuff right first time.

Note: There are certainly more ways to dynamically inject files into the page from within a sandboxed web part, but the methods below are the only ones that I'm aware of that reslove the issue whereby if there are multiple instances of the same web part on the page, then the JavaScipt or CSS depenedies will still only be loaded once.

  1. CustomAction's ScriptLocation property

    Info: http://blog.voyta.net/2010/09/12/referencing-javascript-files-using-customaction-in-sharepoint-2010-sandboxed-solutions/

    Pro's:

    • Uses pure OOTB functionality :)
    • Approach can be used for both CSS and JavaScript files

    Con's:

    • UPDATE 5-Nov: Registers the script on every page in the site (not just the page where the web part has been added)
  2. ECMA Script

    Info: http://blog.mastykarz.nl/dynamically-loading-javascript-sandbox/

    Pro's:

    • Uses pure OOTB functionality :)
    • Waldek (the author) is a noted "big player" on the SahrePoint scene -- I'd basically trust the guy no matter what he told me to do!

    Con's:

    • Can't be used for registering CSS (ideally, I'd like to use the same approach for all my dependencies -- regardless of their file type)
    • It seems the most difficult and complex of the three solutions to implement (this could give a lot of room for error when there are lots of developers working on a project)
  3. AJAX Script Loader

    Info: http://lightningtools.com/sharepoint_2010/add-javascript-files-once-to-a-page-sharepoint-sandbox-solutions/

    Pro's:

    • Uses code written by Microsoft (therefore it can be treated as "tested and trusted")

    Con's:

    • Can't be used for CSS
    • It seems a bit leftfield, i.e. it is a single piece of an AJAX library in beta that actually targets .NET 4.0 and doesn't come packaged OOTB with SharePoint :(
  4. UPDATE 5-Nov: Custom JavaScript Function

    Info: http://github.com/eirikb/sppreload -- kudos to @eirikb for adding this suggestion. It uses the CustomAction's ScriptLocation property to call a custom JavaScript function (passing in the URL of the .js or .css file you want to load). The function determines if the file has already been loaded, and only if not, then it dynamically creates the node in the page head element.

    Pro's:

    • Can be used for CSS and JavaScript

    Con's:

    • Doesn't leverage any OOTB functionality, so there's no assurance against bugs etc.

And to pre-empt the flood of answers that won't take into consideration the limitations of the sandbox, please be aware that the use of delegate controls is forbidden, as are the ScriptLink and CssRegistration controls and the Page.RegisterClientScriptBlock method. Also, the CustomAction.ScriptSrc property is hard-wired to retrieve scripts from the _layouts directory which we unfortunately don't have have access to.

Thanks in advance for your help!

share|improve this question
    
Self-promoting is bad, but I guess I can leave this as a comment at least; have you seen github.com/eirikb/sppreload ? It uses the first point you made, but it does it a bit more neatly, like taking care of dependencies, never re-re-reloading same scripts (based on names) and it supports CSS through injection –  eirikb Nov 2 '12 at 6:09
    
That's very interesting indeed! I haven't downloaded your source yet, but it looks at first glance like you're using the CustomAction's ScriptLink attribute to call a "middle-man" function that you've written to do all the necassary checks (i.e. determine if the file is already loaded and that all its dependencies have loaded etc.). Is this about right? Some quick follow up questions... Are you able to control where / when in the DOM the scripts gets registered? For example, what if I want to ensure that my custom CSS is loaded either before or after the OOTB SharePoint stylesheets? –  Nick Larter Nov 2 '12 at 9:00
    
Yes, that is basically how it works. I use it for many internal projects at work. Since you show interested I decided to upload the WSP to gh-pages on github if you like to test it out, you can find it at github.com/eirikb/sppreload/blob/gh-pages/… –  eirikb Nov 2 '12 at 9:09
add comment

3 Answers 3

CustomAction-ScriptSrc is the way to go, and it does not limit scripts to layouts - there are delimiters for site collection- and site-referenced files. I've used this lots of times in sandbox scenarios.

<CustomAction 
    ScriptSrc="~SiteCollection/Style Library/js/jquery-1.4.3.js"
    Location="ScriptLink"
    Sequence="10">

Note that this will load the script throughout the site whether the page has your web parts or not. This will be handy if you have multiple web parts on the page and want to avoid double-loading your files. The Sequence attribute allows you to specify an order - a bigger numbers means it will load later.

share|improve this answer
    
I wasn't aware you could use tokens inside the ScriptSrc value -- that makes it an attractive option. Unfortunately though, in my case, the JavaScript files registered by the web parts really shouldn't be loaded onto every page in the site, just the home page. So that's a real deal-breaker for me! My common scripts are already referenced directly in my master page. –  Nick Larter Nov 5 '12 at 6:13
    
I just updated the original question with a fourth option (which was posted in a comment by @eirikb). It uses the custom action method too, but he instead points it to a JavaScript function which then works out whether the script file should be loaded on the current page (as apposed to getting the custom action to blindly write the <script> tag). It's the most risky solution as I have no idea how much testing it's had (if any), but it's probably the cleanest as it can be used for CSS files too. Care to weigh in with an opnion? –  Nick Larter Nov 5 '12 at 6:43
add comment
up vote 0 down vote accepted

For those playing at home, since I originally posted this question I have implemented and evaluated all four possible solutions. Ultimately, I eneded up using option #2 (which uses the built in ECMA script model to dynamically load JavaScript files; more details are available at http://blog.mastykarz.nl/dynamically-loading-javascript-sandbox/)

The main deal-breaker for me with the other methods was the fact they they used the custom action's ScriptLink property to load JavaScript files. What I didn't realise originally, was that this mechanism will in fact register your web parts' scripts on every page in your site -- even those where the web part has not been added. In my case (where the SharePoint application in question is a corporate intranet with a huge number of web parts deployed to it and where the entire intranet comprises a single site collection) the page size of evry page in the site almost instantly bloated out of control.

As for using the built-in ECMA scripts to dynamically load your web parts' scripts, the only real drawback here is that there's a bit of development overhead involved in converting all your existing scripts to "on demand" scripts. All the details are provided in the link above, but in short, you need to add an "okay, I've finished loading" command to the bottom of each of your scripts, plus also register any inter-script dependencies that you may have. Doing so means that if a particular function is called, then the ECMA scripts methods will not only go off and load the necessary script (if it hasn;t been loaded already of course!), but it will also load the jQuery library and any other libraries that it might need or depend on. As you can see, once this has all been set up, it's a very neat and reusable pattern :)

And as previously stated in the original question, this method also can't be used for CSS files. This means that if your page contains multiple copies of a custom web part, then you'll still have multiple copies of the web part's CSS files being loaded by the browser. This however, is less of an issue than it sounds... Luckily, CSS is not only fast and lightweight, but it's built on the concept of inheritance, and so regardless of how many copies of the stylesheet have been loaded, it won't affect the appearance of the web part in any way (as the stylesheet rules will simply all override one another). So apart from the slightly increased page bloat, there really is no functional risk to this situation. JavaScript on the other hand is very different -- having multiple copies of the same script on the same page will cause the browser to wet its pants as soon as the first function is called.

I hope this helps someone out there...

share|improve this answer
add comment

Or, you could always put your <script> tags directly into the master page, along with your css links.

share|improve this answer
    
The question clearly states that JavaScript and CSS files need to registered DYNAMICALLY from within a web part (for the purposes of only loading them as part of the page that they're required on). The "worst case" scenario I described is having the files for EVERY web part being registered on the home page. Adding the JS and CS registrations to the master page would ensure this. –  Nick Larter May 15 '13 at 3:36
    
sorry to have wasted your time. I'm glad you found a solution. –  Derek Gusoff May 15 '13 at 18:00
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.