5
\$\begingroup\$

I have a selector that pulls in all anchor tags that start with the # symbol. I am trying to add a :not selector of some form for elements that have a data attribute. I know I could do the following...

$('a[href^="#"]:not([data-known])')

But the problem is that I don't know what the data attribute will be called. I have resorted to doing the following.

$('a[href^="#"]').click(function(event){
    if($.isEmptyObject($(this).data())){
        //custom code here
    }
});  

Is there a way to avoid the if statement and add the :not to the original selector? Is there a better way of doing this period?

Here is a FIDDLE I have been playing in.

\$\endgroup\$
6
  • \$\begingroup\$ I think your best approach would be using jQuery.hasData() (even though you will still use an if statement). \$\endgroup\$
    – Jozeidon
    Commented Apr 18, 2013 at 22:46
  • \$\begingroup\$ Why is that the best approach? I'd argue against it, since it anyhow calls isEmptyObject under the hood, but with a lot of extra code that's not needed in this case. \$\endgroup\$ Commented Apr 18, 2013 at 22:49
  • \$\begingroup\$ Because of the ease of usefulness; even though you make a good point. \$\endgroup\$
    – Jozeidon
    Commented Apr 18, 2013 at 22:52
  • \$\begingroup\$ Actually, hasData simply won't work at all. hasData only checks for data that has been set via .data(). It doesn't automatically pull in the HTML5 data-* attributes. \$\endgroup\$ Commented Apr 18, 2013 at 22:52
  • \$\begingroup\$ Here's a fiddle demonstrating my point above. \$\endgroup\$ Commented Apr 18, 2013 at 22:58

1 Answer 1

4
\$\begingroup\$

Very good question. I'm pretty sure this can't be done within the selector.

However, instead of checking the data every time it's clicked, filter the collection before applying the event listener:

$('a[href^="#"]').filter(function () {
    return $.isEmptyObject( $(this).data() );
}).click(function (event) {
    // Your code here...
});

If you don't care about IE, you can check the dataset property directly, which is much faster and much more reliable, since $(this).data() also contains any arbitrary data that might have been set (possibly by a plugin):

$('a[href^="#"]').filter(function () {
    return $.isEmptyObject( this.dataset );
})

If you find that you have to do this a lot, you can abstract it into a custom filter, which you can then use in your selectors:

jQuery.expr[':']['has-data'] = function (el) {
    return ! $.isEmptyObject( $(el).data() );
    // If you don't care about IE:
    // return ! $.isEmptyObject( el.dataset );
};

Then just use it throughout your code:

$('a[href^="#"]:not(:has-data)').click(function (event) {
    // Your code here...
});
\$\endgroup\$
1
  • \$\begingroup\$ This is great! This solves the main issue I was facing with it having to call $.isEmptyObject() on every click. \$\endgroup\$ Commented Apr 18, 2013 at 23:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.