4 tips for using jQuery with SharePoint Display Templates
Posted
Thursday, February 13, 2014 12:05 PM
by
CoreyRoth
In preparing for my SharePoint Display Templates talk (#SPC3000) at SPC14, you'll be seeing a lot of posts from me in the coming weeks about the components that make up my demos. Today, I wanted to talk briefly about using jQuery with display templates. Display templates are really nothing more than JavaScript so using jQuery seems natural. However, in my experience with them, there are a few things to be aware of.
1) Don't assume jQuery is loaded already
The SharePoint product team has managed to do some amazing client-side things without using a single line of jQuery. Out-of-the-box, you might find it gets loaded on occasion by certain web parts. For the most part though, you shouldn't have any expectation of it being there. When you start working with display templates, you might notice $ is defined, but it won't be what you think it is. If you aren't sure it's loaded, you can always check the developer toolbar on your page.
That means you need to load it yourself. For most of you, your first option is to include it in your master page. However, if you want to make a display template that doesn't have any external dependencies on the branding, you can opt to load the script yourself. Display templates have a method called $includeScript to help you load external scripts. The first parameter is always this.url. In this example, I uploaded my jQuery file into the same folder as the rest of my display templates.
$includeScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Content Web Parts/jQuery.2.0.min.js");
You can also use this with a CDN if you prefer.
$includeScript(this.url, "https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.0.min.js");
If you prefer, you can also register scripts with SP.SOD.
2) Know how to get element IDs
When learning about display templates, I find that one of the easiest to understand is Item_TwoLines.html. This particular template does a good job of showing how IDs are generated and rendered. Most templates follow this basic premise. First generate a unique id for the display template using ctx.ClientControl.get_nextUniqueId() and append a meaningful string to it. In this case, it generates an Id and appends _2Lines_.
var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_2lines_");
That gives us the base id in which all subsequent controls will use. For example, the encodedId might look something like.
ctl00_ctl53_g_4c262258_fbac_4368_83cf_626d56131ca1_csr1_2lines
To generate the element ID for the div containing everything in the display template, it appends to encodedId with another string.
var containerId = encodedId + "container";
This gives us an id for the div like the following.
ctl00_ctl53_g_4c262258_fbac_4368_83cf_626d56131ca1_csr1_2lines_container
The display template then uses that generated id when it is rendering.
<div class="cbs-Item" id="_#= containerId =#_" data-displaytemplate="Item2Lines">
Now, you might be thinking, ok great, I can work with that Id and do something. Maybe something like this even.
$('#' + containerId).append("<p>Really good search result!</p>");
You can do that but only at certain times. Read on...
3) Don't try to access the DOM in the middle of your display template
What do I mean by that? Let's look at the rest of Item_TwoLines.html.
<div class="cbs-Item" id="_#= containerId =#_" data-displaytemplate="Item2Lines">
<a class="cbs-ItemLink" title="_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_" id="_#= pictureLinkId =#_">
<img class="cbs-Thumbnail" src="_#= $urlHtmlEncode(iconURL) =#_" alt="_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_" id="_#= pictureId =#_" />
</a>
<div class="cbs-Detail" id="_#= dataContainerId =#_">
<a class="cbs-Line1Link ms-noWrap ms-displayBlock" href="_#= linkURL =#_" title="_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_" id="_#= line1LinkId =#_">_#= line1 =#_</a>
<!--#_
if(!line2.isEmpty)
{
// don't use jQuery here!
_#-->
<div class="cbs-Line2 ms-noWrap" title="_#= $htmlEncode(line2.defaultValueRenderer(line2)) =#_" id="_#= line2Id =#_">_#= line2 =#_</div>
<!--#_
}
_#-->
</div>
<!--#_
// don't use jQuery here either!
_#-->
</div>
Display Templates let you do some old school classic ASP style spaghetti code. So you might think what if I try to reference an element inside that if statement or maybe you just add a new script block past the end of the last div. You can try it and your script will execute, but your jQuery won't find anything when you try to reference an element by id. That's because it's not on the page yet. If you look at the script that is generated for the display template, you will see that all of your HTML is pushed onto the ms_outHtml object. This allows all of your display template HTML to be pushed onto the DOM at once in a more efficient manner. How do we work around that? Read on...
4) Use jQuery inside of OnPostRender
Any jQuery manipulation you want to do really needs to occur in a post render event using AddPostRenderCallback. You just register a function and this will get executed when all of the HTML for the display template has hit the page. Think of this is as your $(document).ready() of display templates. Take a look at the code snippet below to see how we would have executed our previous example.
AddPostRenderCallback(ctx, function () {
$('#' + containerId).append("<p>Something</p>");
});
This will execute the line of code inside the function once the display template has rendered and then you will get the expected results.
If you are just starting with display templates, hopefully you have found these quick tips useful. We'll be covering this in more detail at my session at SPC14. I hope to see you there.
Follow me on twitter: @coreyroth.