Skip to main content

Bug Tracker

Side navigation

#7263 closed bug (worksforme)

Opened October 21, 2010 02:25AM UTC

Closed November 20, 2010 02:14AM UTC

Last modified March 14, 2012 01:46AM UTC

jQuery.is(':first') not checking correctly

Reported by: qvprof@gmail.com Owned by:
Priority: undecided Milestone: 1.5
Component: unfiled Version: 1.4.3
Keywords: Cc: rwaldron, snover, ajpiano
Blocked by: Blocking:
Description

So jQuery.is(selector) suppose to return 'true' or 'false' but it doesn't seem to work with with the pseudo selectors :first & :last.

In a list of 3 items:

('li:first').is('li:last') //true, wrong, Bug?
('li:eq(0)').is('li:eq(2)')  //false, correct

http://jsfiddle.net/882Tc/

Tried it in a bunch of previous version and got the same result.

I'm not sure if I'm doing something wrong or I'm not understanding :first correctly... but it seems to me that :first & :last doesn't work correctly with jQuery.is()

Thanks You

Attachments (0)
Change History (15)

Changed October 21, 2010 03:26AM UTC by addyosmani comment:1

keywords: → is first last equal
status: newopen

I've thoroughly evaluated this here http://jsfiddle.net/882Tc/10/ and it would appear that if you directly compare any item as follows $('elem:first').is('elem:last') is will return a result 'true'. If however you cache the variables beforehand and do an $x.is($y) comparison that returns the correct result of false. Internally both :first,:last and .is are fairly simple in logic and it could be the case that we require additional logic to go in there to handle these cases. As mentioned, using caching allows you to get this working. Leaving open for further review.

Changed October 21, 2010 04:07AM UTC by qvprof@gmail.com comment:2

Hey addyosmani,

Thanks for checking the bug.

Not sure if it's related but when you cache an $x2 then do a $x.is($x2) returns false. Which it would seem should return true...

http://jsfiddle.net/882Tc/11/

Changed October 21, 2010 03:52PM UTC by jitter comment:3

_comment0: Partly this seems to be a misconception on how `is( selector )` works. \ \ One thing beforehand: `is( selector )` only works with strings. From \ http://api.jquery.com/is/ \ >**selector** A **string** containing a selector expression to match elements against. \ \ So you can take out of the picture all the tests where you tried to \ pass in an object as `is` will always return false in such a case. \ \ Some of the remaining sample tests are false negatives to me. The thing to keep in mind is that the `is`-function uses the matched elements as context not the document. \ \ [http://jsfiddle.net/jitter/hBkxs/ live test case] with explanations why I think most use cases work right. \ \ But there seems to be some kind of a bug. If you look at the last two tests \ \ {{{ \ <ul class="test" style="display:none"> \ <li>one</li> \ <li>two</li> \ <li>three</li> \ </ul> \ \ //true -> ERROR \ $( 'li:first' ).is( '.test li:last' ) \ \ //true -> ERROR \ $( 'li:first' ).filter( '.test li:last' ).length > 0 \ }}} \ \ Both should return `false` in my opinion as there is only a `li` in the set of matched elements which neither has the class `test` assigned nor has it any `li` children. \ \ If you switch from 1.4.3 to 1.4.2 another strange thing happens. \ \ The `filter` test returns `false` correctly but the `is` test still returns `true`. So we have both a regression in `filter` and a case where \ \ {{{ \ $( sel ).is( secondsel ) != $( sel ).filter( secondsel ).length > 0 \ }}} \ \ which shouldn't happen. \ \ More discussion needed. Does someone follow my view on this?1287700080900512
_comment1: Partly this seems to be a misconception on how `is( selector )` works. \ \ One thing beforehand: `is( selector )` only works with strings. From \ http://api.jquery.com/is/ \ >**selector** A **string** containing a selector expression to match elements against. \ \ So you can take out of the picture all the tests where you tried to \ pass in an object as `is` will always return false in such a case. \ \ ~~Some of the remaining sample tests are false negatives to me. The thing to keep in mind is that the `is`-function uses the matched elements as context not the document. \ \ [http://jsfiddle.net/jitter/hBkxs/ live test case] with explanations why I think most use cases work right. \ \ But there seems to be some kind of a bug. If you look at the last two tests \ \ {{{ \ <ul class="test" style="display:none"> \ <li>one</li> \ <li>two</li> \ <li>three</li> \ </ul> \ \ //true -> ERROR \ $( 'li:first' ).is( '.test li:last' ) \ \ //true -> ERROR \ $( 'li:first' ).filter( '.test li:last' ).length > 0 \ }}} \ \ Both should return `false` in my opinion as there is only a `li` in the set of matched elements which neither has the class `test` assigned nor has it any `li` children. \ \ If you switch from 1.4.3 to 1.4.2 another strange thing happens. \ \ The `filter` test returns `false` correctly but the `is` test still returns `true`. So we have both a regression in `filter` and a case where \ \ {{{ \ $( sel ).is( secondsel ) != $( sel ).filter( secondsel ).length > 0 \ }}} \ \ which shouldn't happen.~~ \ \ More discussion needed. Does someone follow my view on this?1287700151550853
_comment2: Partly this seems to be a misconception on how `is( selector )` works. \ \ One thing beforehand: `is( selector )` only works with strings. From \ http://api.jquery.com/is/ \ >**selector** A **string** containing a selector expression to match elements against. \ \ So you can take out of the picture all the tests where you tried to \ pass in an object as `is` will always return false in such a case. \ \ ~~Some of the remaining sample tests are false negatives to me. The thing to keep in mind is that the `is`-function uses the matched elements as context not the document.~~ \ \ ~~[http://jsfiddle.net/jitter/hBkxs/ live test case] with explanations why I think most use cases work right.~~ \ \ ~~But there seems to be some kind of a bug. If you look at the last two tests~~ \ \ {{{ \ <ul class="test" style="display:none"> \ <li>one</li> \ <li>two</li> \ <li>three</li> \ </ul> \ \ //true -> ERROR \ $( 'li:first' ).is( '.test li:last' ) \ \ //true -> ERROR \ $( 'li:first' ).filter( '.test li:last' ).length > 0 \ }}} \ \ ~~Both should return `false` in my opinion as there is only a `li` in the set of matched elements which neither has the class `test` assigned nor has it any `li` children.~~ \ \ ~~If you switch from 1.4.3 to 1.4.2 another strange thing happens.~~ \ \ ~~The `filter` test returns `false` correctly but the `is` test still returns `true`. So we have both a regression in `filter` and a case where~~ \ \ {{{ \ $( sel ).is( secondsel ) != $( sel ).filter( secondsel ).length > 0 \ }}} \ \ ~~which shouldn't happen.~~ \ \ More discussion needed. Does someone follow my view on this?1287707067294553

Partly this seems to be a misconception on how is( selector ) works.

One thing beforehand: is( selector ) only works with strings. From

http://api.jquery.com/is/

>**selector** A **string** containing a selector expression to match elements against.

So you can take out of the picture all the tests where you tried to

pass in an object as is will always return false in such a case.

~~Some of the remaining sample tests are false negatives to me. The thing to keep in mind is that the is-function uses the matched elements as context not the document.~~

~~[http://jsfiddle.net/jitter/hBkxs/ live test case] with explanations why I think most use cases work right.~~

But there seems to be some kind of a bug. If you look at the last two tests

<ul class="test" style="display:none">
    <li>one</li>
    <li>two</li>
    <li>three</li>
</ul>

//true -> ERROR
$( 'li:first' ).is( '.test li:last' )

//true -> ERROR
$( 'li:first' ).filter( '.test li:last' ).length > 0

Both should return false in my opinion ~~as there is only a li in the set of matched elements which neither has the class test assigned nor has it any li children.~~

~~If you switch from 1.4.3 to 1.4.2 another strange thing happens.~~

~~The filter test returns false correctly but the is test still returns true. So we have both a regression in filter and a case where~~

$( sel ).is( secondsel ) != $( sel ).filter( secondsel ).length > 0

~~which shouldn't happen.~~

More discussion needed. Does someone follow my view on this?

Changed October 21, 2010 06:55PM UTC by qvtree comment:4

Hey jitter, thanks for testing.

I didn't realize that jQuery.is filters based on it's context. I always thought it was global.

I ran a few more test cases and think that it actually isn't a bug after all. But what do you guys think?

Seems the reason why:

//Expected False but its True?
log($( 'li:first' ).is( '.test li:last' ), false);

Might be because li:first IS below .test AND is :last in it's context (of only one list item)

So perhaps the jQuery.is documentation just needs to make it clearer that it's contextual and explains that for hierarchy searches, it goes outside it's context, for pseudo searches it stays within it's context.

But perhaps it wasn't meant to work like that.

Changed October 22, 2010 12:23AM UTC by jitter comment:5

Hmm now I am somewhat confused. After looking at this again I ask myself how/why earlier today I came to the conclusion that jQuery.is( selector ) is using the matched elements as context and not document.

Right now I came to the opposite conclusion which would match the original statements of qvprof and addyosmani and make the better part of my previous comment invalid (that's why I did stroke it out). Maybe I was looking at something else...wtf?

Can someone double check this?

So most of my previous comment can be forgotten because it is wrong (or is it). The only thing which stands is my remark about the tests which pass in an object.

I made some further tests test case trying to trim down where the bug occurs. And especially this version of the bug I found doesn't make any sense at all

<ul class="test" style="display:none">
    <li>one</li>
    <li>two</li>
    <li>three</li>
</ul>
<ul style="display:none">
    <li><p>asd</p></li>
</ul>

$( 'li:first' ).is( 'ul:last p' ) //why does this yield true
$( 'li:first' ).is( 'ul p:last' ) //why does this yield true

It looks like there are some bigger problems with some pseudo selectors (:first, :last, :eq(), :even, :odd and maybe others too) they don't occur always. Sometimes it needs a special formed selector to trigger the bug (see also my comments in the testcase)

Changed November 12, 2010 10:37PM UTC by hoganlong@gmail.com comment:6

It seems to me that the real problem is the word "is". If this function was called "has" all the test cases would make sense. There would be no bug.

Right now the functionality is a "has", the test is performed in the context of the result. The first item of a list is also the last item of itself.

Changed November 13, 2010 07:42AM UTC by addyosmani comment:7

cc: → rwaldron, snover, ajpiano

I wanted to close this as a ticket requiring improved documentation on the expected/actual behavior of 'is' however, I think a team review of Jitter's comments are worth taking into account. What is the general feeling on the issues presented here?

Changed November 15, 2010 05:53PM UTC by awellis13 comment:8

I still think there's a bug related to is(':first'). Isn't :first an alias of :first-child? The following does not work:

$('div', $(this)).each(function(){
   if ($(this).is(':first')) {
       $(this).addClass('current');
   } else {
       $(this).hide();
   }
});

However, this does work:

$('div', $(this)).each(function(){
   if ($(this).is(':first-child')) {
       $(this).addClass('current');
   } else {
       $(this).hide();
   }
});

Changed November 15, 2010 06:13PM UTC by ajpiano comment:9

keywords: is first last equalis first last equal needsdoc

:first is not an alias for :first-child.

Just so everyone is clear what the bug is here, it seems to be thatthat jQuery's custom selectors that are used for checking elements against their position in the jQuery object (:first, :last, :eq, etc) are failing when used in .is() against another stringy selector. These selectors are NOT part of QSA, but are obviously useful for a variety of reasons.

Part of me isn't even sure there is something that is worth fixing here, it might be more of a docs issue. While there are lots of valid uses for something that checks DOM position, like .is(":last-child"), I'm unable to think of a viable use case for testing elements against their relative position in an arbitrary selection of elements, which is what .is(":last") does.

Changed November 15, 2010 07:17PM UTC by awellis13 comment:10

Replying to [comment:9 ajpiano]:

:first is not an alias for :first-child. Just so everyone is clear what the bug is here, it seems to be thatthat jQuery's custom selectors that are used for checking elements against their position in the jQuery object (:first, :last, :eq, etc) are failing when used in .is() against another stringy selector. These selectors are NOT part of QSA, but are obviously useful for a variety of reasons. Part of me isn't even sure there is something that is worth fixing here, it might be more of a docs issue. While there are lots of valid uses for something that checks DOM position, like .is(":last-child"), I'm unable to think of a viable use case for testing elements against their relative position in an arbitrary selection of elements, which is what .is(":last") does.

I guess you're right. It is just confusing reading this in the docs for :first:

The :first pseudo-class is equivalent to :eq(0). It could also be written
as :lt(1). While this matches only a single element, :first-child can match
more than one: One for each parent.

It just makes it sound like :first could be used like :first-child for that kind of scenario. I'll admit, the jQuery documentation could use a major overhaul; especially the plugin authoring documentation.

Changed November 15, 2010 07:54PM UTC by ajpiano comment:11

There are a lot of cases where :first and :first-child will act the same, and a lot of places where they won't. .is() is clearly one of these cases.

WRT to the plugin authoring docs, if you would like to discuss that further, we'd welcome you to hop onto IRC (freenode - #jquery-dev) or start a forum thread on your ideas. The plugin authoring guide was just redone a few months ago, and seems to me to be in reasonable shape. In any case, it's outside the scope of this ticket so I'mma stop typing now :)

Changed November 19, 2010 01:18PM UTC by jitter comment:12

keywords: is first last equal needsdocneedsdocs is first last equal

Changed November 20, 2010 02:14AM UTC by dmethvin comment:13

resolution: → worksforme
status: openclosed

jitter, you were right about the context being the current set. So...

$("li:first").is("li:last")

selects the first

li
in the document and compares it to the the selector. The
is()
method is basically:

jQuery.filter( selector, this ).length > 0

Where

selector=="li:last"
and
this
is a jQuery set containing the element selected by
li:first
.

So, does a single

li
element match the selector
li:last
? Yes!

Note that this also explains the

$('li:eq(0)').is('li:eq(2)')
case being false, a single
li
element will never match the selector
li:eq(2)
.

I'm going to close this ticket but leave the needsdocs so we can review and clarify.

Changed January 10, 2011 07:56PM UTC by jmm comment:14

I ran into the same issue and found this report. If the current behavior of

is()
-- working only in the context of the current set -- is the intended behavior, then I'd say the documentation definitely needs work, because it's not at all clear that it will behave that way.

I don't know what implementation issues it would present, but it seems like

is()
would be more useful if it wasn't limited to the context of the current set and could use an arbitrary selector.

I ran into this while writing a keydown handler for elements in a modal dialog. While it's displayed I want to restrict the keyboard focus to elements in the dialog. So for example, when pressing Tab on the last focusable element in the dialog (or Shift+Tab on the first) I want to prevent focus from being given to an element outside of the dialog. I ran into this issue when I tried to accomplish that using

is()
like this:

$( this ).is( "#dialog input:first" )

(Where

this
is any INPUT element in the dialog.)

Changed May 20, 2011 06:53PM UTC by dmethvin comment:15

keywords: needsdocs is first last equal