allanberry
allanberry

Reputation: 7765

Match DOM text without tags?

I need to match text within a string, using JavaScript or jQuery, at any DOM level, which isn't part of an HTML tag. So, for example, I need to match the word foo in the following passage exactly 5 times:

Foo <p class="foobar" id='foo'>foobar foo<a href="/foo">foo</a> foo.</p>

I need to keep the full text intact, with tags, for later use. This is to wrap each result in a <span /> tag using find/replace, an effort which currently fails when it incorrectly matches parts of tags and links.

It seems obvious to use jQuery's .html() method, but this means I have to dodge those pesky tags. The necessary regex is getting too complicated for me to handle. (I also know regex on HTML carries some risk; I'm open to another approach.)

For example, I tried using jQuery's .text() method, which lets me manipulate the text as I want using find/replace, but it's seemingly impossible then to update the page source, because you're dealing with the DOM's text node: it won't let me insert those <span> tags into the source.

Otherwise, I'm stumped. Thanks!

Upvotes: 1

Views: 182

Answers (2)

Richard Hamilton
Richard Hamilton

Reputation: 26434

To target selectors that contain a particular string, use jQuery's contains selector. You can then iterate over all such elements with the each function.

$('*:contains("foo")').each(function() {
    $(this).html("<span class='bold'>" + $(this).text() + "</span>");
});

It should be noted that the contains selector is case sensitive! We need to modify the jQuery method to allow Foo to be found. Add this code to your jQuery file.

jQuery.expr[':'].contains = function(a, i, m) {
  return jQuery(a).text().toUpperCase()
      .indexOf(m[3].toUpperCase()) >= 0;
};

Another solution is to use jQuery's filter function like so

var selection = $("*").filter(function() {
    return /FOO/i.test($(this).text());
});

This is probably a better practice because you don't have to modify the original jQuery code. The test method takes a regular expression and returns a Boolean on whether or not a string contains said expression. The /i flag makes the regex case insensitive. It will match the following

  • Foo
  • foo
  • FOO
  • fOo

Upvotes: 1

Web and Flow
Web and Flow

Reputation: 143

To access just the text use the .text() method, so : (had to escape single quotes)

$('<div>Foo <p class="foobar" id=\'foo\'>foobar foo<a href="/foo">foo</a> foo.</p></div>').text()

returns: "Foo foobar foofoo foo."

Do what you will from there.

Upvotes: 1

Related Questions