Jquery First occurrence tree traversal up parents/siblings

3.3k Views Asked by At

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.

6

There are 6 best solutions below

1
On BEST ANSWER

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;
        }

    });

});
2
On
$(this).parent().siblings(':first')
0
On

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>
0
On

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/

0
On

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.

0
On

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 );