Thierry Savard Saucier
Thierry Savard Saucier

Reputation: 449

using nextUntil function to toggle specific table row

I have this table which contains Events, and on a click on its icon ( an arrow ) I create new rows (called child event ) which are then add under the current row. I want, on a click on the same icon to hide its child event, but only his. Right now, f I click on a new event ( a different row) , it will toggle every hidden row, even if they are not relevent to the current row ...

simply I'm trying to force get some kind of UntilNext but I cant get that function to work, and with the simple loop I'm doing atm it skips the oneEvent but triggers EVERY child row and not the one I want.

$('.JSONArrow').on("click", function () {

    var image = $(this);
    var Row = image.closest("tr");

    Row.toggleClass("Opened");

    if (Row.hasClass("Opened")) {

        image.attr("src", "/Images/downArrow.png")

        if (!(Row.hasClass("alreadyOpen"))) {
            var id = image.attr("id");

            $.get("/Home/Child/" + id, function (data) {
                Row.after(data);
                Row.addClass("alreadyOpen");
            });
        }
    } else {
        image.attr("src", "/Images/rightArrow.png");
    }

    var tr = Row.nextAll('tr');

    for (i = 0; i < tr.length; i++) {
        var eventClass = $(tr[i]).attr('class');
        if (eventClass == 'ChildEvent')
            $(tr[i]).slideToggle()
        else {
            if (eventClass == 'oneEvent')
                return;
        }
    }
});

must be something simple to make nextUntil to work only on child row but I havent manage it so far, according to jquery doc it should be simple like :

   var tr = Row.nextUntil('tr',$(".oneEvent"));

    for (i = 0; i < tr.length; i++) {
        var eventClass = $(tr[i]).attr('class');
        if (eventClass == 'ChildEvent'){
            $(tr[i]).slideToggle()
        }
    }

but I cant make it work. Anyone could point out how to do it, or use another jquery function I dont know about that would select all('tr") that are linked(childEvent) to my Row variable and not the others.

I'm not sure poeple will undestand more with the hthml if you dont know asp or mvc4, but here goes : EDIT ::

<div class="TableHolder">
    <table id="mainTable">
        <thead> 
            <tr class="header">

                <th class="iconColumn">Child</th>

                <th>Date </th>
                .... other <th>
                </th>
        </tr>
    </thead>

            @Html.EditorFor(x => x.logs)

 </table>

and thats the table row per say :

     string dynamicClass = "";
if (Model.Parent == null )
{
    dynamicClass = "oneEvent";
}
else 
{  
    dynamicClass = "ChildEvent";
}

}

  <tr class="@dynamicClass">

      <td class="iconColumn">
        @if (Model.Parent == null )
        {
            <img class="JSONArrow" id="@Model.EventID" src="~/Images/rightArrow.png"/>
        }
    </td>

    <td>@Model.TimeUTC.ToString("dddd, dd MMMM yyyy")<br />@Model.TimeUTC.ToString("HH:mm:ss") </td>
    .... other column

</tr>

I'll try to simplify what I ment :

you have rows.(Event) when you click on a row(Event), new dynamic rows appears under it (called ChildEvent).

if you click a second time on a row(event), you hide the rows if they exists under him, but only if they are ChildEvent class.

my current code hides ALL the ChildEvent on the table, I want to hide only those under the (event) clicked.

if the user clicks back on a row(event), it will show the (ChildEvent) if they exists, but only the (childEvent) relevant to the row(event) clicked, and not the ChildEvent of the whole table.

childEvent can only be added under its relevant Event.

re-edit :

     row.nextUntil(".oneEvent", ".ChildEvent").slidetoggle();

this is what i want, textually : toggle the tr who are under the one I clicked, if they have the ChildEvent class. test until you reach a tr who have the class "oneEvent".

I just cant undestand what parameters go where.

rererereedit :

<table>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
</table>

ON CLICK :

 <table>
  <tr class="oneEvent, Opened, alreadyOpened"></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
</table>

click on the same oneEvent

 <table>
  <tr class="oneEvent, alreadyOpened"></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
</table>

click on a new oneEvent :

<table>
  <tr class="oneEvent, alreadyOpened"></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
  <tr class="oneEvent, Opened, alreadyOpened"></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>
</table>

click on another new oneEvent

<table>
  <tr class="oneEvent, alreadyOpened"></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
  <tr class=oneEvent></tr>
  <tr class="oneEvent, Opened, alreadyOpened"></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
  <tr class="oneEvent, Opened, alreadyOpened"></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>

click back on an earlier one Event :

<table>
  <tr class="oneEvent, alreadyOpened"></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
  <tr class=oneEvent></tr>
  <tr class="oneEvent, Opened, alreadyOpened"></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
  <tr class="oneEvent, alreadyOpened"></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>

click on the 1st OneEvent :

<table>
  <tr class="oneEvent, Opened, alreadyOpened"></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
  <tr class=oneEvent></tr>
  <tr class="oneEvent, Opened, alreadyOpened"></tr>
   <tr class=ChildEvent display = true></tr>
   <tr class=ChildEvent display = true></tr>
  <tr class="oneEvent, alreadyOpened"></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
   <tr class=ChildEvent display = false></tr>
  <tr class=oneEvent></tr>
  <tr class=oneEvent></tr>

Upvotes: 0

Views: 2290

Answers (2)

jfriend00
jfriend00

Reputation: 707706

Yeah. We have enough HTML to see what you're asking for now! Here's what I would suggest:

$('.JSONArrow').on("click", function () {
    var row = $(this).closest("tr");
    if (row.hasClass("Opened")) {
        // click on an already opened row
        this.src = "/Images/rightArrow.png";
        row.removeClass("Opened");
        // close all following .ChildEvent rows until you get to the next .oneEvent
        row.nextUntil(".oneEvent", ".ChildEvent").slideUp();
    } else {
        // click on a closed row
        this.src = "/Images/downArrow.png";
        row.addClass("Opened");
        // if not previously opened, then add the child HTML
        if (!row.hasClass("alreadyOpened")) {
            $.get("/Home/Child/" + this.id, function(data) {
                row.after(data);
                row.addClass("alreadyOpened");
            });
        } else {
            // show all following .ChildEvent rows until you get to the next .oneEvent
            row.nextUntil(".oneEvent", ".ChildEvent").slideDown();
        }
    }
});

You also have to fix your HTML. You separate class names with a space, not with a comma when you put multiple class names in your HTML. See here for a working demo:

http://jsfiddle.net/jfriend00/ZkvgN/


Since you seem to be having performance problems, here's a special purpose version of nextUntil that is 3x faster as measured in this jsperf: http://jsperf.com/nextuntilfast

jQuery.fn.nextUntilFast = function(untilClass) {
    // for speed reasons, this operates only on the 
    // first DOM object in the jQuery object
    var result = [], cur;
    if (this.length) {
        cur = this[0];
        while (cur = cur.nextSibling) {
            // check elements only
            if (cur.nodeType === 1) {
                if ($(cur).hasClass(untilClass)) {
                    break;
                } else {
                    result.push(cur);
                }
            }
        }
    }
    return this.pushStack(result, "nextUntilFast", untilClass);
};

You would use it by replacing this:

 row.nextUntil(".oneEvent", ".ChildEvent").slideDown();

with this:

 row.nextUntilFast("oneEvent").slideDown();

OK, here's my third guess at what you want to hide all .ChildEvent objects in the current row. Since the HTML you included has no .ChildEvent objects in it, this is again a guess:

Row.find(".ChildEvent").slideToggle();

Since you haven't included your HTML, it's hard to know exactly how to solve this problem.

After reading your question about 20 times (since you aren't around to answer the clarifying questions), here's my best guess at what you're asking. If what you want to do with the last block of code in your function is to slideUp any rows that are after where the click was up to and not including a row with .oneEvent, then you can do that with this code:

 Row.nextUntil(".oneEvent", ".ChildEvent").slideUp();

The .oneEvent indicates where to stop matching sibling elements. The .ChildEvent is a filter that removes items from the result that don't match that selector. This will give you all following rows up to (but not including) the row that is .oneEvent that are also .ChildEvent and it will then slide them up. If they are already closed, it will leave them closed. If you really want a toggle, then you can use .slideToggle() like your original code did, but I was guessing that what you're really after is collapsing of other rows.

This would be a revised version of your code:

$('.JSONArrow').on("click", function () {
    var row = $(this).closest("tr");
    if (row.hasClass("Opened")) {
        this.src = "/Images/rightArrow.png";
        row.removeClass("Opened");
    } else {
        this.src = "/Images/downArrow.png";
        row.addClass("Opened");
        // if not previously opened, then add the child HTML
        if (!row.hasClass("alreadyOpen")) {
            $.get("/Home/Child/" + this.id, function(date) {
                row.after(data);
                row.addClass("alreadyOpen");
            });
        }
    }

    // now close all the following rows up to .oneEvent
    row.nextUntil(".oneEvent", ".ChildEvent").slideUp();
});

This was my original guess at what you needed help with...

The typical way that a click action is applied to the same region that the click occurred and not all regions in the document is with the following algorithm:

$(this).closest("parent container selector").find("target selector").action()

You use .closest() to find the proper parent container. You use .find() to find objects matching a selector that are in this container.

If you want to apply something to all items in the container, then you select a higher container selector (like perhaps the whole table). If you want to apply something to just this region, then you pick a container selector for the .closest() call that goes up only to your immediate region. The .find() then identifies the targets in whatever region you have selected. In this way, you can target only child objects of whatever scope you want.

For example, to apply an action to all items that match a selector in the same row as the click occurs, you would do this:

$(this).closest("tr").find("target selector").action()

For more specific code, please add your HTML to your question. As you have it know, you're asking us to try to figure out what your HTML must be by looking at your code. That's not easy and sometimes not possible and certainly never leads to the best possible solution.

Upvotes: 1

Thierry Savard Saucier
Thierry Savard Saucier

Reputation: 449

Will be easier I guess to keep on working from this instead of my main question who's getting quite clustered ...

$('.JSONArrow').on("click", function () {

    var image = $(this);
    var Row = image.closest("tr");

    Row.toggleClass("Opened");

    if (Row.hasClass("Opened")) {

        image.attr("src", "/Images/downArrow.png")

        if (!(Row.hasClass("alreadyOpen"))) {
            var id = image.attr("id");

            $.get("/Home/Child/" + id, function (data) {
                Row.after(data);
                Row.addClass("alreadyOpen");
            });
        }
    } else {
        image.attr("src", "/Images/rightArrow.png");
    }

    Row.nextUntil($(".oneEvent"), $(".ChildEvent")).slideToggle();
});

I got it to work like that. but I get a script error which crash my browser when I open a oneEvent tr who have alot of childEvent. the chilldEvent will appear, but after that if I try to click on anything else, I get an error box saying the script have crashed.

Upvotes: 0

Related Questions