Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am trying to write a jQuery selector to select a range of table cells (minRow, minCol) to (maxRow, maxCol).

I have a selector that works for a horizontal range (col 1, row 2) to (col 3, row 2)
$('tr:lt(3):gt(1) td:lt(4):gt(0))')

But this fails for the corresponding vertical range (col 2, row 1) to (col 2, row 3)
$('tr:lt(4):gt(0) td:lt(3):gt(1)')
because the td selector won't loop over the row, it will just take the first one

This version using map and each works
$('tr:lt(4):gt(0)').map( function() { return $(this).find('td:lt(3):gt(1)') } ).each( function() { /* this.do_something */ } );
but is even uglier

Is there an elegant way to achieve this?

Bonus points for finding a more elegant range selector than :lt():gt()

Examples of the above horizontal and vertical ranges and a block range are at this fiddle http://jsfiddle.net/jghaines/qeLhgw4f/4/

The selector tr:lt(3):gt(1) td:lt(4):gt(0) only works because a single row is being selected. What's happening is tr:lt(3):gt(1) will return a set of td elements in row 3, and td:lt(4):gt(0) will select the second, third, and fourth td elements by their index in that specific set .

When you use a selector like tr:lt(4):gt(0) td:lt(3):gt(1) , multiple rows are selected and the selectors are compounded relative to the set that is returned (rather than each individual td element). The selector tr:lt(4):gt(0) returns a set consisting of the second, third, and fourth rows. Based on this returned set, the selector td:lt(3):gt(1) will select the third td element by its index in that specific set .

In other words, the :gt() / :lt() selectors will select all elements at an index greater than or less than an index within the matched set . Your selectors weren't working as expected because the elements were being selected based on the matched set (rather than each individual td element).

The solution would be to chain a .find() method between tr:lt(4):gt(0) / td:lt(3):gt(1) .

In other words, replace:

$('table#2 tr:lt(4):gt(0) td:lt(3):gt(1)').addClass('red');

With:

$('table#2 tr:lt(4):gt(0)').find('td:lt(3):gt(1)').addClass('red');

Updated Example

Since you wanted an alternative to combining :lt():gt(), it's worth pointing out that you can use the .slice() method:

For instance, you could replace:

$('table#2 tr:lt(4):gt(0) td:lt(3):gt(1)').addClass('red');

With:

$('table#2 tr').slice(1, 4).find('td:lt(3):gt(1)').addClass('red');
                Terrific, thanks. I note that the slice will only work on the first tr selector. This does not work: $('table#4 tr').slice(1,4).find('td').slice(1,4).addClass('yellow'); 
– Jason
                Jan 13, 2016 at 4:03
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.