jQuery selector for an element that directly contains text?

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

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


try adding the greater than:

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

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