views:

64

answers:

5

I just asked a question here about selecting all id's of the form id_* where * is any string and id is the id of the element. I got a great working solution:

$("*[id^=" + id + "_]").each(function() {... // id is the element name

I need to take this one step further now: All of my ids will be of the form: a_b_c ... where a b and c are arbitrarity strings (that do NOT contain a '_'). So, now rather than selecting all the elems of the form id_* where * could be a_b_c ... I need only to select 1 level deep: i.e. all elems of the form id_a. So in other words it stops at the next '_'. Is this possible?

As an example:

If my id is: first And there exist id's: first_second, first_second_third It will select only first_second

A: 

What I ended up doing (and I'm open to faster implementations) is:

$("*[id^=" + id + "_]").each(function() {
   //here I simply split the id and test the size of the array
   //if its too large (i.e. too deep in the tree), I return true (to continue
   //  to the next iteration):

   var row = $(this);
   var split = row.attr('id').split("_");

   if(split.length > SOME_PREDETERMINED_VAL)
      return true;

   //code here
});

I am not totally happy with this since it still traverses all elements (or would it do this anyway regardless of the filter in the each() function??).

aeq
A: 

This doesn't give you the whole solution, but you could try the attribute prefix selector or the attribute starts with selector

That will allow you to select any descendant of an element:

$("[id^='a_b_']").each(....

Will think how to remove the grandchildren etc, but this might get you started.

EDIT

I have just found that a similar question was asked about jQuery wildcards - this looks as if it will do what you need.

Giles
A: 

It seems like you are seriously overcomplicating this task. Let's say your structure is currently like this:

<div id="a">
    <div id="a_b">
        <div id="a_b_c">
        </div>
    </div>
</div>

Why don't you just do something along these lines...

<div id="a">
    <div class="b">
        <div class="c">
        </div>
    </div>
</div>

So, if I JUST wanted #a .b I would do:

$("#a .b").not("#a .c").show();  

Makes it a bit more semantic and readable as well. Am I understanding what you're trying to do? Might need to shed a bit more light on what exactly you're doing

Plan B
You shouldn't have multiple divs with the same ID's. You can, however have divs with the same names. `<div name="a" class="b">` `$('div[name="a"].b')`
Rocket
AH, good point. I'm hung over, rewriting
Plan B
the problem is in your assumption of my structure (my fault for not elaborating). Like I said in one of my comments, its only a logical table..so it doesn't follow a nice hierarchy (like you showed with the nested divs). Its rather complicated since the tree is within a table structure (specifically the row headers). So while I'm sure it may be regular in some way, using a relative patter would be crazy complicated (much more so than the solution I was getting at with my question).
aeq
A: 

Sounds like you are storing too many values in the id of the field. With HTML5 we now have data- attributes.

Perhaps, you should be making use of data- attributes something like this to link them?

<div id="a">
</div>

<div id="b0" data-parentId='a'>
</div>

<div id="b1" data-parentId='a'>
</div>

<div id="b2" data-parentId='a'>
</div>

<div id="c" data-parentId='b1'>
</div>

It will still validate, as any non-standard attribute starting with data- will be considered valid.

See: http://ejohn.org/blog/html-5-data-attributes/

Your jQuery selectors can then make use of this new attribute, rather than trying to parse strings

Something like this would select all of a's children

var childrenOfA = $("div[data-parentId='a']);
Chad
A: 

The obvious solution is changing your document, for example instead of

<div id="a"></div>
<div id="a_b"></div>
<div id="a_b_c"></div>

you could write

<div id="a" class="l1"></div>
<div id="a_b" class="l2"></div>
<div id="a_b_c" class="l3"></div>

and then select $('.l2[id^=a_]'). If that is not an option, you could try some sort of sieve scheme:

var set = $('[id^='+id+'_]'), i = 0;
while (i < set.length) {
    var e = set.eq(i);
    if (e.attr('id').substr(id.length+1).match('_')) {
        set = set.not(e);
    } else {
        i++;
    }
    set = set.not($('[id^='+e.attr('id')+'_]'));
}

(I haven't tested, so there might be errors, and I'm not sure not is the one that subtracts from a result set, but you get the idea.)

It depends on the document structure and browser whether this will be actually faster than the naive implmentation of simply walking through the while set and skipping everything with two _ in the id. (High number of branches per node helps, and it will be probably faster on browsers which have a native implementation of the CSS3 prefix selector which jQuery can call on.)

update: fixed some mistakes. The logic might change depending on your structure, e.g. the innermost if branch is unnecessery if foo_bar always precedes foo_bar_baz.

Tgr