2

I have the following jQuery code:

$("body.page-calendar-practices-2012-05 #content-header h1#title")

which works fine. I can use it to alter the html in my page. But I really want to select pages which have a class in the body STARTING WITH 'page-calendar-practices' ie: it will include other pages with different dates. I tried the following:

$("body[class^=page-calendar-practices] #content-header h1#title")

and it didn't work. What is wrong?

EDIT - here's the HTML:

<body class="not-front logged-in page-calendar two-sidebars page-calendar-practices-2012-05 section-calendar" >
Joseph Silber
  • 214,931
  • 59
  • 362
  • 292
user1015214
  • 2,733
  • 10
  • 36
  • 66

3 Answers3

5

[class^=page-calendar-practices] will only select an element who's class attribute starts with "page-calendar-practices"; not an element of which one of its classes starts with "page-calendar-practices".

So it would select an element such as this:

<body class="page-calendar-practices-2012-05">

You can use the * selector:

$('body[class*=page-calendar-practices]')

but that will also select the following:

<body class="page some-page-calendar-practices">

Alternatively, you can filter with this simple check:

$('body').filter(function() {
    return ~(' ' + this.className).indexOf(' page-calendar-practices');
}).find('#content-header h1#title');

which is a little more verbose than the simple "contains" selector, but is more accurate.

Here's the fiddle: http://jsfiddle.net/DR9K4/


To make things easier, you can add your own filter to jQuery, as follows:

jQuery.expr[':']['class-starts']​ = function(el, i, m) {
    return ~(' ' + el.className).indexOf(' ' + m[3]);
};

and then simply use it wherever you want:

$('body:class-starts(page-calendar-practices)');

See it here in action: http://jsfiddle.net/gEJeW/


Update: In jQuery 1.8, the Sizzle engine was completely rewritten. As part of the update, the expression extension architecture has also been reworked, so the above function won't work anymore.

If you're using jQuery 1.8+, use this:

$.expr[':']['class-starts'] = $.expr.createPseudo(function (arg) {
    return function (el) {
        return ~(' ' + el.className).indexOf(' ' + arg);
    };
});

Here's the fiddle: http://jsfiddle.net/gEJeW/2/


For more information, see here: Getting the "match" object in a Custom Filter Selector in jQuery 1.8

Community
  • 1
  • 1
Joseph Silber
  • 214,931
  • 59
  • 362
  • 292
2

Your attempt is looking for a class attribute that starts with your string, not a specific class name in that attribute that starts with your string.

I think you can probably just use the attribute contains logic:

$("body[class*='page-calendar-practices'] #content-header h1#title")

This could match class names that don't start with that, but it seems unlikely you'd be using class names like that.


If you wanted to be exact, I don't think you could do it in a simple selector - you'd probably need some javascript logic:

$(document.body).filter(function() {
    return((" " + this.className).indexOf(" page-calendar-practices") != -1);
}).find("#content-header h1#title");
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • +1 I like the way you prepend a space, then just use that in your regex. Nice touch! – Joseph Silber Jun 20 '12 at 18:51
  • @JosephSilber - I just removed the regex since I realized it wasn't needed. I learned the space addition trick in jQuery source. I like your jQuery custom selector. I didn't know it was that easy to extend. – jfriend00 Jun 20 '12 at 18:54
  • That's right. Hope you don't mind me *borrowing* this method for my filter expression. – Joseph Silber Jun 20 '12 at 18:56
1

http://api.jquery.com/attribute-starts-with-selector/

jQuery('[attribute^="value"]')

Description: Selects elements that have the specified attribute with a value beginning exactly with a given string.

Yours doesn't start with it - it has the matching string halfway through

You want to use the * selector: http://api.jquery.com/attribute-contains-selector/

ChrisW
  • 4,970
  • 7
  • 55
  • 92