Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preventing downward traversal in jQuery once matches are found

I'm looking to use a selector which will stop traversing downward once a match is found while still traversing through siblings. Consider the following:

<ul>
  <li class='foo'>
    <ul>
      <li  class='bar'>
      </li>
    </ul>
  </li>
  <li class='baz'>
  </li>
</ul>

When using the selector $('ul li'), jQuery will return .foo, .bar, and .baz. Ideally, I want a selector which only has .foo and .baz.

My only thoughts would be to:

1) Traverse back up looking for $(selected).parents('ul ul') as a method for exclusion

2) Step through the heiarchy one child at a time and to stop traversal once a match is found.

3) Use the $.not() method to exclude ul li ul li like $('ul li').not('ul li ul li')

All of these solutions seem inelegant and slow since they require multiple invocations of jQuery and are pretty far removed from native DOM methods (namely document.querySelectorAll). Normally, i'd just go with #3 and call it a day, but I'm developing for an embedded system where cpu cycles are at a premium and chaining jQuery invocations could potentially lead to degraded performance.

Any thoughts?

Edit: The example given is just an abstraction of a possible tree structure I would be searching. The use case is to search for the closest match on a page as part of a keyboard navigation library. Since layouts differ in structure, I can't expect the matches to be direct descendants.

like image 382
devnill Avatar asked Dec 20 '25 23:12

devnill


1 Answers

The solution to your problem will require JavaScript, and your first and third ideas are where I see you finding your answer.

Just to clarify, CSS (and jQuery) selectors are interpreted from right to left. When you create a selector, the rightmost selector (the key selector) is the ultimate one you match. The browser will find the key selector, then search up (/backwards) the document until it finds a match or exhausts possible matches.

There is currently no parent CSS selector to do this the other way.

Here are samples of ways to get the behavior you're looking for with jQuery: http://jsbin.com/jazax/2/

You can get all li contained in a ul then do something if there are no li parent elements.

$("ul li").each(function() {

    var $this = $(this);
    if(!$this.parents("li").length) 
    {
      $this.prepend(":)");
    }

});

You can

$("ul li").not("li ul li").each(function () {

  var $this = $(this);
  $this.prepend(":)");

});

Or simplify that even further to reduce the number of selectors jQuery needs to match against:

$("ul li").not("li li").each(function () {

  var $this = $(this);
  $this.prepend("3");

});

Finally, here is a sample demonstrating a performance comparison. http://jsperf.com/downwardtraverseso


Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!