Link to JavaScript Files in a SharePoint 2010 Sandbox Solution

This post is one post in a series of posts I penned in hopes it would help those looking to move a SharePoint 2010 Visual Studio 2010 farm based solution to a sandbox. There are many gotcha’s and pitfalls that you may run into or you may want to be aware of. You can learn more about this series here as well as download the CodePlex Project, From the Farm to the Sandbox, which includes the sample farm and sandbox solutions references through this series.
The related posts include:
Automatically Check In Files in a SharePoint 2010 Sandbox Feature Event Receiver
SharePoint 2010 Sandbox Feature Deactivation – Removing Module Assets
The Proper Web Part Control Class for a SharePoint 2010 Sandbox Solution
Why CSSRegistration Will Not Work in a SharePoint 2010 Sandbox Solution
Custom Properties in a SharePoint 2010 Sandbox Web Part

RegisterClientScriptInclude – Page.ClientScript.RegisterClientScriptInclude

The standard way I link to a JavaScript file in a web part included in a Farm based solution would be to use the Page.ClientScript.RegisterClientScriptInclude method in the web part’s OnInit function. A second valid method you may also try is using the Microsoft.SharePoint.WebControls.ScriptLink class in your farm solution. But with either of these two methods work in sandboxed solution? Nope.

Review the Page.ClientScript.RegisterClientScriptInclude Method

The following code is from the OnInit function of a normal web part.

1
2
3
4
5
6
7
8
9
10
protected override void OnInit(EventArgs e)
{
     base.OnInit(e);
     web = SPContext.Current.Site.RootWeb;
     SPWeb topLevelSite = SPContext.Current.Site.RootWeb;
     string sURL = topLevelSite.ServerRelativeUrl;
     if (!sURL.EndsWith("/"))
          sURL += "/";
     this.Page.ClientScript.RegisterClientScriptInclude(this.GetType(), "jquery.1.7.1.min.js", sURL + "/Style Library/Scripts/jquery.1.7.1.min.js ");
}

If you try this in a sandbox solution it actually works, or rather it does not throw any errors. But the .js file does not actually get linked to, nor does it get loaded in the client browser. Why? Well if you check out the documentation you will find that there is nothing inherently wrong with this function and I haven’t found much documentation on this myself (if you find any great resources, please tell me), but what I have gathered is that the Page object the code references is not the Real page object, but rather a shell version. This means that when we attempt to register our script to the this.Page object, it is being registered with a temporary page object made available only to our sandbox web part. Changes to this page object do not propagate to the actual Page object.
There are different ways to create a <script type=”text/javascript”> tag directly in the returned html, but I prefer the following code. This is from the Render function of a simple web part in a sandbox solution.

1
2
3
4
5
6
7
8
9
10
protected override void Render(HtmlTextWriter writer)
{
     base.Render(writer);
     web = SPContext.Current.Site.RootWeb;
     SPWeb topLevelSite = SPContext.Current.Site.RootWeb;
     string sURL = topLevelSite.ServerRelativeUrl;
     if (!sURL.EndsWith("/"))
          sURL += "/";
     writer.WriteLine("<script type="text/javascript" src="" + sURL + "/Style%20Library/Scripts/jquery.1.7.1.min.js"></script>");
}

Now there is a major and a minor issue with this method. The minor issue is that it would be nice if we were registering this script in the body of the web part, not necessarily the head section of the returned html. Not ideal, but doable. The major issue though? Well, if your page already loads this js file elsewhere, or possibly you include this web part into two places on a given page then the script will be loaded twice. Not exactly good.
If you read my post of replacing the CSSRegistration() tag you may be thinking about our solution there, which was to use a CustomAction. Well, you can add a CustomAction to your webpart’s Elements.xml file for loading js files as well.

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<elements xmlns="http://schemas.microsoft.com/sharepoint/" >
  <module Name=”CusomWebPart" List="113" Url="_catalogs/wp">
    <file Path=" CusomWebPartCusomWebPart.webpart" Url="CusomWebPart.webpart" Type="GhostableInLibrary">
      <property Name="Group" Value="Custom" />
    </file>
  </module>
  <customAction
   Location="ScriptLink"
   ScriptBlock="document.write('<script type="text/javascript" src="~sitecollection/Style%20Library/Scripts/jquery.1.7.1.js"></' + 'script>');"
    Sequence="100" />
</elements>

What this does is first add your CustomWebPart to the web part library, but second, it adds a CustomAction, with a location of ScriptLink. The actual location of ScriptLink is not in the head section, rather at the top of your body. This will make sure that your javascript file is only included once no matter how many times your custom web part is added to a page. You will want to be aware that Custom Action ScriptLinks are added to every page, meaning that your js file will be loaded on every page in this site collection. Not ideal for performance, but I find it is better than adding the js directly in the code. You could also add a particular class or block of code to your web part that could use javascript within the ScriptBlock to look a particular flag. If that block of code is found at least once, i.e. your web part has been added to a given page, then fire the code to load the additional js file.
For a complete review of the Farm and Sandbox solutions’ Feature Event Receiver, please reference From the Farm to the Sandbox for the full code. Also, please do not forget to check out the other posts in this series, all related to converting a farm solution to a sandbox solution.

2 thoughts on “Link to JavaScript Files in a SharePoint 2010 Sandbox Solution

  1. Nick Larter October 19, 2012

    I’ve used a custom action’s

    1
    ScriptBlock

    property to register the jquery-1.7.2.js script to my page in a sandbox web part and it’s working great.
    The only problem I’ve found so far though, is that if you have more than web part registering the same file, then multiple copies will get sent to the browser. For example, if I add a different web part to my page (which also requires jQuery and also has the same

    1
    ScriptBlock

    registration), then I end up with 2x copies of the jquery-1.7.2.js file being loaded.
    Do you know of any way to prevent this?
    I tried prefixing

    1
    document.write(...)

    with a

    1
    if(!jQuery)

    clause in each of my web parts but the custom action appears to get processed BEFORE the scripts have been loaded, so I still get two copies. Please help!

    1. Eric Overfield October 21, 2012

      Hi Nick,
      Yup, it sure would be nice if Sandboxed solutions had access to the actual Page object, although it makes sense why it does not. Anyhow, more to your problem, I suggest the following based on my recommendation of using a custom action in your elements.xml file.

      1
      ScriptBlock="if (!jQuery) {document.write('<script type="text/javascript" src="~sitecollection/Style%20Library/Scripts/jquery.1.7.1.js"></' + 'script>');"}

      Now it sounds as though you already did this. If you have two of the same web parts on the page, or two web parts with the same script code, then you should see two if statements in your html source, in the head section. Ideally only one should be loaded and the second would not. The download request of the js file is handled at the client side, so your server is sending the html and the end client retrieves this. What I could see happen is that when the first “if (!jQuery)” conditional is triggeredd, the document.write block caused the client to request the jQuery Library. The client moves on to the second “if (!jQuery)” conditional statement before the jQuery library has finished downloading and loading. This means that the second conditioanl also succeeds and the jQuery library is loaded again.
      You have me stuck on this one, I will see what else I can find. Sandboxed solutions are frustrating sometimes, I look forward to SharePoint 2013. I assume it is out of the question to include the jQuery Library in your Master Page? There is also another hack. You could set a trigger variable, that if set means you will assume the jQuery library is being loaded.

      1
      ScriptBlock="if (typeof jQueryTriggered === 'undefined') jQueryTriggered = false; if (!jQuery && !jQueryTriggered) {document.write('<script type="text/javascript" src="~sitecollection/Style%20Library/Scripts/jquery.1.7.1.js"></' + 'script>'); jQueryTriggered=true;}"

      What this is doing is that when the jQuery Library is loaded the first time it sets another variable to tell our scripting to trust that another library load has already occured. Yes, I am aware that this is a hack but it should work.
      I hope this helps. If I have another idea I will drop another comment.

Leave a Comment

Your email address will not be published. Required fields are marked *

Enter Code *

Filed Under

Related

One of the first forks in the road when planning a #SharePoint intranet project is deciding whether to go with modern or classic experiences. If you’re looking to take advantage of the latest and greatest, we've got your answer. http://ow.ly/4BrA50z2HQs

It’s official, another 30 days of practicing #SocialDistancing are our future. Another 30 days of #workingfromhome? Have no fear! I have some tips to help you remain close while keeping your distance. http://ow.ly/ezV950z1CRx

A ginormous and heartfelt THANK YOU to everyone who joined us yesterday for "#MicrosoftTeams & #PowerApps - Better Together". We'd like to especially say THANK YOU to Power Platform superstar @aprildunnam for guiding us through this amazing session. Stay tuned for the recap!

Working from home is a wonderful privilege, but we’d be lying to you if we said it came without challenges. Here are a few ‘tips and tricks’ for not just staying sane but excelling in your new environment and finding that ever-elusive work/life balance. http://ow.ly/vrah50yXK5g

Learning about @Microsoft #PowerApps and ⁦@MicrosoftTeams⁩ from ⁦@aprildunnam⁩ during ⁦@pixelmillteam⁩ webinar in the #mancave today. #WFH

Subscribe to PixelMill's
E-news

* indicates required

Let's Talk Digital
Workspaces Today

Get In Touch