Efficiently find the next visible table row with jQuery
Asked Answered
E

5

6

In a table with some rows hidden, I want to get the next visible row, if one exists. This will do the job:

row = $(selectedRow).nextAll(':visible');
if ($(row).length > 0)
    selectedRow = row;

but it is very slow when many rows follow the selected row. A scripted approach is:

var row = $(selectedRow).next();
while ($(row).length > 0 && !$(row).is(':visible'))
    row = $(row).next();
if ($(row).length > 0)
    selectedRow = row;

This is much faster, but there's got to be an elegant all-jQuery approach I can use.

Etherege answered 13/12, 2011 at 19:44 Comment(0)
E
7

Based on the helpful suggestion from mblase75, here is the most elegant solution I've found:

var row = $(selectedRow).next(':visible');

if ($(row).length == 0)
    row = $(selectedRow).nextUntil(':visible').last().next();

if ($(row).length > 0)
    selectedRow = row;

Often (in my case), the table isn't filtered, so the next row is visible much of the time. When it is not, nextUntil() yields a non-empty set of non-visible rows. Selecting the last row in that set and then the next row following it gives the next visible row in the table, if there is one.

Etherege answered 14/12, 2011 at 19:8 Comment(0)
S
1

Just ran into exactly the same situation. Based on Marshall Morrise's answer, if you want a one-liner, you can try...

selectedRow = $(selectedRow).nextUntil(':visible').add(selectedRow).last().next();

The new bit here is the .add(selectedRow) which prevents us from trying to find the next() of an empty jQuery element. Only remaining problem is the last if in Marshall's post - unfortunately an empty jQuery element is still truthy.

Saville answered 1/11, 2012 at 6:11 Comment(0)
D
0

Why are you using .nextAll if you just want one row?

I think that if you replace

row = $(selectedRow).nextAll(':visible');

with

row = $(selectedRow).nextUntil(':visible').next();

you'll get the speed improvement you're looking for.

Douse answered 13/12, 2011 at 19:55 Comment(2)
Thanks for the reply. The problem is $(selectedRow).next(':visible') first applies .next() and then gives me that next row if it is visible. If it is not visible, I get nothing.Etherege
Thanks for taking the time to answer. Didn't know about nextUntil(). I tried what you suggest, but it didn't work for me because nextUntil() returns an empty set if there are no non-visible rows between the selected row and the next visible row, and the .next seems to be applied to that empty set. But your guidance brought me to something better than what I had, which I'll post as an answer to my own question.Etherege
W
0

There are two problems with one -liners mentioned in other answers:

  1. They miss the case when there are no invisible rows. In that case, nextUntil does not return any elements. Below is the code that fixes this issue.
  2. If you are using specific class name, instead of jQuery's default show/hide then also it doesn't seem to work reliably.

Below code fixes both of above issues with other answers:

//invisibleRowClassName parameter is optional
function nextVisibleSibling(element, invisibleRowClassName) {
    var selector = invisibleRowClassName ? (":not(." + invisibleRowClassName + ")") : ".visible";
    var invisibleElements = element.nextUntil(selector);
    if (invisibleElements.length === 0) {
        return element.next();
    }
    else {
        return invisibleElements.last().next();
    }
}

And here's the code to get previous visible element as well.

//invisibleRowClassName parameter is optional
function prevVisibleSibling(element, invisibleRowClassName) {
    var selector = invisibleRowClassName ? (":not(." + invisibleRowClassName + ")") : ".visible";
    var invisibleElements = element.prevUntil(selector);
    if (invisibleElements.length === 0) {
        return element.prev();
    }
    else {
        return invisibleElements.last().prev();
    }
}
Wynn answered 12/1, 2014 at 4:21 Comment(0)
A
0

One short variant of this is to write:

$(element).nextAll().filter(":visible:first")

This will return an empty match if there are no visible elements, and a specific element otherwise.

I use it like this (in coffeescript):

$(input_element).on "keydown", (e) ->
    if e.which == 40  # Down key
        new_selected = $(selected_element).nextAll().filter(":visible:first")
        if new_selected.length
            $(selected_element).removeClass("selected")
            new_selected.addClass("selected")

    if e.which == 38  # Up key
        new_selected = $(selected_element).prevAll().filter(":visible:first")
        if new_selected.length
            $(selected_element).removeClass("selected")
            new_selected.addClass("selected")
Archipelago answered 4/3, 2015 at 8:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.