3

Please see this image:

enter image description here

Can someone explain the difference?

Edit

Let me indicate what puzzles me. Notice that:

  1. $row.is('tr.items:last') === false
  2. $row[0].id === $('tr.items:last')[0].id

The two statements seem to contradict each other. The first tells us that $row is not the last of tr.items. But the second one tells that $row is exactly $('tr.items:last'), i.e. the last of tr.items.

No such thing occurs with the :last-of-type selector.

What is going on here?

halfer
  • 19,824
  • 17
  • 99
  • 186
mark
  • 59,016
  • 79
  • 296
  • 580

3 Answers3

5

last-of-type is a CSS pseudo-class, which represents the last sibling of the given tag name. It may work as a jQuery selector if the browser supports querySelectorAll() and you're not using jQuery-only selectors; otherwise, it'll use Sizzle, which doesn't support it. See this jQuery ticket.

On the other hand, :last is a jQuery selector, which selects the last matched element.

pimvdb
  • 151,816
  • 78
  • 307
  • 352
João Silva
  • 89,303
  • 29
  • 152
  • 158
  • Probably worth noting that `:last-of-type` is not included in jQuery itself. When `querySelectorAll` fails (e.g. in older browsers) then a selector with that pseudoclass will fail. – pimvdb Sep 01 '12 at 14:41
  • More information on which CSS selectors aren't implemented in jQuery: http://stackoverflow.com/questions/11745274/what-css3-selectors-does-jquery-really-support-e-g-nth-last-child – BoltClock Sep 01 '12 at 16:05
  • 1
    @mark: Can you provide the exact markup for your table? With this markup, http://jsfiddle.net/6mues/, everything works as expected. – João Silva Sep 01 '12 at 16:12
1

jQuery does a different filter mechanism for .is when it comes to set filters like :last. The point is that it normally uses .filter on the current set and checks whether there are any elements left after filtering.

This works for cases such as:

$("<a></a><b></b>").is("b");  // true, there is a <b> after filtering

But for :last this fails, because such a filter is relative to the set. Consider a document with two elements:

$("a:first").is("a:last");  // would be true if the same method was used,
                            // because in the set with the first <a> element,
                            // the last <a> element is that element. So filtering
                            // with `a:last` yields something, and `.is` gets you
                            // true.

This is in contrast with what you may expect. So, jQuery instead searches for a:last in the current context and checks whether a:first is apparent in that set.

The problem in your case is that $(ev.target) (in handleKeyDown) makes the context to be that input element and not the document (which is the usual case). No tr.items can be found in that context and you get false. This is arguably a bug in jQuery.

Anyway, what you can do is checking against a set instead. It is faster to use the corresponding functions, anyway:

$row.is( $("tr.items").last() );  // true
pimvdb
  • 151,816
  • 78
  • 307
  • 352
  • I still do not get it, but suppose everything is like you say it is. `$("tr.items").last()` is by far not the same as `$("tr.items:last")`. If I have a 100 rows, then the first expression obtains the 100 rows and returns the last, whereas the second one is expected to pinpoint just the last row. For me, it is like doing `SELECT` in sql and then filter the list in code vs doing `SELECT WHERE` in the first place. I mean, no one doubts that the second form is the right one. So why are the relative selectors deprecated? CSS now has `:last-of-type`, which is exactly what I need. – mark Sep 02 '12 at 12:56
  • @mark: My apologies, I was stretching it a bit. It is not deprecated but using those jQuery-only selectors disallows the (fast) CSS selector of the browser. But `:last`/`.last()` are the same - `:last` is applied after selecting the `tr.items`, which is what calling `.last()` does as well. – pimvdb Sep 02 '12 at 13:00
  • Let me summarize for me: **1.** `:last` is buggy. **2.** use either `.last()` or `:last-of-type`, where the latter is unavailable in old browsers (how old?), but may be really faster than `.last()` if supported. Then comes the question - isn't jQuery supposed to spare us exactly these details? Shouldn't it implement `:last` as `:last-of-type` where supported and as `.last()` where is not? – mark Sep 02 '12 at 13:32
  • @mark: `:last` and `:last-of-type` aren't the same thing. `:last` really just selects the last element of the matched elements, e.g. `tr:last` gives you the last `tr` element on the page. `tr:last-of-type` selects all `tr` that are the last of their siblings. If you have two tables, `tr:last-of-type` gives you two `` elements. `tr:last` will only ever give you one element. – pimvdb Sep 02 '12 at 13:42
  • Oh, I see. But it does not matter in my particular case. Given that important distinction, is my summarization correct? – mark Sep 02 '12 at 14:18
  • @mark: If you disregard the difference then you are correct. But your initial question is all about the difference in the first place :) – pimvdb Sep 02 '12 at 14:21
  • Well, in my particular case there should have been no difference. The right answer is that `:last` is buggy and you sounded that answer. – mark Sep 02 '12 at 14:24
0

:last-of-type selects the last sibling with a specified tag name. Note that :last-of-type is not an "official" jQuery selector and only works in some browsers - specifically, those that support document.querySelectorAll() (newer versions of everything except IE).

Abraham
  • 20,316
  • 7
  • 33
  • 39