0

Is there a way in jQuery to, given an element and a selector, select the last match of that selector before that element from an in-order traversal of the DOM tree?

For example, given the DOM tree:

<html>
  <body>
    <div class="a" id="div0" />
    <div>
      <div class="a" id="div1"/>
    </div>
    <div class="b" id="div2"/>
    <div id="element"/>
    <div class="a" id="div3"/>
    <div class="b" id="div4"/>
  </body>
</html>

Using the selector .a and element #element, you'd get #div1, and using the selector .b and the element #element, you'd get #div2.

The use case is writing a GreaseMonkey script to work across different versions of Firefox with some slightly mangled HTML. I've got an element I can find consistently in all versions, but another element I want to find is either a previous sibling of an ancestor of the element or a descendent of a previous sibling of an ancestor of the element (depending on the version).

All I can really rely on is that it is the last match of the selector that occurs before the element I have when doing an in-order traversal of the DOM tree.

rampion
  • 87,131
  • 49
  • 199
  • 315
  • 1
    I find real HTML is easier to read the pseudo code like this. – gdoron Mar 05 '12 at 18:46
  • I've previously written a `realPrev` plugin. See: [jQuery .prev() of a type regardless of it's parent etc](http://stackoverflow.com/a/7771241/938089?jquery-prev-of-a-type-regardless-of-its-parent-etc). In your case, you can use: `$('#element').realPrev('.a,#element');` – Rob W Mar 05 '12 at 18:46
  • -1 because of the poorly formated NOT html which makes it very hard to determine the closures/wraps of the divs in your example. – Mark Schultheiss Mar 05 '12 at 18:56

3 Answers3

3

This should do it, using your first example:

var $collection = $(".a, #element");

var eleIndex = $collection.index($("#element"));

var prevEl = $collection.eq(eleIndex - 1);

alert(prevEl.attr("id"));

http://jsfiddle.net/WdsGa/

James Montagne
  • 77,516
  • 14
  • 110
  • 130
  • Come to think of it, mine is quite similar to your answer as well. – Starx Mar 05 '12 at 19:05
  • and for whatever reason, `$collection.index` wasn't working under old Firefox (my guess is equality on XPCNativeWrappers), so I had to do that bit manually. But still, very helpful. – rampion Mar 05 '12 at 20:03
0

Sortof. This will work for sibling elements just fine:

$("div.a:first").nextUntil("#element").last();

however, your nested div.a will not work with that snippet, I'm not sure of an efficient way to solve that. It selects the first element matched by div.a, then all siblings after it until it finds an element that matches #element, and then gets the last of those selected elements. But, again, it only works with siblings.

Kevin B
  • 94,570
  • 16
  • 163
  • 180
0

This should do it

var last = $(".a, #element").eq($(".a, #element").length-1);
Starx
  • 77,474
  • 47
  • 185
  • 261
  • 2
    Unless I'm misunderstanding this, all it does is reproduce jquery's `last` in a less efficient way. http://api.jquery.com/last/ – James Montagne Mar 05 '12 at 18:50