8

I want to create an array of all the html elements within a div that contain text strings, such as

<p>some string</p>.  

I don't want to get hold of the strings, I want the array items to be the elements (in the example, would be the p node). I do not know before hand what the strings will be, so I can't look for string values to match. I also don't want empty text nodes to end up in the array.

Thanks!

Tim Sheiner
  • 3,253
  • 4
  • 27
  • 22

8 Answers8

11
$("#my_div *").filter(function()
{
     var $this = $(this);
     return $this.children().length == 0 && $.trim($this.text()).length > 0;
})

This version will not return parent elements that contains the elements having texts, only the last level elements.

May not be the fastest but works quite well on StackOverflow homepage :)

Vincent Robert
  • 35,564
  • 14
  • 82
  • 119
  • 1
    From my testing, this was the solution that returned only the elements containing text, not their parents as well. Thanks!!! – Tim Sheiner Jun 19 '09 at 19:33
  • 1
    $('\*:not(:has(\*))') may be a better way to select children-less (leaf) nodes... definitely less typing. – Dave Dec 07 '11 at 22:24
  • This solution breaks when the element containing the text has children which do not contain the text. It only works when the element containing the text has no other children. – Dingredient Dec 31 '14 at 19:00
7

A custom selector could be helpful in your case:

jQuery.expr[':'].hasText = function(element, index) {
     // if there is only one child, and it is a text node
     if (element.childNodes.length == 1 && element.firstChild.nodeType == 3) {
        return jQuery.trim(element.innerHTML).length > 0;
     }
     return false;
};

After that, you can simply do this:

$('#someDiv :hasText') // will contain all elements with text nodes (jQuery object)
$('#someDiv :hasText').get() // will return a regular array of plain DOM objects

I am assuming that you are only trying to select elements that have ONLY text inside of them.

TM.
  • 108,298
  • 33
  • 122
  • 127
2

You can make use of not and the empty selectors to get the non-empty elements while converting to an array can be achieved using get

$("#theDiv > :not(:empty)").get();

The above selector gets all the children elements of "theDiv" and that aren't empty (i.e. they either have children or text) and then converts the matched set to an array.

If you want only elements that have text inside them, this should work...

$("#theDiv > :not(:empty, :has(*))").get();

To get rid of elements that have whitespace you can make use of filter

$("#theDiv > :not(:has(*))").filter(function() { 
                 return $.trim(this.innerHTML).length > 0;
         }).get();
John Foster
  • 8,697
  • 2
  • 35
  • 37
  • 1
    This won't work if he wants elements that ONLY have text inside. This also returns any elements that have any children, even if none of those children have text inside of them. – TM. Jun 19 '09 at 17:05
  • 1
    Another note, this would only work on immediate children of the div as well (not sure if this is the desired behavior or not). Remove the > to check all ancestors. The * also serves no purpose in this case. – TM. Jun 19 '09 at 17:17
  • Updated regarding selecting elements that only have text inside. Thanks for picking up on the * I've updated my answer accordingly – John Foster Jun 19 '09 at 17:23
  • Lower selector is almost right, but it will select elements that have empty (all whitespace) text nodes as well. – TM. Jun 19 '09 at 17:24
  • Got me on that one :). I was just trying to find a way to do it with a one-liner without resorting to anything like a closure (a subsequent call to filter would get rid of the elements with whitespace) or custom selector. – John Foster Jun 19 '09 at 17:39
1

You could cycle through the children and grab everything that has a .text() value != ""

Sampson
  • 265,109
  • 74
  • 539
  • 565
  • That sounds like a good plan but I don't know jQuery well enough to understand how construct syntax that does what you describe. Can you produce an example? Thanks. – Tim Sheiner Jun 19 '09 at 16:35
1
var array = [];
var divSelector = "div.mine";

$(divSelector).contents().each(function()
{
   // If not an element, go to next node.
   if (this.nodeType != 1) return true;       

   var element = $(this);
   if ($.trim(element.text()) != "")
     array.push(element);
});

array is the array of elements that have some text in them.

cdmckay
  • 31,832
  • 25
  • 83
  • 114
0

d is the div under which you want to find things
v is an empty array
i you have to start out at 0.

The $.trim is used so that you don't get nodes that are just whitespace.

$("*",d).filter( function() { 
     return $.trim($(this).text()) != ""
  } ).each( function() { 
     v[i] = $(this).text(); 
     i++; 
  } );

One can also use v.push($(this))...which is something that totally slipped my mind.

Thomas
  • 4,889
  • 4
  • 24
  • 19
  • This looks promising, will try it. If I only want the elements, not the actual text would the final assignment be: v[i] = $(this); ? – Tim Sheiner Jun 19 '09 at 16:45
  • It depends on whether you want it to be a jQuery object or not. If you want it to be a jQuery object, it would be $(this). If you just want the DOM element, it would just be this. – Thomas Jun 19 '09 at 16:47
  • And actually, you wouldn't even need to do that...all you would have to do is remove the .each(), since the only purpose for that was to pull out the text. (Sorry, I was somewhat distracted when I made the last comment. – Thomas Jun 19 '09 at 17:06
0
 $(function() {
        var array = new Array();
        $("#testDiv *").each(function(x, el) {

            if ($.trim($(el).text()) != '' ) {
                array.push(el);
            }
        });
        alert(array.length);
    });
bashmohandes
  • 2,356
  • 1
  • 16
  • 23
0

Use the :contains selector:

var matches = new Array();
$('#start_point *:contains(' + text + ')').each(function(i, item) {
 matches.push( item );
}
Tharsan
  • 955
  • 1
  • 6
  • 19