Abe Miessler
Abe Miessler

Reputation: 85036

jQuery selector for an element that directly contains text?

I was able to get this partially working using the :contains selector, but my problem is if an element contains an element that contains the text it is still returned. For example:

$('div:contains("test")')

Will select both divs below:

<div>something else
   <div>test</div>
</div>

fiddle: http://jsfiddle.net/TT7dR/

How can I select only divs that "directly" contain the text? Meaning that in the above example only the child div would be selected.

UPDATE:

Just to clarify, if I were searching for the text "something else" instead of "test" then I would like to only find the parent div.

Upvotes: 26

Views: 22295

Answers (10)

Matthias Gwiozda
Matthias Gwiozda

Reputation: 595

less code to write (but with a little limitation):

let selector = $('div:contains("test")');
selector.not(selector.has('div:contains("test")'))

Just use the jQuery function (.has) because the css :has is experimental: https://developer.mozilla.org/en-US/docs/Web/CSS/:has#Browser_compatibility


Limitation:

When you have a structure like this:

<div>
<div>test</div>
test
</div>

Then only the inner div - Element will be found by this solution. This is because there is still an Element - Child of the div that :contains the string "test".

Upvotes: 0

user1405377
user1405377

Reputation: 1

You can simply select the element that doesn't have your element

$('div:contains("test"):not(:has(> div))')

Upvotes: 0

Avatar
Avatar

Reputation: 15156

Just to complete the knowledge base. If you need to get all DOM elements within the body (not only DIVs) that contain specific text or characters you can use:

function getNodesThatContain(text) {
    var textNodes = $(document).find(":not(iframe, script)")
      .contents().filter( 
          function() {
           return this.nodeType == 3 
             && this.textContent.indexOf(text) > -1;
    });
    return textNodes.parent();
};

console.log(getNodesThatContain("test"));

Hope that helps.

jsfiddle: http://jsfiddle.net/85qEh/2/

Credits: DMoses

Upvotes: 9

Ari
Ari

Reputation: 4969

Add more alternative:

if($(selector).text().trim().length) {
   var thetext = $(selector).contents().filter(function(){
     return this.nodeType === 3;                     
   }).text().trim();    

  console.log(thetext);             

}

It will select the text only and remove any element with tag!

Reference

Upvotes: 0

Jor
Jor

Reputation: 699

Finds specific element, but not parents

var elementsContainingText = ($(':contains("' + text + '")', target)).filter(function() {
    return $(this).contents().filter(function() {return this.nodeType === 3 && this.nodeValue.indexOf(text) !== -1; }).length > 0;
});

Upvotes: 1

Yuvaraj Gunisetti
Yuvaraj Gunisetti

Reputation: 9

Try the following:

$("div>div:contains(test):only-of-type")

Upvotes: 0

rkw
rkw

Reputation: 7297

You might have to do an in-efficient query. Do not use this solution if someone finds a selector that manages to filter out child elements: http://viralpatel.net/blogs/2011/02/jquery-get-text-element-without-child-element.html

$("div:contains('test')")
    .clone()    //clone the element
    .children() //select all the children
    .remove()   //remove all the children
    .end()  //again go back to selected element
    .filter(":contains('test')")

edit: that snippet above is just to test the element, in implementation it would look more like this: http://jsfiddle.net/rkw79/TT7dR/6/

$("div:contains('test')").filter(function() {
    return (
    $(this).clone() //clone the element
    .children() //select all the children
    .remove() //remove all the children
    .end() //again go back to selected element
    .filter(":contains('test')").length > 0)
}).css('border', 'solid 1px black');

Upvotes: 6

bobince
bobince

Reputation: 536329

$('div>:contains("test")') is not a general solution, it only works for your specific example. It still matches any element whose descendants contain the text test, as long as its parent is a div.

There is in fact currently no selector that will select only direct parents of text nodes containing your target text. To do it you would have to walk the DOM tree yourself checking each text node you find for the target text, or write a plugin to do the same. It'd be slow, but then not as slow as :contains already is (it's not a standard CSS selector so you don't get browser-native fast selector support).

Here's a plain DOM function you could use as a starting point. It might be improved to find text in adjacent (non-normalised) text nodes, or to hide it in a plugin/selector-extension.

function findElementsDirectlyContainingText(ancestor, text) {
    var elements= [];
    walk(ancestor);
    return elements;

    function walk(element) {
        var n= element.childNodes.length;
        for (var i= 0; i<n; i++) {
            var child= element.childNodes[i];
            if (child.nodeType===3 && child.data.indexOf(text)!==-1) {
                elements.push(element);
                break;
            }
        }
        for (var i= 0; i<n; i++) {
            var child= element.childNodes[i];
            if (child.nodeType===1)
                walk(child);
        }
    }
}

Upvotes: 12

Clive
Clive

Reputation: 36957

This seems to work for me:

$('div >:contains("test")');

http://jsfiddle.net/TT7dR/1/

This forces the matched :contains selector to be a direct child of the <div> element

Upvotes: 0

Hussam
Hussam

Reputation: 510

try adding the greater than:

$('div>:contains("test")')

Upvotes: 3

Related Questions