Thursday, May 3, 2012

Iterating Through N Level Children


This seems like something neat that might be "built into" jQuery but I think it's still worth asking.



I have a problem where that can easily be solved by iterating through all the children of a element. I've recently discovered I need to account for the cases where I would need to do a level or two deeper than the "1 level" (just calling .children() once) I am currently doing.




jQuery.each(divToLookAt.children(), function(index, element)
{
//do stuff
}
);



This is what I'm current doing. To go a second layer deep, I run another loop after doing stuff code for each element.




jQuery.each(divToLookAt.children(), function(index, element)
{
//do stuff
jQuery.each(jQuery(element).children(), function(indexLevelTwo, elementLevelTwo)
{
//do stuff
}
);
}
);



If I want to go yet another level deep, I have to do this all over again.



This is clearly not good. I'd love to declare a "level" variable and then have it all take care of. Anyone have any ideas for a clean efficient jQueryish solution?



Thanks!


Source: Tips4all

5 comments:

  1. This is an awesome question because of the levels deep catch. Check out the fiddle.

    Converted this to a plugin.

    Activate

    $('#div').goDeep(3, function(deep){ // $.fn.goDeep(levels, callback)
    // do stuff on `this`
    });


    Plugin

    $.fn.goDeep = function(levels, func){
    var iterateChildren = function(current, levelsDeep){
    func.call(current, levelsDeep);

    if(levelsDeep > 0)
    $.each(current.children(), function(index, element){
    iterateChildren($(element), levelsDeep-1);
    });
    };

    return this.each(function(){
    iterateChildren($(this), levels);
    });
    };

    ReplyDelete
  2. This question is awesome :-)

    If you know your DOM is not too gigantic, you could just find all the descendants and filter out the ones that don't qualify:

    var $parent = $('#parent');
    var $childrenWithinRange = $parent.find('*').filter(function() {
    return $(this).parents('#parent').length < yourMaxDepth;
    });


    After that, the jQuery instance "$childrenWithinRange" would be all the child nodes of that parent <div> that are within some maximum depth. If you wanted exactly that depth, you'd switch "<" to "===". I may be off by one somewhere.

    ReplyDelete
  3. You should be able to just do it with the all-selector(docs), the child-selector(docs) and multiple-selector(docs) like this:

    Example: http://jsfiddle.net/mDu9q/1/

    $('#start > *,#start > * > *,#start > * > * > *').doSomething();


    ...or if you only wanted to target the children 3 levels deep, you could do this:

    Example: http://jsfiddle.net/mDu9q/2/

    $('#start > * > * > *').doSomething();


    Both of these selectors are valid for querySelectorAll, which means big performance boost in supported browsers.

    ReplyDelete
  4. The question sounds like the answer could be XPATH. I'm not well informed about the browser-support, but in XPATH you only need to create a path like

    /*/*/*/*



    https://developer.mozilla.org/en/introduction_to_using_xpath_in_javascript
    (works in FF,Chrome,Safari,Opera)
    http://msdn.microsoft.com/en-us/library/aa335968%28v=vs.71%29.aspx
    (didn't try it yet)

    ReplyDelete
  5. var lvlFunc = function(elmt, depth) {
    if(depth > 0) {
    elmt.children().each(function(i, e){
    // do stuff on the way down
    lvlFunc($(this), --depth);
    // do stuff on the way out
    });
    // do stuff
    }
    };

    lvlFunc(divToLookAt, 3);


    Make sure that you put your "do stuff" code in the right location if matters which order the "stuff" is performed in.

    ReplyDelete