A jQuery find that also finds the root element
March 14, 2011 | 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