When I wrote “Extending jQueryβs selector capabilities” a few months ago I shared a custom selector that enabled you to select elements based on corresponding data held by jQuery. The syntax required was something like this:
$('body p > a:data(ABC=123)'); |
This all tied into jQuery’s native ‘data‘ method which enables you to add data to elements without creating expando properties.
So, what’s new?
I’ve been fiddling around with the innards of jQuery and Sizzle (jQuery’s default selector engine) and have something which I think is a tiny bit better, syntactically at least:
// Data is added to an element (nothing amazing here): $('a#someLink').data('ABC', '123'); // Element is selected by querying data as if it were an attribute!!! $('a[ABC=123]'); // Awesome! |
From the above example it should be obvious that the jQuery’fied data is now directly tied into Sizzle’s attribute selectors. My first thought was that conflicts may occur where a data key is provided that also exists as an actual DOM property (what the attribute selector is intended for). For example, the consequence of the following action had to be considered:
// <a href="http://google.com" id="someLink">Google</a> $('a#someLink').data('href', 'not the real href'); $('a[href=http://google.com]'); // This still has to work $('a[href="not the real href"]'); // This should fail |
When a DOM property exists the attribute selector will, as expected, return that DOM property (even if a matching data key exists). And when no DOM property of that name exists then the element’s data will be queried.
The magic:
Here’s the code that makes it possible:
(function($){ var _dataFn = $.fn.data; $.fn.data = function(key, val){ if (typeof val !== 'undefined'){ $.expr.attrHandle[key] = function(elem){ return $(elem).attr(key) || $(elem).data(key); }; } return _dataFn.apply(this, arguments); }; })(jQuery); |
This will work exactly as expected; all variations of the attribute selector seem to work successfully. You can read up on attribute selectors in jQuery here: http://docs.jquery.com/Selectors (scroll down to “attribute filters”).
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
Sorry for sounding stupid, but the code that you’ve posted, where would I put that to be able to use the new selectors in my javascript, just anywhere in bteween some script tags?
@Jack, Not a stupid question at all. Put it in a script tag below where jQuery is loaded. Alternatively, if you host your own copy of jQuery, paste it in at the bottom of the jQuery file itself.
@James: Have you considered submitting that as a patch so we can determine if it’s something to put into core?
@Rey, just added it as a “feature” ticket (#4590) – I’m not sure if it’s something you would want to add to the core but it’s definitely worth considering. π
I personally think that the “:data” selector is a nicer implementation. It makes it more self evident where the variable and value are coming from. The bracket selector “[]” is a standard for specifically selecting attributes, I don’t know if it’s good to be mixing something else with that.
If I had to jump into someone elses code and they were using this, I probably would go crazy trying to figure out how they are getting values from attributes that aren’t there. I’d probably have no clue that they have this extra little snippet at the bottom of the js file somewhere.
@James: Well just so you know, John Resig thought it was pretty neat when I showed it to him π
Thanks for answering my Question James. With some thought I think I agree with Kyle, when adding the “:data” bit it is much more clear what is going on. If this feature is to be included in jQuery (I’ll vote for it!) then I think that the “:data” version should be added.
Jack.
Just for the record, this has been already come up on the jquery-dev list a while ago:
http://groups.google.com/group/jquery-dev/browse_thread/thread/d74e5a88b9649d24
there’s already a ticket for it as well:
http://dev.jquery.com/ticket/3586
and I also made a plugin a few months ago that does this a bit differently:
http://plugins.jquery.com/project/EnhancedAttributeSelectors
Anyway, this implementation is very elegant, and doesn’t add as much overhead as mine at all. What I would only suggest is using a prefix so there’s no ambiguity if you’re dealing with an attribute or data:
then you can select by data like this:
$('a[:ABC=123]');
Oops, that doesn’t work unless you modify some regex too:
@Balazs, thanks for the links. I tried searching on Google for something similar before publishing this post but the search was fruitless. Nice plugin you’ve got there! I definitely like the idea of a prefix on the data selector, it would certainly eliminate the attribute problem. Thanks π
@Kyle, I agree; from a code maintainability point of view, the previous data selector is definitely superior. Maybe we could meet halfway with a ‘:’ or even a ‘data:’ prefix like Balazs suggested? That would definitely make it more explicit:
Plus, the new implementation ties in very nicely with Sizzle’s core and so is slightly faster than the older ‘:data()’ one. It’s really a matter of taste in the end… go with whatever seems more intuitive.
this is just awesome. exactly what i’ve been looking for ! Many thanks for this quick hack.
Has there been any more talk of this feature being added? I just found myself needing it today, and I’m having trouble adapting the initial code to work with the data: approach found in the comments.
Just a note. I’ve been testing this recently and it doesn’t appear to work well with recent releases of jQuery. I’m looking into it and will hopefully have an answer soon. For now, I suggest using the following
:data
solution:Its usage is as follows:
Thanks James, I’ll check it out.