Search code examples
javascriptjquerychildrenclosest

JavaScript | jQuery | Selecting In Layers: Select Closest Children But Their Children


The goal is to select any descendant(s) -- regardless of direct-descendant indication -- but not their children. In other words, if I'm searching from document I'd like to find all children which are not wrapped by the target selector:

<div id="a1" class="scenario-1" data-behavior="test">
    test
    <div id="a2" data-behavior="test">
        test 1
        <div id="a3" data-behavior="test">
            test 2
        </div>
    </div>
</div>

<div class="scenario-2">
    <div id="b1" data-behavior="test">
        test 1
        <div id="b2" data-behavior="test">
            test 2
        </div>
    </div>
</div>

$(document).findAllInFirstLayer([data-behavior]);

This, ideally, would select #a1 & #b1 in the result-set. However, #a2, #a3, and #b2 should not be included, as this would proceed one scope to many.

The complimentary function for this will be recursive to drill down to the next scope for each layer-element in the set. So the next recursive call would return a set containing #a2, #b2, but not #a3 or any children ([data-behavior]) of #b2.

Also, this Question should not be marked as a duplicate of this question as the Accepted-Answer is not acceptable here -- the accepted answer here should use a jQuery selector or prove its impossibility with only using a jQuery selector.

Edit

With @guest271314's help, we reached the following answer:

'[data-behavior]:not([data-behavior] [data-behavior]), [data-behavior]:first'

Now, a recursive function can be used to take a parent-context and find the first-level scopes -- and recur in that fashion indefinitely. Here's an example:

arm: function autoRegisterModules(parent) {
    var $firstScope = $(parent).find('[data-behavior]:not([data-behavior] [data-behavior]), [data-behavior]:first');
    console.log('#context, #first-scope', parent, $firstScope);
    if ($firstScope.length) {
        $firstScope.each(function (i, p) {
            autoRegisterModules(p);
        });
    }
},

Please be sure to give credit where it's due.

#prethanks


Solution

  • This, ideally, would select #a1 & #b1 in the result-set.

    Try using :not() for first result set , :first for next results

    var first = $("[data-behavior]:not([data-behavior] [data-behavior])"),
      second = first.find("[data-behavior]:first"),
      third = second.find("[data-behavior]:first");
    
    console.log(first, second, third);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
    </script>
    <div id="a1" class="scenario-1" data-behavior="test">
      test
      <div id="a2" data-behavior="test">
        test 1
        <div id="a3" data-behavior="test">
          test 2
        </div>
      </div>
    </div>
    
    <div class="scenario-2">
      <div id="b1" data-behavior="test">
        test 1
        <div id="b2" data-behavior="test">
          test 2
        </div>
      </div>
    </div>


    So the next recursive call would return a set containing #a2, #b2, but not #a3 or any children ([data-behavior]) of #b2.

    using $.fn.extend()

    (function($) {
      $.fn.extend({
        layers: function(sel) {
          var root, next, res = [],
            sel = sel || this.selector;
          if ($(sel + ":not(" + sel + " " + sel + ")").length) {
            root = $(sel + ":not(" + sel + " " + sel + ")");
            res.push([root]);
            if (root.find(sel + ":first").length) {
              next = root.find(sel + ":first");
              res.push([next]);
              while (next.find(sel + ":first").length) {
                next = next.find(sel + ":first");
                res.push([next])
              }
            }
          }
          return this.data("layers", res)
        }
      })
    }(jQuery))
    
    var layers = $("[data-behavior]").layers().data("layers");
    $.each(layers, function(key, value) {
      console.log(key, value[0])
    })
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
    </script>
    <div id="a1" class="scenario-1" data-behavior="test">
      test
      <div id="a2" data-behavior="test">
        test 1
        <div id="a3" data-behavior="test">
          test 2
        </div>
      </div>
    </div>
    
    <div class="scenario-2">
      <div id="b1" data-behavior="test">
        test 1
        <div id="b2" data-behavior="test">
          test 2
        </div>
      </div>
    </div>