jQuery selector for an element that directly contains text?

16,251

Solution 1

$('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);
        }
    }
}

Solution 2

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

Solution 3

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');

Solution 4

try adding the greater than:

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

Solution 5

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;
});
Share:
16,251

Related videos on Youtube

Abe Miessler
Author by

Abe Miessler

Software Engineer who works with Javascript/Node.js, Python, C#, Go, SQL Server, MongoDB, MySQL and a whole lot more. I enjoy learning new technologies when they are the best tool for the job. I usually fill the role of a full stack engineer but always seem to enjoy working with data the most. 80th recipient of the Gold SQL badge 50th recipient of the Gold SQL Server badge Hobbies include web application security and machine learning.

Updated on June 04, 2022

Comments

  • Abe Miessler
    Abe Miessler almost 2 years

    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.

  • ThiefMaster
    ThiefMaster over 12 years
    div matches also the inner div, it doesn't really matter except that with > the outer div won't tested for its contents
  • Abe Miessler
    Abe Miessler over 12 years
    Close but not quite there. If you switched it to $('div>:contains("something else")') it alerts 0 when it should alert 1. jsfiddle.net/TT7dR/4
  • Nick Rolando
    Nick Rolando over 12 years
    @genesis Thanks. Based on what you said, I don't see how this works in this case. We're trying to get the div that contains 'test', not for its children (which it has none of).
  • Abe Miessler
    Abe Miessler over 12 years
    Close but not quite there. If you switched it to $('div>:contains("something else")') it alerts 0 when it should alert 1. jsfiddle.net/TT7dR/4
  • Yuvaraj Gunisetti
    Yuvaraj Gunisetti almost 10 years
    did>div refers the div child whose parent is div and containing test and which is of only one child to parent div.
  • Mahn
    Mahn about 9 years
    Not something for production but it's suprisingly fast for debugging purposes.
  • mrbellek
    mrbellek over 8 years
    Note that since this clones the elements, you cannot perform any useful changes on them, since you are altering the clones.