3

Is there some method in jQuery to get all nested children? I mean complete collection from all nested levels so that I do not need to call function recursively.

Here is my function

$.fn.extend({
  compressElementsWidth: function() {
    var range = { min: 9999, max: 0 };
    console.log( $(this).contents() );

    $(this).contents().filter(
      function() { 
        if (this.nodeType == 3 )
           return this.nodeValue && this.nodeValue.replace(/\s{1,6}/,'') ? $(this) : null;
        else
           return this.nodeName.match(/IMG|A|GROUP|FIELDSET|INPUT|SELECT|TEXTAREA|BUTTON|SUBMIT/) ? $(this) : null;
      }).each(function(e) {
        var left = $(this).position();
        if (p.left < range.min ) 
          range.min = p.left;
        var right = p.left + $(this).width;
        if ( right > range.max ) 
          range.max = right;
    });
    var max_width = range.max - range.min; 
    $(this).contents().filter(
      function() 
      { 
        if (this.nodeType == 3 )
           return this.nodeValue && this.nodeValue.replace(/\s{1,6}/,'') ? $(this) : null;
        else
           return this.nodeName.match(/IMG|A|GROUP|FIELDSET|INPUT|SELECT|TEXTAREA|BUTTON|SUBMIT/) ? $(this) : null;
      }).each(function(e) {
        $(this).css("max-width:", max_width );
    });
  }
});
window.onload = function() {  
  $("div.column-right-outer").compressElementsWidth();
};

So I need to get all elements inside the div.column-right-outer. You can test this code on this page, just save it and include jQuery and the code above. For example in the Blog Archive, there is huge list of links and I need to get all the links and all the visible text which is under the right column. If there would be images or form elements I need them in the collection too.

enter image description here

Results of recursion and $(node).find("*") are about 10.000-16.000 and extremely slow performance. enter image description here

2
  • What are you planning on doing with all of this info? Commented Sep 1, 2016 at 15:23
  • This is a function which should calculate "complete content width" of the column (wrapper). In this particular page it is not perfect example. But if you would remove the column "Popular Posts", than it should compress width of the column so that the redundant width is removed. Commented Sep 1, 2016 at 15:28

5 Answers 5

4

method in JQuery to get all nested children

You can use the all selector : "*"

Combine this with the parent element in any number of ways:

var nodes = $("div.column-right-outer *");
var nodes = $("div.column-right-outer > *");
var nodes = $("div.column-right-outer").find("*");

If you need all items, then don't apply a parent:

var nodes = $("*")

Example fiddle: https://jsfiddle.net/9xted244/


.contents() is similar to "*" and will include text and comments

Sign up to request clarification or add additional context in comments.

3 Comments

I read that * is very slow. I tried it out though. The problem was there was about 26.000 of nodes in the result (It looked like there are nodes from the complete document). To be exact I tried this: $(this).find("*").contents() ... I expect 36 nodes or similar +/- 100 nodes in the list. I don't need the text which is not visible, only the visible elements.
User either * or .contents() - not both, or you'll get the same elements back multiple times.
$("*").filter(":visible")
0

What about element.childNodes ?

https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes

EDIT

childNodes returns a collection of node, including text nodes. Every tabulation and line break are text nodes.

2 Comments

"all children from all levels"- .contents() does exactly what you've requested: loads all child items below the selected item, recursively. It's not ".children" which only does one level below.
Strange function. When I change it to collect .prop("nodeName") it produces several thousands of div ul li span a.... no sense at all. It is extremy slow. Crazy. Similar like ...find("*") but maybe find is a bit faster.
0

I would suggest using jQuery's contents() method. It is used for this exact purpose, if I am not mistaken.

See the API here: https://api.jquery.com/contents/

Are you calling it in a frame load?

$("#frame").load(function() {

    ... contents method

});

3 Comments

Can you write a recursive function to call the contents method until there is nothing left/text?
See updated answer, try directly putting it in jQuery's load instead of window onload
Can you add console output given what you have thus far?
0

maybe write a function that dives into each thing?

function dive(that) {
    var element_width = that.width();
    array_width.push(that.width());
    if(that.children().length > 0){
        that.children().each(function(){
            dive($(this));
        });
    }
}

var array_width = [];
dive($('div.column-right-outer'));

This should push the widths of your elements into an array. I haven't tested it, but maybe this is a start?

Also, not sure how fast/slow it would be.

Comments

0

Here is the result of my tests.

1) console.log($('div.column-right-outer').contents()); gives three results

2.1) recursive call with contents() produces about 27.000 of results +/- extremely slow performance. 2.2) find("*") is similar, same count of results and extremely slow performance

3) console.log($('div.column-right-outer').find("*").filter(":v‌​isible")); produces 224 results. This is correct. The performance seems better but I think the find("*") still can reduce performance.

Now, the code of updated function:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
$.fn.extend({
  compressElementsWidth: function() {
    var range = { min: 9999, max: 0 };
    $(this).find("*").filter(":visible").filter(
      function() { 
       if (this.nodeType == 3 )
         return this.nodeValue && this.nodeValue.replace(/\s{1,6}/,'') ? $(this) : null;
       else
         {
         var result = this.nodeName.match(/IMG|A|GROUP|FIELDSET|INPUT|SELECT|TEXTAREA|BUTTON|SUBMIT/);
         return this.nodeName.match(/IMG|A|GROUP|FIELDSET|INPUT|SELECT|TEXTAREA|BUTTON|SUBMIT/) ? $(this) : null;
         }
      }).each(function(e) {
        var left = $(this).position();
        if (p.left < range.min ) 
          range.min = p.left;
        var right = p.left + $(this).width;
        if ( right > range.max ) 
          range.max = right;
    });
    var max_width = range.max - range.min; 
    $(this).find("*").filter(":visible").filter(
      function() 
      { 
       if (this.nodeType == 3 )
         return this.nodeValue && this.nodeValue.replace(/\s{1,6}/,'') ? $(this) : null;
       else
         return this.nodeName.match(/IMG|A|GROUP|FIELDSET|INPUT|SELECT|TEXTAREA|BUTTON|SUBMIT/) ? $(this) : null;
      }).each(function(e) {
        $(this).css("max-width:", max_width );
    });
  }
});
window.onload = function() {  
  $("div.column-right-outer").compressElementsWidth();
  // console.log($('div.column-right-outer').find("*").filter(":visible"));
};
</script>

To test it just save this page and insert the code above into the file.

Note: the function is still not fully working because it should enter to the function on line #16 where is var left =... this does not happen. It returns $(this) when result of the match is array. This happens when .nodeName is "A". But still it does not enter to the next function after return from here. I also tried to return this. Any idea how to fix this?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.