A jQuery find that also finds the root element

March 14, 2011 | categories: jQuery, Web, Python, JavaScript, Programming | View Comments

jQuery's find method is arguably the most used method in jQuery applications. Yet, when using .find() recently, I found out that it makes a rather weird exception for root element(s) of a document, that is, it ignores them.

Consider this example from the comments in the find API:

var el = $('<div id="one"><div id="two"></div></div>').find("#one");

el will be empty here, because <div id="one"> is the root element. It would work if the element were nested inside, say, another div.

In a recent project that uses .find() to apply progressive enhancement to parts of the page that have been updated through Ajax, this became a pain.

Consider this success callback function that

  • replaces parts of the page with updated content from the server
  • re-enables Ajax forms on the updated content
function success(data) {
    $("#content").replaceWith(data);
    enableAjaxForms($("#content"));
}

function enableAjaxForms(node) {
    node.find("form.ajax").ajaxForm({
        success: success
    });
}

As long as the server returns a document with a form that's not the root element, this will work. But when we return HTML that has a form element as the root, our enableAjaxForms function will silently fail to find the form:

<form class="ajax"> <!-- form.ajax is root, .find() won't find it -->
  <div> ... </div>
</form>

What do? There's another function in jQuery that filters on the root elements. It's called .filter() and finds only the root elements. So this will work:

var el = $('<div id="one"><div id="two"></div></div>').filter("#one");

To get the results we want, we need to combine .filter() and .find(). We don't care whether the element we're looking for is at the root or not.

So here's a rather simple implementation of a jQuery.find2 method that'll return both root and child elements as the result of our query:

$.fn.find2 = function(selector) {
    return this.filter(selector).add(this.find(selector));
};

And finally, this is how you would use it. That is, just like you use .find() really:

var html = '<div class="one"><div class="one"></div></div>';
var el = html.find2(".one"); // will match both divs