jQuery: implementing custom selectors for jQuery Templates plugin

I’ve been working a lot over the last couple of months with jQuery and the Templates plugin written by Microsoft (wondering if that will ever leave the beta phase??).
What I’ve come to love about it is the flexibility and nice separation of concerns it introduces.
However, I still felt that it was a little bit “ugly” to write the actual code that connects a JSON result with a template rendering stage.
Luckily, jQuery has some great extension points that can make this easier and I found some great articles by James Podolsey and from  jQuery HowTo that really helped my write my own extensions. So here goes…

Custom Selector Templates

Before going into actually implementing the custom selectors we want for the jQuery Template Plugin we’ll first have a look at how a custom selector works by providing a little example.

Similar to the built-in custom selectors that extend jQuery’s CSS selectors like :contains and :even you can write your own custom selectors using a predefined template model that looks like this (below are two examples of implementing the exact same selector which is called :test):

image

Note: that a custom selector can take up to four arguments (current, index, metadata, and elements) which we will have a look at shortly.

The two approaches (marked with red and blue) for extending jQuery with custom selectors are equal though I prefer the second approach (which is also better if you want more that one selector in the same self calling function).

Example usages:

  • $(:test)
  • $(:test(‘one’))
  • $(p:test)
  • $(:test, div)

Now let’s test the selector by using $(p:test(‘one’)) and see what happens, remember that the :test selector will log its arguments to the console window for now so we can have a look at them.

This is the html that the selector will execute on (three p-tags):

image

So jQuery will actually first select all P-tags, using the element selector, and then execute the :test selector on the result so we would expect three calls to be seen in the console window.
As a side note it’s always good to limit the selector calls as much as possible or should I say narrow them, perhaps by using a context so the search doesn’t need to look at the entire DOM tree. We’ll come back to that later on.

The arguments

Remember that I said that the selector function can take up to four arguments (test: function (current, index, metadata, elements))

image

As you can see we get four arguments from the call $(p:test(‘one’)):

  1. the currentDOM element (the p-tag in the example)
  2. the indexof the current DOM element (within all the elements to execute the selector on)
  3. some metadataabout the selector (well look more into detail on this array in a bit)
  4. finally, an array containing all the elements to run the selector on (since we executed it on p-tags we’ll get all three p-tags)

The metadata argument

This is the interesting part. Remember the call, $(p:test(‘one’))

image

So looking at the metadata argument (which is an array) we can see that also contains four elements:

  1. the complete selector
  2. the name of the selector
  3. the quote symbol used around the arguments, if used
  4. the arguments used by the selector (in the example ‘one’)

Let’s just try to call the selector without any arguments just to see the difference:

image

image

You can see that the quote symbol and arguments are undefined.

Lastly, keep in mind that a selector should always return true or false depending on whether you want the current element to be included in the result. In this example $(p:test).size() will return 0 since we always return false from the selector, just saying.

Now I think we are ready to implement the custom selectors for the jQuery Templates Plugin.

jQuery Templates Plugin Selectors

Let’s first briefly see how we would use the template plugin OOB  (so to say) to see what improvement can be made.
Consider this template:

image

We have a DIV that works as the target for the rendered content that will be executed on the SCRIPT tag that has the template information. Let’s see how that is rendered now.

image

From a JSON result we’ll start by finding the container (or target) using the ID selector.
Then render the template (and here we can see an example of specifying the context, hence when looking for the SCRIPT tag we’ll limit the scope to search in by telling jQuery to look in our container since that was where we placed it).

Finally, append the html that was rendered to the container (DIV) to see the result on the screen.
Problems? No, it works.
Can we make it simpler and more readable? I hope so.

It would be great if we could find containers and templates be just saying something like this:

$(:container(books)) and $(:template(books))

Thus changing the javascript code to look like this instead:

image

To make that work we have a couple of choices. We can make an assumption that the ID tag of a container and/or template will always have an ID prefix of container-{ID} and/or template-{ID} just like we had in the example.
Another solution is to use the data attribute that jQuery is good at working with. I’ll use that method in my example.

Using the data attribute

By using the data- (dash) syntax to identify my containers and templates I now have altered the html to look like this:

image

…and now here is the code for the actual selectors:

(function ($, undefined) {

    if ($ === undefined) {
        throw "Dependency: JQuery is not defined. Please check javascript imports.";
    }

    $.extend($.expr[':'],
    {

        // :template(name)
        template: function (current, index, metadata, elements) {

            var arg = metadata[3],
                d = $(current).data("template-for");

            if (d === undefined) {
                return false;
            }

            return arg ? arg === d : true;

        },

        // :container(name)
        container: function (current, index, metadata, elements) {

            var arg = metadata[3],
                d = $(current).data("container-for");

            if (d === undefined) {
                return false;
            }

            return arg ? arg === d : true;

        }

    });

} (jQuery));

So the selector/s first check that we have found an element with the data-container-for and/or data-template-for attribute and then optionally compares that to the name of the container and/or template.

Summing up

So by including this javascript we can now find all “container” elements (yeah, I’ll call them that) and make some changes to them (like putting everything in bold text).

image

image

yeah…that’s pretty!!

More interesting though, is that the OnSuccess javascript method now looks like this instead:

image

Of course, you can optimize and change this a lot more but the basic idea was to show how easy it is to write these custom selectors.

About these ads

, ,

  1. #1 by Anatoly Mironov on January 8, 2012 - 20:16

    Great post! Now the jQuery tmpl can be simplified. I’ll go and try it immediately :)

  2. #2 by Leigh on May 23, 2013 - 18:34

    I know this is an old post, but I just wanted to say thanks for writing up such a detailed explanation of how of custom selectors work! It was very helpful.

  1. Simplifying jQuery tmpl interface « Sharepoint…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 313 other followers

%d bloggers like this: