17

I have a table that kind of expands and collapses, but it's getting too messy to use it and IE and Firefox are not working properly with it.

So, here's the JavaScript code:

  function toggle_it(itemID){ 
      // Toggle visibility between none and '' 
      if ((document.getElementById(itemID).style.display == 'none')) { 
            document.getElementById(itemID).style.display = '' 
            event.preventDefault()
      } else { 
            document.getElementById(itemID).style.display = 'none'; 
            event.preventDefault()
      }    
  } 

And a Sample HTML:

<table>
    <tr>
        <td>Product</td>
        <td>Price</td>
        <td>Destination</td>
        <td>Updated on</td>
    </tr>
    <tr>
        <td>Oranges</td>
        <td>100</td>
        <td><a href="#" id="toggle" onClick="toggle_it('tr1');toggle_it('tr2')">+ On Store</a></td>
        <td>22/10</td>
    </tr>
    <tr id="tr1" style="display:none">
        <td></td>
        <td>120</td>
        <td>City 1</td>
        <td>22/10</td>
    </tr>
    <tr id="tr2" style="display:none">
        <td></td>
        <td>140</td>
        <td>City 2</td>
        <td>22/10</td>
    </tr>
    <tr>
        <td>Apples</td>
        <td>100</td>
        <td><a href="#" id="toggle" onClick="toggle_it('tr3');toggle_it('tr4')">+ On Store</a></td>
        <td>22/10</td>
    </tr>
    <tr id="tr3" style="display:none">
        <td></td>
        <td>120</td>
        <td>City 1</td>
        <td>22/10</td>
    </tr>
    <tr id="tr4" style="display:none">
        <td></td>
        <td>140</td>
        <td>City 2</td>
        <td>22/10</td>
    </tr>
</table>

The problem is that I use one ID for each and every and that's very annoying because I want to have a lot of hidden rows for each parent and a lot of parents, so it would be too many IDs to handle. And IE and FireFox are only showing the first Hidden Row and not the others. I suspect this happens because I've made it work by triggering all IDs together. I think it would be better if I use Classes instead of IDs to indetify the hidden rows.

I'm really new to all of this so please try and explaining it in any kind of simply way. Also I've tried jQuery but wasn't able to get it.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
user2957683
  • 173
  • 1
  • 1
  • 4

6 Answers6

21

It's difficult to figure out what you're trying to do with this sample but you're actually on the right track thinking about using classes. I've created a JSFiddle to help demonstrate a slightly better way (I hope) of doing this.

Here's the fiddle: link.

What you do is, instead of working with IDs, you work with classes. In your code sample, there are Oranges and Apples. I treat them as product categories (as I don't really know what your purpose is), with their own ids. So, I mark the product <tr>s with class="cat1" or class="cat2".

I also mark the links with a simple .toggler class. It's not good practice to have onclick attributes on elements themselves. You should 'bind' the events on page load using JavaScript. I do this using jQuery.

$(".toggler").click(function(e){
    // you handle the event here
});

With this format, you are binding an event handler to the click event of links with class toggler. In my code, I add a data-prod-cat attribute to the toggler links to specify which product rows they should control. (The reason for my using a data-* attribute is explained here. You can Google 'html5 data attributes' for more information.)

In the event handler, I do this:

$('.cat'+$(this).attr('data-prod-cat')).toggle();

With this code, I'm actually trying to create a selector like $('.cat1') so I can select rows for a specific product category, and change their visibility. I use $(this).attr('data-prod-cat') this to access the data-prod-cat attribute of the link the user clicks. I use the jQuery toggle function, so that I don't have to write logic like if visible, then hide element, else make it visible like you do in your JS code. jQuery deals with that. The toggle function does what it says and toggles the visibility of the specified element(s).

I hope this was explanatory enough.

Taylan Aydinli
  • 4,333
  • 15
  • 39
  • 33
  • Dude, thank you so much! That was EXACTLY what I was looking for! It worked perfectly, even in the other browsers. I know this is really simple but I don't know anything of JavaScript, just some html. The reason I "made up" this example table is because this code is for my companies website (which I'm making) and there was no need to use the actual table (with lots of css and stuff) since the doubt was only about that mechanism. Thanks again! – user2957683 Nov 06 '13 at 11:41
  • Can I use the Slide effect instead of it just popping up? how? – user2957683 Nov 07 '13 at 10:04
  • This was helpful to me but I was using it off a checkbox, not linked text. For that to work, I had to remove the line "e.preventDefault();". Otherwise, the checkbox wouldn't check. Hope this helps someone! – nbardach May 06 '20 at 20:44
8

Well one way to do it would be to just put a class on the "parent" rows and remove all the ids and inline onclick attributes:

<table id="products">
    <thead>
    <tr>
        <th>Product</th>
        <th>Price</th>
        <th>Destination</th>
        <th>Updated on</th>
    </tr>
    </thead>
    <tbody>
    <tr class="parent">
        <td>Oranges</td>
        <td>100</td>
        <td><a href="#">+ On Store</a></td>
        <td>22/10</td>
    </tr>
    <tr>
        <td></td>
        <td>120</td>
        <td>City 1</td>
        <td>22/10</td>
    </tr>
    <tr>
        <td></td>
        <td>140</td>
        <td>City 2</td>
        <td>22/10</td>
    </tr>
    ...etc.
    </tbody>
</table>

And then have some CSS that hides all non-parents:

tbody tr {
    display : none;          // default is hidden
}
tr.parent {
    display : table-row;     // parents are shown
}
tr.open {
    display : table-row;     // class to be given to "open" child rows
}

That greatly simplifies your html. Note that I've added <thead> and <tbody> to your markup to make it easy to hide data rows and ignore heading rows.

With jQuery you can then simply do this:

// when an anchor in the table is clicked
$("#products").on("click","a",function(e) {
    // prevent default behaviour
    e.preventDefault();
    // find all the following TR elements up to the next "parent"
    // and toggle their "open" class
    $(this).closest("tr").nextUntil(".parent").toggleClass("open");
});

Demo: http://jsfiddle.net/CBLWS/1/

Or, to implement something like that in plain JavaScript, perhaps something like the following:

document.getElementById("products").addEventListener("click", function(e) {
    // if clicked item is an anchor
    if (e.target.tagName === "A") {
        e.preventDefault();
        // get reference to anchor's parent TR
        var row = e.target.parentNode.parentNode;
        // loop through all of the following TRs until the next parent is found
        while ((row = nextTr(row)) && !/\bparent\b/.test(row.className))
            toggle_it(row);
    }
});

function nextTr(row) {
    // find next sibling that is an element (skip text nodes, etc.)
    while ((row = row.nextSibling) && row.nodeType != 1);
    return row;
}

function toggle_it(item){ 
     if (/\bopen\b/.test(item.className))       // if item already has the class
         item.className = item.className.replace(/\bopen\b/," "); // remove it
     else                                       // otherwise
         item.className += " open";             // add it
}

Demo: http://jsfiddle.net/CBLWS/

Either way, put the JavaScript in a <script> element that is at the end of the body, so that it runs after the table has been parsed.

nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • 1
    Thank you for the help, man! I tried vape's idea first and it worked out great but thanks for the help anyway! – user2957683 Nov 06 '13 at 11:43
7

JQuery 10.1.2 has a nice show and hide functions that encapsulate the behavior you are talking about. This would save you having to write a new function or keep track of css classes.

$("tr1").show();

$("tr1").hide();

w3cSchool link to JQuery show and hide

spartikus
  • 2,852
  • 4
  • 33
  • 38
2
event.preventDefault()

Doesn't work in all browsers. Instead you could return false in OnClick event.

onClick="toggle_it('tr1');toggle_it('tr2'); return false;">

Not sure if this is the best way, but I tested in IE, FF and Chrome and its working fine.

Poornima
  • 918
  • 5
  • 11
  • Returning false instead of using event.preventDefault() might be better but using `onclick` attributes is definitely not better practice. (Also, I'm not sure if event.preventDefault() doesn't work in all browsers. It should. jQuery works on pretty much all major browsers. If you must support older versions of IE [like 7, 8, 9] you can use 1.9.+ with jQuery migrate) – Taylan Aydinli Nov 05 '13 at 20:28
  • @vape - `preventDefault()` definitely doesn't work on older IE if using plain JS, but jQuery (and other libraries) implement it for all browsers when you use jQuery event binding. You don't need jQuery migrate to support old IE if you're using v1.x. – nnnnnn Nov 05 '13 at 20:30
  • @nnnnn I know preventDefault doesn't work on older browsers if using plain JS. But if I'm not mistaken, jQuery's preventDefault augments the native JS preventDefault method and makes it compatible with older browsers. Also, as far as I know, you do need jQuery migrate to support old IE versions if you are using jQuery 1.9.x as explained on the jQuery 1.9 upgrade guide [http://jquery.com/upgrade-guide/1.9/]. But I might be wrong some or all of these, of course :) – Taylan Aydinli Nov 05 '13 at 20:36
  • Yes, jQuery augments the native `event` object in several ways to make it consistent cross-browser, including providing `preventDefault()` for all browsers. But you definitely don't need the migrate plugin. The 1.x stream supports old IE. The 2.x stream doesn't. (Where you _might_ need the migrate plugin is if you were using an old version of jQuery and upgrade to 1.9+, because several previously deprecated features were removed in that version and the migrate plugin sort of puts them back.) – nnnnnn Nov 05 '13 at 21:04
  • Thanks for the info. I thought 1.9.x also broke compatibility with old IE versions and needed the migrate plug-in. Looks like I was mistaken :) – Taylan Aydinli Nov 06 '13 at 06:23
  • Thank you guys, I did what vape said and it worked perfectly. Thanks for the help. I was amazed at Stack Overflow, so many answers so quickly! – user2957683 Nov 06 '13 at 11:42
2

Below is my Script which show/hide table row with id "agencyrow".

<script type="text/javascript">

                        function showhiderow() {
                            if (document.getElementById("<%=RadioButton1.ClientID %>").checked == true) {

                                document.getElementById("agencyrow").style.display = '';
                            } else {

                                document.getElementById("agencyrow").style.display = 'none';
                            }


                        }
    </script> 

Just call function showhiderow()upon radiobutton onClick event

Vijay Kumbhoje
  • 1,401
  • 2
  • 25
  • 44
0

AngularJS directives ng-show, ng-hide allows to display and hide a row:

   <tr ng-show="rw.isExpanded">
   </tr>

A row will be visible when rw.isExpanded == true and hidden when rw.isExpanded == false. ng-hide performs the same task but requires inverse condition.

user1920925
  • 672
  • 6
  • 5