Geeks With Blogs
Thorvald Bøe
SharePoint is great. jQuery is great. Together, they are brilliant! At least that is how it is supposed to be, but unfortunately there are a few caveats here that can lead to some nightmareish random async error situations.

I spent quite a few hours debugging exactly such a situation in a recent project. Maybe these experiences will help someone in a similar situation.

The task:
Create two web parts, let's call them the project web part and the quote web part. They will be built using jQuery and display various err.. project and quote information. Both web parts are deployed on the same page on a SharePoint 2013 installation. Simple enough!

The implementation:
For each web part, I created two files using SharePoint designer, that I put in a document library - one html file and one js file. E.g. 'project.html' and 'project.js' for the project web part. On the actual page, I put a content editor web part (CEWP) that referenced the html file, which again referenced the js file. The js file contained all the display logic. The html file contained the markup in addition to referencing jQuery and other libraries. The quote web part used Datatables.net to display tabular data.

The problem:
At first, it seemed everything was working fine. And why shouldn't it, after all I had done a nice job separating all the logic and their dependencies. Or so I thought. But after some testing, I noticed strange, seemingly random errors occuring. One of the error messages said: "Object doesn't support property or method 'dataTable'"

The cause:
The simplified cause turned out to be that both web parts were loading their own instance of jquery. Since they were both on the same page, there is no guaranteed sequence of which library is loaded first. Datatables hooks up with the current jquery instance, and some times that instance would be overwritten by the other web part's instance. For example:

Quote web part loads jQuery
Quote web part loads datatables - datatables extends jQuery with dataTable method
Project web part loads jQuery - jQuery variable now points to an instance without dataTable extension
Quote web part tries to display table data by calling dataTable method - boom!

To further complicate the matter, there is a third jQuery instance loaded by an external component defined in a site template outside my control. This is an old (1.5) version that does not support the latest dataTable.

The solution:
In order to resolve the situation, I needed two things:
-Full control over the library loading sequence so that dataTables would extend the "right" jQuery instance, not just the one that happened to be 'in the lead' at the moment
-The two web parts needs to use the same jQuery instance

To achieve this, I created a new js file 'IncludeScripts.js' which is an attempt for a singleton construct for loading jQuery. I included IncludeScripts in the html page of both web parts, and the intention is that no matter how many web parts there is on a single page, only one jQuery instance is loaded. Before you say "why not just create a single web part that will load jQuery and the others will use the same instance?" - remember that such a web part would have to provide a guarantee that it would load before the others. Maybe you could achieve this by putting it on the top of the page? I don't know, and I wouldnt risk it. This way, the first web part to request jQuery loads an instance, stores it in a variable and the next just gets the same instance.

The implementation is quite simple. At the top i declare some variables:
var IncludeScriptsLoaded;
var jQuery_1_11;
var doneCallBackArray;
var failCallBackArray;

The main function:
function IncludeScripts(doneCallBack, failCallBack) {....

Then, after some initialization, I start loading the scripts:

if (IncludeScriptsLoaded == null || IncludeScriptsLoaded == false) { //load only once
  IncludeScriptsLoaded = true;
  $.getScript("/_layouts/15/SP.RequestExecutor.js").done(function(script, textStatus){
  //make sure jquery is loaded before ui/datatables
  $.getScript("/Libraries/jquery-1.11.0.min.js").done(function(script, textStatus){
    jQuery_1_11 = $; 
    $.getScript("/Libraries/jquery-ui-1.10.4.custom.min.js").done(function(script, textStatus){
      $.getScript("/Libraries/jquery.dataTables.min.js").done(function(script, textStatus){....

and so on and so on...

All scripts are loaded in sequence, and finally the doneCallBack is executed. The 'jQuery_1_11' variable exists as a global reference to the shared jQuery instance. To use it, just load the script like this:

$.getScript("/Libraries/IncludeScripts.js").done(function(script, textStatus){ //load the script
IncludeScripts(LoadPageData_QOI, function(error) { //when loaded, get the jQuery instance
jQuery_1_11.Something... //use the shared jQuery instance

The implementation is far from perfect, the main issue here is that I actually use $.getScript to load jQuery, in other words I use jQuery to load jQuery. This is only possible because of the third jQuery instance I mentioned that is already present. If not for that, I would probably have to implement a third container CEWP just for loading jQuery and force the other web parts to wait until that was loaded. Anyway, those are just details, I simply chose the most convenient solution that would work based on my premises - the main principle here is still valid: Use the same jQuery instance in all your web parts!

There are of course other ways to solve this problem. You could include jQuery in the master page. Or you could modify the aspx page to load a single jQuery instance. And probably others. And for every method there are drawbacks and advantages. And for every unique situation, a unique solution is required. I believe that for my particular situation I chose the most convenient solution that also worked.

I might be wrong, but it still works, so I don't really care :)


Posted on Tuesday, May 13, 2014 2:36 PM jquery , sharepoint | Back to top


Comments on this post: SharePoint and jQuery loading nightmares

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Thorvald Bøe | Powered by: GeeksWithBlogs.net