3

What is the best way to get closest (up tree) given a class, but it can be a sibling of a parent.

I want to get "errors1" element start with element ".start"

<div>
    ...
    <div class="errors">errors2</div>
    <div>
        .....
        <div class="errors">errors1</div>
        ...
        <div>...</div>
        <div>
            .....
            <div class="start"></div>            
        </div>     
    </div>
</div>

Made a script but its really bad and doesnt work at all.

http://jsfiddle.net/9DfCJ/1/

Thanks

EDITED: Added "..." to enhance that structure is dynamic. The point is to find the closest ".errors" up tree.

TheVillageIdiot
  • 40,053
  • 20
  • 133
  • 188
Luccas
  • 4,078
  • 6
  • 42
  • 72

6 Answers6

1

A function to return the item as a jQuery object so it is more reusable.

This is more efficient than Byran's answer because it only looks at one level at a time rather than loading all parents and checking all levels of children via find (http://api.jquery.com/find/) each time.

To use, just paste the function into your javascript file or script tags after jQuery has been loaded.

$('.start').closestParentsAndSibling( '.errors').html('found!');

(function( $ ){
   $.fn.closestParentsAndSibling = function(search) {
        // Get the current element's siblings
        var siblings = this.siblings(search);

        if (siblings.length != 0) { // Did we get a hit?
            return siblings.eq(0);
        }

        // Traverse up another level
        var parent = this.parent();
        if (parent === undefined || parent.get(0).tagName.toLowerCase() == 'body') {
            // We reached the body tag or failed to get a parent with no result.
            // Return the empty siblings tag so as to return an empty jQuery object.
            return siblings;
        }
        // Try again
        return parent.closestParentsAndSibling(search);
   }; 
})( jQuery );
Loren
  • 9,783
  • 4
  • 39
  • 49
1

Investigate jQuery's prev() command. For example, to find and hide the previous 'errors' when in the context of the 'start' class, you would use the following:

$('.start').click(function(){
  $(this).prev('.errors').hide();
});

Read more here: http://api.jquery.com/prev/

UPDATE:

You may have better luck using parent() to traverse upward if it is heavily nested. parent() also takes a selector as an argument... without an argument it grabs the parent of the current node.

http://api.jquery.com/parent/

philwinkle
  • 7,036
  • 3
  • 27
  • 46
1
$(this).parent().siblings(':first')
Rich
  • 7,146
  • 1
  • 23
  • 25
  • He's looking for a way to query by the class from the parent. In this case `$(this).parent('.errors')`; – philwinkle Feb 15 '11 at 04:25
  • i forgot to say that the structure may vary. The point is to achieve the closest ".errors" in the tree – Luccas Feb 15 '11 at 04:26
1

Great question. The code below should work for you. It loops over each parent of .start until it finds an element with .errors. Then it alerts the text of the .errors element that is closest to .start.

jQuery(function($){

    var $start = $('.start'),
        $parents = $start.parents();

    $parents.each(function(){
        var $this = $(this),
            $thisErrors = $this.find('.errors'),
            numErrors = $thisErrors.length;

        if(numErrors){
            alert($thisErrors.eq(numErrors-1).text());
            return false;
        }

    });

});
Bryan Downing
  • 15,194
  • 3
  • 39
  • 60
  • 1
    I guess it's better that you replace the find selector for '.errors:not(.start .errors)' to avoid getting .errors element that is descendant of the .start or if the start element is hard to be selected with a selector add jQuery.contains($start, $errorsMatchElement) validation before returning with a match. – g8M Apr 03 '14 at 11:58
0

If you want to go up the tree, and get all the errors, you could do something like that:

var start = $(".start"),
    current = start.parent(),
    parent = current,
    end_class = "stop",
    get_errors = function(elt) {
        return elt.children(".errors");
    },
    errors = get_errors(current)

    while (parent = current.parent()) {

        $.merge(errors, get_errors(parent));
        if (parent.hasClass(end_class)) {
           break;
        }   
        current = parent;
    }

At the end, errors will contain all the errors.

You would have to add the end_class to your top div though.

<div class="stop">
    ...
    <div class="errors">errors2</div>
    ...
</div>
Robin
  • 21,667
  • 10
  • 62
  • 85
0

I did some digging and found something to get the first element that that matches the selector, beginning at the current element and progressing up :

var myText = $(".start").closest($(".errors")).text(); 

read more about it here and you can use your event to call this code.

himanshu dhiman
  • 293
  • 1
  • 4
  • 13