RSS
 

Utilizing the awesome power of jQuery to access properties of an element

28 Oct

I’m about 35% of the way through a 100gb backup at the moment, so I thought I’d throw in a quick blog post. I’m sure this has been brought up a million (or so) times but, in my opinion, you can’t reiterate this point enough. I occasionally get my hands dirty in the jQuery tags on Stack Overflow, and all too often I see code like this:

$(".myClass").click(function () {
    if ($(this).attr("id") == "myId") {
        runMyFunction($(this).val());
    }
    else if ($(this).attr("id") == "myOtherId")) {
        runMyOtherFunction($(this).val());
    }
});

The worst part is that this type of code is commonly given as an answer to a question. While I often feel tempted to vote down these solutions, they’re not incorrect per se, so I generally tend to post my own answer. “Why is this bad?”, you might be thinking. Well, lets simplify and break down the following code:

    if ($(this).attr("id") == "myId") {

We can separate $(this).attr("id") into 2 expressions; $(this) and attr. When you pass an argument to jQuery, it runs through the following process:

  1. Create a new instance of jQuery.fn.init(), passing this to the constructor which,
  2. initializes 4 variables,
  3. checks that the 1st argument passed isn’t a “falsy” value like null, undefined, “”, etc,
  4. looks for a nodeType property on the 1st argument passed, which is true in our case and,
  5. sets the resulting jQuery object’s context, 0 and length properties before returning the newly created instance.

Now .attr("id"):

  1. Pass the context (the collection of elements – only 1 in our case), our attribute name (“id”), value (undefined in this case), another argument named pass (true) and the jQuery.attr function to jQuery.access which,
  2. stores the length of the collection passed in as an optimisation,
  3. checks to see if the second argument is an object (a collection of property and value pairs to set on each element in the collection),
  4. checks to see if a property is being set, in our case it isn’t so it instead,
  5. checks the stored length of the collection isn’t 0 and calls the passed in jQuery.attr, passing the collection’s first element and the attribute name we tried to access. This function then,
  6. checks that the element argument passed in isn’t a “falsy” value, its nodeType property isn’t 3 or 8 (text and comment nodes respectively),
  7. checks that the pass argument was passed in (it wasn’t in this case),
  8. sets a variable that identifies whether the element passed in is an XML element (it’s not) and another that checks whether a value has been passed to assign to the property,
  9. tries to normalize attribute names into property names by looking it up in a map keeping the original name if it isn’t found,
  10. checks for a nodeType of 1 (element node), which is true in our case so then it,
  11. performs a regular expression test on the string to see if it matches /^(?:href|src|style)$/ and stores the result in a variable,
  12. checks to see if the string is “selected” to work around a bug in Safari. It isn’t, so it moves on and,
  13. checks to see if the string is the name of a property on the element, which is true in our case so it,
  14. checks to see if the set variable is “truthy”, it’s not so it skips that block and,
  15. checks to see if the element is a form element, it’s not so it,
  16. checks to see if the string is “tabIndex” to apply another workaround if true. It’s not so, finally, it feels justified to,
  17. return element[attribute].

Wow. Talk about taking the scenic route, we went through those 17 steps for jQuery to finally get to what we could have done in the first place: this.id. Of course, I’m not trying to put a downer on jQuery (it’s great and does all things!). jQuery *has* to do all this stuff for you so that you don’t run into the usual unexpected issues caused by certain implementations of getAttribute/setAttribute. Knowing which attributes are safe to access as property names means that you don’t need jQuery to do this leg work for you.

The real point of this post is that, when answering questions or helping people out with source code, try and set a good example. For the majority of attribute names you don’t need to use jQuery’s attr() to get the value of an attribute. If you’re unsure, look it up and find out if there are any specific quirks you need to know about. It might even be more efficient to work around those quirks without jQuery’s help.

I’ll leave you with an example that shows the difference over many iterations:

[iframe http://jsfiddle.net/AndyE/YPUTp/embedded/result,js height=100px width=500px]

 

Tags: , , , , ,

Leave a Reply

 

 
  1. Nathan L Smith

    October 28th, 2010 at 4:19 pm

    > Knowing which attributes are safe to access as property names means that you don’t need jQuery to do this leg work for you.

    So, which ones are safe in which situations and where’s the URL with an up to date list of all of them?

    One of the points of using a framework like jQuery is that it normalizes the differences between browses and makes it so you don’t need to have a list of these attributes taped to your monitor.

    I understand your point, but I’m know there are cases where direct property access fails, or where passing the element to $ gives you extra methods to work with.

    I think the bigger performance problem of your example is the fact that $(this).attr(“id”) is called twice instead of cached in a var.

     
    • Andy

      October 28th, 2010 at 4:36 pm

      Nathan,

      If you read through the steps I listed above, you’ll see the properties that jQuery tests for, src, href, selected, tabIndex, and the attribute names that are mapped can be checked by typing jQuery.props in your favourite console.

      It’s true that a not caching these things is also a problem, but does nobody care about efficiency anymore? If, ultimately, jQuery gets to the single line of code you could write, why not just write that line of code?

      I’ve seen cases where large blocks of code will repeatedly use attr() when unnecessary and I think setting an example by only using it where appropriate is a good habit to get into.

       
      • Blixt

        November 2nd, 2010 at 8:43 pm

        Andy,

        While I agree on this particular point (id is an attribute that has been there from the beginning of DHTML, and writing longer code to get it is very unnecessary), let’s not lose sight of what you’re choosing when choosing jQuery. It’s a balance between simplicity (to the coder) and efficiency. jQuery is often on the simplicity side of things, since you can always do whatever jQuery does more efficiently by writing the minimal code needed.

        So while I agree with your example, I’m writing this because your post seems to encourage micro-optimization (“doing this in jQuery uses X operations while solution B uses only Y operations”). “[D]oes nobody care about efficiency anymore”? I think people who go with jQuery chose the simple route and will have to accept a loss in performance some times. I personally avoid any kind of frameworks in tight loops because as you proved, their overhead can add up quite a lot.

         
        • Andy

          November 3rd, 2010 at 9:30 am

          Yeah, you’re right, of course. I guess it’s one of my pet peeves, gets me worked up when I see such inefficient code. Like Nathan said, the real problem is repeated use of $(this) and attr() in the same code blocks without assigning them to variables.

          I’m not normally one to advocate micro-optimizations, it’s just that this particular one irks me.

           
          • Stefan Andersson

            November 4th, 2010 at 4:28 pm

            Err, no. The post is 100% spot on. You use jQuery when it’s terser, more efficient or more effective – and not when it’s neither. This post is a beautiful reminder to us all. Thank you!

             
        • Tim Down

          November 11th, 2010 at 11:34 am

          This is not a micro-optimization. $(this).attr(“id”) is worse than this.id in every possible way: it’s more complicated, it’s less readable, it’s massively slower and it’s compatible with fewer browsers. Don’t dismiss the speed issue; it really is significant on mobile browsers.

           
          • Andy

            November 12th, 2010 at 10:56 am

            That’s a good point, even more so with the recent unveiling of jQuery mobile (which requires jQuery core). I agree that it is orders of magnitude slower, and I didn’t take into consideration mobile browsers in my reply comment to Blixt.

             
          • Blixt

            November 19th, 2010 at 2:25 pm

            I did say that I did not think that the example in the post was a micro-optimization, and that it was a good thing. It was mostly the whole post going into implementation details that was my peeve. I’ve re-read it a few times and I think it’s a good warning to developers using jQuery.

            I kind of got the impression that the message was that how a call to jQuery is executed should be in the mind of all developers, which is not something I believe you can expect from the average person using jQuery. On the other hand, I guess this post was mostly directed at more advanced developers.

            My comment was not meant to dismiss the contents of the post, it was meant to be constructive feedback on how the post was written, for future posts that I look forward to. :)

             
          • Andy

            November 19th, 2010 at 11:28 pm

            Thanks, Andreas :-)

             
  2. Tweets that mention Utilizing the awesome power of jQuery to access properties of an element « What the Head Said -- Topsy.com

    November 3rd, 2010 at 3:26 am

    [...] This post was mentioned on Twitter by Adam Fahy, Damian Galarza. Damian Galarza said: Excellent point re: jQuery and other JS libraries http://bit.ly/9Y9MuF (via @afahy) Great post on jQuery usage [...]

     
  3. Mathias Bynens

    November 3rd, 2010 at 7:46 am

    Here’s a jsPerf test case comparing $(this).attr('id') with this.id: http://jsperf.com/native-properties-vs-jquery-attr

     
  4. Tim Down

    November 18th, 2010 at 12:23 am

    Good post, by the way: a nice summary of this particular problem. As I’m sure you’re aware from Stack Overflow, I’m in 100% agreement, and would probably go further and recommend against anybody using attr() at all, unless they can explain to me exactly what it’s supposed to do.

     
    • Andy

      November 19th, 2010 at 11:27 pm

      Thanks Tim, yeah I had noticed you share the same views. I think that any developer that takes pride in their work should take a look at what jQuery is doing underneath, if only to improve their understanding of the DOM.

      That being said, sometimes we all take the easy route.

       
    • JB

      September 11th, 2012 at 7:23 pm

      What would you use for HTML5 custom attributes if you recommend against using attr()?

       
  5. $( this ).attr( … ) Is Often Unnecessary | Ian Dunn

    April 9th, 2012 at 10:32 pm

    [...] often much more efficient to access DOM properties directly, rather than using jQuery. The downside to that approach is that there are differences between the [...]