开发者

How to optimize $.find().first()?

I need to retrieve the first element.

I do that with this code...

$(element).find('.x').first();

As much as I understand, that code...

  1. Retrieves all elements from element that matched .x,
  2. 开发者_开发问答Removes unneeded elements;

Is there any better way to do it? Like $.findOne() or something?


As per jQuery docs:

Because :first is a jQuery extension and not part of the CSS specification, queries using :first cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using :first to select elements, first select the elements using a pure CSS selector, then use .filter(":first").

So rewriting your selector to:

$(element).find('.x').filter(":first")

or (this one will give you direct descendants only and will be faster than .find, unless you're looking for nested elements too)

$(element).children('.x').filter(":first")

should give you better results.


Update After valuable inputs from kingjiv and patrick dw (see comments), it does seem that these two are faster than .filter(':first') contrary to what the doc claims.

$(element).find('.x').first();   // faster

$($(element).find('.x')[0]);     // fastest


If you want to have it real fast, you should use native browsers methods. Modern browsers support querySelector [docs]:

var $result;
if(element.querySelector) {
    $result = $(element.querySelector('.x'));
}
else {
    $result = $(element).find('.x').first();
}

The usage is a bit limited, as it would only work if element is a single element and if the selector is a valid CSS selector. You could make a plugin out of it. But then, if you consider all cases, like multiple elements etc., there is probably no advantage anymore.

So again, if you have a very specific use case, this might be useful, if not, stick with jQuery.

Update: Turns out, making a plugin is still faster: jsPerf benchmark

(function($) {
    $.fn.findOne = function(selector) {
        try {
            var element, i = 0, l = this.length;
            while(i < l && (element = this[i].querySelector(selector)) === null) {
                i++;
            }
            return $(element);
        }
        catch(e) {
            return this.find(selector).first();
        }
    };
}(jQuery));

How this works:

The plugin iterates over the selected DOM elements and calls querySelector on each of them. Once an element is found, the loop will terminate and return the found element. There are two reasons an exception could occur:

  • The browsers does not support querySelector
  • The selector is not a pure CSS selector

In both cases the plugin will fall back to use the normal jQuery method.


As crazy as it seems, in every performance test I've seen, .first() has better performance than :first.

As most people are suggesting, it seems as though using $(element).find(".x:first") should have better performance. However, in reality .first is faster. I have not looked into the internals of jquery to figure out why.

http://jsperf.com/jquery-select-first

And apparently using [0] and then rewrapping in a jquery object is the fastest:

$($(element).find(".x")[0])

EDIT: See mrchief's answer for an explanation of why. Apparently they have now added it to the documentation.


This should be better

$(element).find('.x:first');


Use :first selector:

$(element).find('.x:first')


It's better to write:

$('a:first');

What you're writing is "in 'element', find '.x' and return the first one". And that can be expressed like this

$('.x:first', element);


how about using first-child pseudo class ? like

$(element).find('.x:first-child')

However it might generate issues if your structure is like

<div>
   <p></p>
</div>
<div>
   <p></p>
</div>

so actually it is not what you are looking for (if you mean general solution). Others mnetions :first and this seems to be the correct approach


Your bottleneck is really the .find(), which searches all the descendants instead of just the immediate children.

On top of that, you're searching for a class .x (which uses a jQuery custom search) instead of an ID or a tagname (which use native DOM methods).

I would use Mrchief's answer and then, if possible, fix those two bottlenecks to speed up your selector.


You could combine the $(element) and .find() calls using a descendant selector; I'm unsure of the performance comparison:

$("#element .x").first().hide();


That way is fine according to the jQuery documentation, or at least better than using :first selector.

You can try as alternatives .filter(":first") or get the first element using array accessor against the .find() result [0].

Also, instead of .find() you can change it to:

$('.x', element)

To narrow the search to .x elements inside element, intead of searching the whole document.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜