Azure / Office 365 / SharePoint Development / Search

Correctly including scripts into your display templates

January 12, 2015

When creating display templates it is sometimes required to load additional script and/or CSS files. These files can easily be referenced in a display template with the following functions:

  • $includeScript
  • $includeLanguageScript
  • $includeCSS

Now there is a problem when using these $include functions. The problem is that the function loads your referenced files asynchronously. So that means that if you need to call a function in the referenced JavaScript file, this could result in an error that states that the function is not defined. As the $include functions are loading the files asynchronously, you cannot be sure that the function will be available when you need to execute your code.

What I recommend is to use the $include function only if you do not have a dependency to one of these files. CSS file references will not give you any problems because it does not matter for your code when they get loaded.

Now, what can you do if your code happens to have a dependency to one of the referenced files? If that is the case, you need to update your code and use the RegisterSod function instead of the $includeScript function.

Info: If you want to load a script dependency that is required in your item display template, you can also check out the following post: How to load scripts that are required for your item display template rendering.

Solution

As I already mentioned, the solution that I recommend is to switch to the SP.SOD.registerSod / RegisterSod function instead of using the $includeScript function. This function can be implemented in the script element, but the downside of it is that you cannot add the URL tokens to it. So it is best to only do it for external script references like for example jQuery:

If you need to reference files with a URL token (~sitecollection or ~site) like you can do in the $includeScript function, you could use a function that is called: Srch.U.replaceUrlTokens. It is easier to place this code where you can find the rest of the JavaScript, otherwise, you need to ensure that the search JavaScript files are loaded. As everything in the script block gets executed before the scripts are registered, so you need to add some additional code. That is why I place this code in the first DIV element of the HTML display template.

So in my first example, my files need to be loaded in the following order:

  • Script1.js
  • Script2.js
  • Script3.js

This is the code that needs to be added:

This will register the scripts, but they are not yet loaded if you go to your page. To load them you will need to make use of the SP.SOD.executeFunc / EnsureScriptFunc function. There are some dependencies here, so there are two ways to do it:

  1. Call the EnsureScriptFunc function three times for each file (script 1, script 2, script 3);
  2. Define the script dependencies with RegisterSodDep function. This is the cleanest way to the job done.

If you are going to use the ensure script function three times, it will look like this:

Note: another way could be to load these files is with LoadMultipleSods or SP.SOD.loadMultiple: LoadMultipleSods(['script1.js', 'script2.js', 'script3.js'], function() { // the code });. This will load the files asynchronously, so if your files are depended on each other, this approach cannot be used.

Now by defining the script dependencies, it looks like this:

When you add this to your display template and refresh the page, it returns the following log messages:

Scripts loaded in correct order

Scripts loaded in correct order

In this example I included three JavaScript files when you only need to add one reference like jQuery, for example, you do not have to add a dependency. You only need to register the reference to the jQuery script and the ensure script function.

Refreshing your results

When you are going to refresh your results (which can happen on a search query, refinement, sorting, ... actions), that will trigger the display templates to get loaded again and render the results on the page. When the code is going execute the EnsureScriptFunc with the script3.js reference you will notice that the Script3 function will not get executed. Why is this happening?

In the EnsureScriptFunc there are some checks in place to see if the script is loaded. The first time the function gets called, the script3.js reference is not yet loaded for the script on demand (SOD). You can check this in the SOD variable when the EnsureScriptFunc gets called:

Script missing state

Script missing state

In the screenshot above you can see that the script has a state equal to 1 which corresponds to a missing state:

When the script reference is missing, the code will retrieve this script and once it is loaded, execute your code.

Now when you are going to refine, sort, or do a new search query, your results will get refreshed and the templates will get loaded again. When the EnsureScriptFunc function gets called, the SOD variable will now have the following state:

Script loaded state

Script loaded state

As you can see the script reference is not missing anymore because it is already loaded. This is very normal because there was not a page refresh, so the script is still available from the first result rendering. That is also why the Script3 function will not get executed.

The solution to this is very easy. You have to specify the function name which you want to call as the second parameter in the EnsureScriptFunc function.

The EnsureScriptFunc function will check if the parameter is not undefined or null. If this is not the case, it knows that it can execute the code which is written inside the block.

Loading jQuery

For jQuery you will have something like this:

It could be that jQuery is already referenced in the master page or page layout. What you can do is checking if jQuery is loaded before registering your file:

Background information

Let me show you what happens if you are going to implement your script with the $includeScript function. As an example, I created a copy of the default control_list.html display template and I added three different script references in the script block:

For testing purposes, I added some demo functions in the script files to show log messages. Here is an example of the code from script1.js:

In script2.js I do a call to the Script1 function. In script3.js a call will be made to Script1 and Script2. I also added an AddPostRenderCallback function to the template which will call the Script3 function.

Now when I configure a Content Search Web Part in combination with this template, this is what the results are in my developer tools console:

Async script loading - incorrect

Async script loading - incorrect

The first time I load my page script2.js was loaded before script1.js. That is why I received the error that Script1 is not defined. If I refresh my page again this is what I get:

Async script loading - incorrect

Async script loading - incorrect

Now the third script is loaded first. So the $include functions do exactly what they are supposed to do, they load the scripts onto your page async.

The correct result should have been like this:

Async script loading - correct

Async script loading - correct

When using the $includeScript function the code gets executed from the moment the file is loaded. This is normal behaviour of course, but you have no control when it gets executed, or in which order it gets executed because it is asynchronous.

When you are working with some JavaScript frameworks and plugins, you need to be sure that the framework itself is loaded before the plugin scripts. For example jQuery needs to be loaded before jQueryUI. Also, you need to be sure that these files are loaded before you can execute your code. That is why you best use the RegisterSod and EnsureScriptFunc functions instead of the $includeScript function.

Download

Here you can find all the scripts that are created for this post: Blog GitHub Repository.

Changes

18/02/2015

Added a section to give you more information how to correctly include a function call when your results get refreshed (for example: by refining the results).

20/04/2016

Updated the article with a section allocated to jQuery loading.

Comments

  • Pingback: Office 365 Developer Podcast: Episode 029 on MSDN code samples with Andrew Byrne | POKORNY()

  • Pingback: Office 365 Developer Podcast: Episode 029 on MSDN code samples with Andrew Byrne | Office 365 Deployment Autoblog()

  • Pingback: Office 365 Developer Podcast: Episode 029 on MSDN code samples with Andrew Byrne » PC Portal of Wausau()

  • Meester Unnone

    You have to be very careful with the Srch.U.replaceUrlTokens. For instance, I found that if I had a Pages library and, inside the library I have a file called Main.aspx that has a Content Search web part, the call to replaceUrlTokens(“~sitecollection”) returns “/Pages/~sitecollection” which it totally wrong. No matter what I pass in I always get “/Pages…”. Pretty useless.

    • Meester Unnone,

      Haven’t experienced any trouble with it, but what I notice is that you do not call the function with valid parameters.

      In your example you do a Srch.U.replaceUrlTokens(“~sitecollection”) call. Which seems to be good, but the function is not intended to do the replacement like this. The function is created to replace a token from a URL string, like I showed in my example.

      Inside the function there will be a check in place to see if “~sitecollection/” exists in the provided URL. Notice the slash at the end of the token. So that means in your example, it will not find the URL token, so that is also the reason why you are retrieving incorrect URLs from the function call.

      If you would change the call to: Srch.U.replaceUrlTokens(“~sitecollection/”) you would get a correct URL.

      Now when you only want to use the function to get the site collection URL or site URL you can also use the following variables:

      ~site: _spPageContextInfo.webServerRelativeUrl
      ~sitecollection: _spPageContextInfo.siteServerRelativeUrl

      I hope this gives you some insights in how the function works.

      Regards,
      Elio

  • Shiv

    Thank you for this post. I have some issue with calling functions on pagination. I have used your Display template showing page numbers and used RegisterSod functions to call my custom js files.

    My jquery files are loaded and functions are executing on pageload. But when I click next button on pagination. my jquery functions are not executing on paging but I can see in developer tools files are loaded but functions are not calling.

    Thank you for your help.

    • Shiv,

      It is important that you register your jQuery code after your new set of results gets loaded. You can do this by adding the AddPostRenderCallback method to your code. That will ensure your code gets registered after all the results are loaded.

      Regards,
      Elio

      • Dev

        Hi Elio,

        I am also facing the same issue. I had registered my Jquery and jquery datatable using the same. and then EnsureScriptFunc inside AddPostRenderCallback.

        Works fine for the first page but not on the second. workaround for display template that I have to live with is putting in the page.

        I will appreciate if you could post an example (I know you have one download on Github) since there is not much explaination on Display templates about their rendering.

        I also want to know if there is a synchronous way of doing the same.

        • Hi Dev,

          You need to check to see if your jQuery files are already loaded.

          You will have to replace the datatable URL and datatable function in the typeof.

          Regards,
          Elio

        • Dev

          Thank you!!! I appreciate your help.

          Regards,
          Dev

  • Shiv

    Hello Elio,

    Thank you for the update. I have used your display template & js files (List_IncludeScripts.html & script1.js,script2.js,script3.js) for Content search webpart and and added refiner in that page.

    On page load I see the result as below :

    Script 1 loaded
    Script 2 loaded
    Script 1 function called:script2
    Script 3 loaded
    Script 1 function called:script3
    Script 2 function called:script3
    Script 3 function called:Template

    But when click on any refiner I can see the search results are updated but Functions are not executing in console. I thought at least “Script 3 function called:Template ” should be loaded.

    Any idea, how Can i call script 3 function?

    • Shiv,

      That is because the EnsureScriptFunc already registered the script and is ready to be used. So it is normal that you do not see it get logged again.

      What I meant with my previous comment is that you need to add a check to see if the jQuery file is already loaded. If it is not loaded, the jQuery variable will be undefined. If that is the case, you use the EnsureScriptFunc to get jQuery loaded on your page.

      When you are going to refine the results, SharePoint has already loaded the jQuery file (because there was no page refresh), so EnsureScriptFunc is not needed anymore and the jQuery variable will not be undefined.

      Here is some code you could use in your template:

      Change the logging with a function call to what you want to do, that way you do not need to duplicate code.

      Regards,
      Elio

  • Shiv

    Elio, you saved my time! that worked. Thanks for all your help!!! :)
    Sorry for not able to get your previous post..

  • Justas

    Awesome post. I had an exactly this kind of problem with jquery.js and jquery.datatables.js – your post helped me resolve this very quickly. Huge thanks!

    • Vikrant Raj Behal

      I’m trying to use jquery & jquery.datatables.js. Can you share code or guide me on usage? I don’t know how to call $(“#” + encodedId).dataTable function.

  • Kevin

    Hi Elio,

    Thanks for this very helpful post. However I have modified the item_commonHoverPanel_actions.html display template in order to add a custom link to the displayForm of an item in search. This works, but I want the information to show up in a popup, so in a custom script I have implemented the code to do this, but it does not work.

    function popup(){
    SP.UI.ModalDialog.showModalDialog({
    url: “myUrl”,
    title: “Test”,
    allowMaximize: true,
    showClose: true,
    width: 850,
    height: 600,
    dialogReturnValueCallback: newCallback
    });

    function newCallback(dialogResult, returnValue) {
    SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK);
    }
    }

    Can you point me into the right direction with this?

    Thnx very much.

    • Kevin,

      Check if the sp.ui.dialog.js file is loaded, otherwise you won’t be able to open the content in a new dialog window.

      If it is not loaded, you could also dynamically load it as follows:

      Regards,
      Elio

  • Basant Pandey

    Hi Elio,

    I have a requirement to capture the search button click, in this case when i put my code in jquery ready function, i will not get back the search button ID. but when i apply settimeout function having 5000 intervals, its work in my end. Let me know how can i fix this.

    how will i know that the search web part render in the page successfully? Then only i able to get the element.

    Thanks

    • Are you doing this in a search center? If so, you can create a searchbox display template and link your function to the click event.

      If you want to do it from another display template you need to be sure that all templates have been rendered on the page.

      • Basant Pandey

        Yes I am doing in search center. Is there any other way? I am currently doing in my custom master page. let me know is this a correct way?

        Thanks for your quick help.

        • Yes you could create a custom searchbox display template and configure it to be used on the searchbox web part. This will make the process simpler and you don’t need any event binding.

          The default search box display template can be found in the master page gallery: /_catalogs/masterpage/display templates/search/control_searchbox.html

        • Basant Pandey

          Thanks!!!

  • Driverdo19

    Hi Elio,

    We’re having problems with a few of our display templates when rendering results for the first time (when results are not cached). These display templates are having preview images included.
    The RegisterSOD & EnsureScriptFunc did not fix it, would you have any idea what can be the problem here? Thanks!!

    • Can you give me a bit more context of what is going wrong and what you want to achieve?

  • Jerry

    Hi Elio,

    Is there a way I can process the search results before displaying them? I have been looking for an OnPreRender or some function like that. I would assume I would do this in the control template. I want to capture the JSON, pass it to a JS function, do some processing, and then return it to be displayed.

    Thoughts?

    Thanks in advance…

    • You can do this by overriding the default SP JS function or you can do the processing in your display template and add the HTML to the page once you processed all the items. This is also an approach I use to group search results. I place them one by one in an array, and at the end I append these results from the array to the page. Check the following link for more info: https://github.com/SPCSR/DisplayTemplates/tree/master/Search%20Display%20Templates/Result%20Grouping%20Templates%20(CSWP)

      • Jerry

        Thank you for the quick reply. I am really interested in the first option, overriding the default SP JS Function. Unfortunately, I do not know what the default SP JS Function is. Would you happen to have an example of how to do this? Thank you.

        • Don’t know it out of my head. You will have to do some digging through the code. Start at the moment your control template is called and go down the stack.

  • Dmitrii

    That article save my day! I made a helper module to load multiple js/css and use it to develop pure html+js pages that are referenced as CEWP content.

  • Sudheer

    Hi Elio,

    I have a javascript file which contains a method that calls SharePoint web service. I need to call this file in search display template before the results are rendered, so that I can render the results based on the output from the javascript method.

    I have tried to include the javascript file using RegisterSod and EnsureScriptFunc in the control template to load and call the custom js file. But the item templates are executed first always before the custom js file has completed execution.

    Can you please suggest which approach should I follow in this case?

    • In that case it is a little bit harder, because you cannot prevent the results from rendering without breaking the display template. Probably the best solution is to temporarily store the item result output in a variable:

      After your script execution is done, you can then append the markup to the element again:

  • Nicolae Anghel

    Your code works great. It’s just that I managed to use this and it also works:

    document.write(“”);

    The scripts are loaded in order and before the code executes. I’ve only tested in IE.

    • If you are lucky enough, this will work. It is just the same as what $includeScript will do. So when you are going to add a couple of scripts that way, you are not in control of the order in which they are loaded. Which can cause issues when you have dependencies.

      With the EnsureScript approach, your scripts will get loaded first before the dependent code gets executed.

      • Nicolae Anghel

        That’s the thing, I was getting errors with $includeScript, but I’m not getting any errors with this approach. It can’t load scripts in a different order this way, my only concern was that the code in the display template would run before the scripts are loaded, but it’s not.

        • Which errors did you get?

        • Nicolae Anghel

          The ones you talked about in the article. I’m trying to use momentjs and momentjs timezones, but they were loading in random order. Now they’re loading correctly.

        • I’m not recommending $includeScript as this get loaded async which is OK when you do not have any dependency.

          In your case, for the timezone script, moment needs to get loaded first. Otherwise it will through an error that moment is not loaded yet. These things can happen of course. As you already loaded this on your machine a couple of times, the scripts could have already been cached, but this will not be the case for all the users.

          If you want to use moment to override the rendering of your field values, that is a bit harder. The reason is that the search wp does not wait for the scripts to get loaded. In that case you will have to update the DOM elements after everything is rendered, or make sure the scripts are loaded before the web part is being used (script wp), or creating a custom render function.

  • Vrushabh Shah

    Hi Elio – I am calling the AddPostRenderCallback method on my Control_SearchResults template to modify the DOM after the page is loaded. This works fine on page load but cannot call back the method again when I move to the next page on my search results.

    Any assistance should help.

    • What are you exactly doing in your AddPostRenderCallback function?

      • Vrushabh Shah

        https://uploads.disquscdn.com/images/62ecb77ccf0e204411b37c756c305fea505823e334d4280618c3c9668a69f42c.png
        I am trying to add a wrapper around my search results and provide a suitable header as shown in the attached image using jquery. I can’t seem to get this function to callback as soon as I navigate to second page of search results.

        • Have you written the AddPostRenderCallback function like this?

  • sandesh ranadive

    Hi Elio,
    Thank you for a great article. I am having a problem using the Moment.js files in display templates(Item and control templates). We are using a search webpart and getting calendar data. So in the item display template we want to offset the event dates using moment.js. I need to get the moment.js files loaded before the item template is into use, but that doesnt happen. I am using $IncludeScript, but seems like the js files all are loading after the item template. How to Make sure that the js files for moment.js run before the execution of item templates?

  • I’m afraid this is not possible. You will have to use your own method to add it to the page.

  • Pingback: How to load scripts that are required for your item display template rendering - @eliostruyf()

  • Marc

    Thank you for the information. It was a great help!

  • Pingback: SharePoint search - Display Templates - Keep it Simple()

  • You probably need to put your ensureScriptFunc code in an AddPostRenderCallback function, because right now your code will get executed once your web part gets loaded. When you put it in the AddPostRenderCallback function, it will execute the code after the templates are rendered.

  • Roman K

    I’m not sure whether I understood the task correctly, but if your final function needs all libraries, I would suggest registering dependencies on Modernizr to the final SOD registration, also it’s a good idea to register dependency on SP.JS if you use some SP code there. So the code would look like:

    //Load modernizr.cutom.js

    RegisterSod(‘modernizr.custom.js’, Srch.U.replaceUrlTokens(“~sitecollection/style library/Preview/js/modernizr.custom.js”));

    //Load jQuery and grid.js with dependencies

    RegisterSod(‘jquery-3.2.1.slim.min.js’, Srch.U.replaceUrlTokens(“~sitecollection/style library/Preview/js/jquery-3.2.1.slim.min.js”));

    RegisterSod(‘grid.js’, Srch.U.replaceUrlTokens(“~sitecollection/style library/Preview/js/grid.js”));

    RegisterSodDep(‘grid.js’, ‘jquery-3.2.1.slim.min.js’);

    RegisterSodDep(‘grid.js’, ‘modernizr.custom.js’);

    RegisterSodDep(‘grid.js’, ‘sp.js’);

    EnsureScriptFunc(“grid.js”, null, function () {

    $(function() {

    Grid.init();

    });

    });

    • If you do this, you will initialize the grid even before the results are rendered. That is why I mentioned to use the AddPostRenderCallback function. In your case it would look like this:

      • Roman K

        If the Grid library has to be loaded after the data is rendered than I agree.
        As I understand AddPostRenderCallback is similar to $(document).ready ?
        Elio, what is ctx here? How do you initialize it?

        • Not completely similar. The AddPostRenderCallback makes sure that all results are rendered, before executing the callback function. As the search web parts are async, it could be that they are still loading and rendering data, although jQuery might think it ready.

          So by using the AddPostRenderCallback method, you make sure that all is rendered before executing anything else.