156

Here's the rough HTML I get to work with:

<li class="par_cat"></li>
<li class="sub_cat"></li>
<li class="sub_cat"></li>
<li class="par_cat"></li> // this is the single element I need to select
<li class="sub_cat"></li>
<li class="sub_cat"></li>
<li class="sub_cat current_sub"></li> // this is where I need to start searching
<li class="par_cat"></li>
<li class="sub_cat"></li>
<li class="par_cat"></li>

I need to traverse from the .current_sub, find the closest previous .par_cat and do stuff to it.

.find("li.par_cat") returns the whole load of .par_cat (I've got about 30 on the page). I need target the single one.

isherwood
  • 58,414
  • 16
  • 114
  • 157
daulex
  • 1,615
  • 2
  • 12
  • 10

5 Answers5

277

Try:

$('li.current_sub').prevAll("li.par_cat:first");

Tested it with your markup:

$('li.current_sub').prevAll("li.par_cat:first").text("woohoo");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li class="par_cat"></li>
  <li class="sub_cat"></li>
  <li class="sub_cat"></li>
  <li class="par_cat">// this is the single element I need to select</li>
  <li class="sub_cat"></li>
  <li class="sub_cat"></li>
  <li class="sub_cat current_sub">// this is where I need to start searching</li>
  <li class="par_cat"></li>
  <li class="sub_cat"></li>
  <li class="par_cat"></li>
</ul>

will fill up the closest previous li.par_cat with "woohoo".

OXiGEN
  • 2,041
  • 25
  • 19
karim79
  • 339,989
  • 67
  • 413
  • 406
  • thank you Karim. This worked well too, but I assume the .prev() would eat less resources, as .prevAll would get ALL the previous matched elements and then would filter the one I need. Accepting Ed's answer. – daulex Feb 22 '10 at 10:54
  • 3
    Actually, after a bit of texting, the .prev() failed in a few instances. .prevAll with :first, worked every time. Thank you. – daulex Feb 22 '10 at 11:13
  • 1
    .prev() only checks the immediately preceding element. I added the comment to the answer below along with the jsFiddle exemplifying. After looking around, this is the best answer as although it has to cycle through all the elements, it doesn't require two functions to grab all then filter to find it. – David Hobs Jun 27 '12 at 15:09
  • 10
    Does this mean `:first` is working on a list of returned elements that starts closest to the selected object (as opposed to top to bottom relative to the page)? – NReilingh Mar 10 '13 at 00:41
  • yeah the :first is needed, otherwise all prev will be selected. Thanks a lot for this answer – Johansrk Apr 11 '14 at 22:24
  • 2
    Thank you very much for the very useful answer. Can you please answer @NReilingh question because it's really confuses me. Isn't `:first` a CSS selector that selects the first element in *DOM* (e.g `div.red:first` will select the first red DIV in the *DOM*) did jQuery parse the selector differently ? – Accountant م Jul 26 '17 at 10:19
  • 1
    @Accountantم There is no CSS selector called `:first`. – TylerH Oct 22 '20 at 16:06
  • @TylerH oh now that makes sense, thanks. It's a jQuery only selector and it's free to parse it as it likes :D. – Accountant م Oct 22 '20 at 18:05
14

Using prevUntil() will allow us to get a distant sibling without having to get all. I had a particularly long set that was too CPU intensive using prevAll().

var category = $('li.current_sub').prev('li.par_cat');
if (category.length == 0){
  category = $('li.current_sub').prevUntil('li.par_cat').last().prev();
}
category.show();

This gets the first preceding sibling if it matches, otherwise it gets the sibling preceding the one that matches, so we just back up one more with prev() to get the desired element.

eprothro
  • 1,087
  • 11
  • 16
14

Try

$('li.current_sub').prev('.par_cat').[do stuff];
TylerH
  • 20,799
  • 66
  • 75
  • 101
Ed James
  • 10,385
  • 16
  • 71
  • 103
  • 23
    Wait, what? Doesn't this only check the immediately preceding element? Thus your code would return null? - Here it is tested on jsFiddle: http://jsfiddle.net/Y2TEH/ – David Hobs Jun 27 '12 at 15:05
  • 4
    @DavidHobs I wrote this two years ago.... I certainly would use prevAll() these days. – Ed James Jun 27 '12 at 16:29
  • 10
    @EdWoodcock consider updating your answer, in that case. –  Nov 10 '15 at 14:12
5

I think all the answers are lacking something. I prefer using something like this

$('li.current_sub').prevUntil("li.par_cat").prev();

Saves you not adding :first inside the selector and is easier to read and understand. prevUntil() method has a better performance as well rather than using prevAll()

Tom McDonough
  • 1,176
  • 15
  • 18
  • 1
    I tried this one first, what happened is prevUntil returns many elements, then it gets prev() for each of the many elements. The desired result is one element – JasonWilson Aug 17 '20 at 15:34
-1

You can follow this code:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
    $(document).ready(function () {
        $(".add").on("click", function () {
            var v = $(this).closest(".division").find("input[name='roll']").val();
            alert(v);
        });
    });
</script>
<?php

for ($i = 1; $i <= 5; $i++) {
    echo'<div class = "division">'
        . '<form method="POST" action="">'
        . '<p><input type="number" name="roll" placeholder="Enter Roll"></p>'
        . '<p><input type="button" class="add" name = "submit" value = "Click"></p>'
        . '</form></div>';
}
?>

You can get idea from this.

AL MaMun
  • 65
  • 7