1

I've realized that several times lately I've had a need to incorporate text node selection in some jQuery selectors.

jQuery doesn't natively really have any support for textnodes. Most answers about questions of this type recommend using .contents() but this doesn't really come under "selectors" but rather "traversing". As such it only gives direct children and it gives all children, not just text nodes.

I find I need to select all the text nodes within arbitrarily marked-up text, so some of the text nodes I'll be interested in will be grandchildren, great-grandchildren, etc:

<div id="formattedText">
  This is a <b>sample</b> block <a href="http://somewhere.com">of <em>text</em></a>
  that I might want to get a list of <span class="groovy">all</span> text nodes in.
</div>

So the most expressive way to achieve this rather than trying to implement recursive calls to .children() would be to extend the jQuery selector to something like this:

var txtNodes = $('#formattedText §');

Where § stands for some new bit of syntax I would add to jQuery representing a text node. I'd have to find some unused ASCII symbol or something like the :odd() extensions:

var txtNodes = $('#formattedText:textNodes()');

How would one go about extending jQuery's selector engine this way? I'd assume I'd want to hook into their existing DOM-walking methods and not have to resort to building new ones?

Or maybe there's an expressive trick using .contents() that I'haven't thought of that does do what I want without me having to extend jQuery?


My use case: I want to do custom "highlighting" of text from a 3rd party website in a userscript (greasemonkey etc). By "custom" I mean different patterns will get different background-colors etc. So I need all the text nodes no matter their depth and I don't want the tags around them and I don't want all the text in one big chunk as .text() would give me.

hippietrail
  • 15,848
  • 18
  • 99
  • 158

1 Answers1

1

I haven't caught up on the new signature required to make :-pseudo-selector extensions, but here's a method:

(function($){

    function walk( node, ret ) {
        var ret = ret || [],
            cur,
            nodes = node.childNodes;

        for( var i = 0, l = nodes.length; i < l; ++i ) {
            cur = nodes[i];

            if( cur.nodeType === 1 ) {
                walk( cur, ret );
            }
            else if( cur.nodeType === 3 ) {
                ret.push( cur );
            }
        }

        return ret;
    }

    $.fn.findText = function() {

        var ret = this.map( function() {
            return walk( this );
        });

        return this.pushStack( ret, "textNodes", "" );
    };

})( jQuery );

On the demo here, I'll iterate over the found textnodes in order they were found so you can see the order on the page.

Esailija
  • 138,174
  • 23
  • 272
  • 326