Show/Hide Table Rows using Javascript classes - javascript

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>+ On Store</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>+ On Store</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.

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.

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>+ On Store</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.

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

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.

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

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.

Related

How to create an html table with data that can change using JS

I'm trying to create an online calculator, using a table, with a tag that will have an output that changes. The output is the result of whatever math function is used on the calculator. So if someone puts in 5*5, the th should show 25. I tried using a variable, output, in javascript, and then using document.getElementsByTagName("th").innerHTML(output), to have it changing, but that didn't work. Then I tried the same command without a variable, and instead just directly inserting a string in the innerHTML and it still wasn't working. I also tried write(), but that didn't work either. Any ideas on what I can try?
This is my table (the id's and classes are just some style attributes in my css file):
<table class="center" style = "width:20%">
<tr>
<th colspan="4" id = "final">0</th>
</tr>
<tr id = "opRow">
<td>+</td>
<td>-</td>
<td>x</td>
<td>/</td>
</tr>
<tr class = "dataRow">
<td>7</td>
<td>8</td>
<td>9</td>
<td rowspan = "4" id = "eqBut">=</td>
</tr>
<tr class = "dataRow">
<td>4</td>
<td>5</td>
<td>6</td>
</tr>
<tr class = "dataRow">
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr class = "dataRow">
<td style = "width:26%" id = "acBut">ac</td>
<td>0</td>
<td>.</td>
</tr>
</table>
getElementsByTagName() returns an array and so you'll have to reference it like this:
document.getElementsByTagName("th")[0]
assuming that you only have 1 th tag on your current page.
plus innerHTML is a property of an element and not a method, so you'll have to rewrite:
document.getElementsByTagName("th")[0].innerHTML = output
Also, your current implementation should be throwing some errors in your browser console log please do check it out.
Method / function
getElementsByTagName
is returning an array with all available tags from your html, then you've only 1 th in your table so is result the th tag from your table is first [0], now use method mention to select th tag and select first element [0]
innerHTML - used to rewrite data as I know
and use it to rewrite cell.
So use that :
document.getElementsByTagName("th")[0].innerHTML = 52;
To check if is working try this :
document.addEventListener("DOMContentLoaded", function(){
document.getElementsByTagName("th")[0].innerHTML = 52;
});
Well, I hope that you understand what I want to say. Thanks.

Cypress iterate table rows get specific element in cells

I'm trying to iterate through table rows and get each row which includes a specific value,
but it doesn't work for me.
I'm using .each() to iterate the rows and .within() on each $el,
inside that, I use cy.get('td').eq(1).contains('hello') but I the get assertion error:
Timed out retrying: Expected to find content: 'hello' within the element: <td> but never did.
when I console.log cy.get('td').eq(1) it yields the desired cell in each row and the test passes, so I don't understand why chaining .contains() doesn't work...
it('get element in table', () => {
cy.visit('http://localhost:3000/');
cy.get('tbody tr').each(($el) => {
cy.wrap($el).within(() => {
cy.get('td').eq(1).contains('hello') // contains() doesn't work
})
})
});
<table>
<thead>
<tr>
<th>Month</th>
<th>Savings</th>
</tr>
</thead>
<tbody>
<tr>
<td>January</td>
<td>$100</td>
</tr>
<tr>
<td>February</td>
<td>hello</td>
<td>$80</td>
</tr>
<tr>
<td>$10</td>
<td>hello</td>
</tr>
</tbody>
</table>
should('have.text', text) should work
cy.get('td').eq(1).should('have.text', 'hello')
If there's whitespace around text, use contain.text
cy.get('td').eq(1).should('contain.text', 'hello')
The simple answer is: don't :)
To be more specific use html attribute selection instead. The convention is to have an attribute named data-cy. Furthermore, I discovered it convenient to have a data-cy-identifier for when selecting specific rows. Since I'm not sure what you're trying with your code, I'll use a similar example that can hopefully get you going:
<table data-cy="expences">
<tr>
<td data-cy="month">January</td>
<td data-cy="price">$100</td>
</tr>
<tr data-cy="discounted">
<td data-cy="month">Feburary</td>
<td data-cy="price">$80</td>
</tr>
<tr data-cy="discounted">
<td data-cy="month">March</td>
<td data-cy="price">$10</td>
</tr>
</table>
You can of course do all sorts of combinations of this, but now you can do more specific and useful selections, such as:
cy.get('[data-cy="expenses"]').find('[data-cy="discounted"]').find('[data-cy="price"]').should(...)
And similar. This is flexible, because it reflects the structure of your data, and not the presentation, so you can change this to a list or whatever later. It avoids selecting of volatile things, so it's also more robust. It also uses a hierarchy rather than overly specific selectors.
The idea of adding things like data-cy-identifier allows you to do selections by ID (you can propagate it using javascript, angular, vue or whatever you use) and then checking things like the contents of a row with logically related items.
Hope it can get you going. Also I can recommend reading: https://docs.cypress.io/guides/references/best-practices.html

Focus on first form element, whatever it may be. jQuery dialog

I have a jQuery modal dialog, when it opens I want it to focus on the first form element.
At the moment I have this:
the_dialog.dialog({
modal: true,
width: 700,
title: 'title',
close: suicide ,
open: function(event, ui) {
setTimeout(function() {
jQuery('#').focus(); <-- VERY SPECIFIC CSS SELECTOR PERHAPS?
}, 220);
}
}
);
My problem is that the this dialog is called from a few different places in my application and the first form element can sometimes be an input or sometimes be a select.
The layout of the form is always the same, only the first form element is subject to change.
<table>
<tbody>
<tr>
<td>LABEL</td>
<td>FIRST FORM ELEMENT</td>
</tr>
<tr>
<td>LABEL</td>
<td></td>
</tr>
<tr>
<td>LABEL</td>
<td></td>
</tr>
<tr>
<td>LABEL</td>
<td></td>
</tr>
</tbody>
</table>
Without adding any CLASSes or IDs, when the form opens, can I focus on the FIRST FORM ELEMENT, whatever it may be?
Can use the psuedo :input selector within your open callback and look for the first non hidden element
open: function(event, ui) {
the_dialog.find(':input:not(:hidden):first').focus()
}
:input filters the tags <input>, <textarea> and <select>
:hidden filters any tags that are not visible from display:none as well as type="hidden"
You can try
jQuery('input,select').first().focus();
with respect to the comments, you should scope it so that it applies to dialog only, e.g.
http://jsfiddle.net/chkfgfwy/
You can use the :input selector to find the first form element within the dialog:
the_dialog.find(':input:first').focus()
I'd suggest:
// I'm assuming that <textarea> elements may be used, they can be removed if not;
// 'dialog' is an assumed reference to a jQuery object containing the relevant <table>:
dialog.find('input, select, textarea')
// retrieving the first element matched by the selector:
.eq(0)
// focusing that found element:
.focus();
// this part is entirely irrelevant, and used only to vary the "first form element",
// in order to demonstrate the approach working, regardless of which element is 'first':
var formElements = ['<input />', '<select></select>', '<textarea></textarea>'];
$('td:nth-child(2)').html(function(i) {
return $(formElements[Math.floor(Math.random() * formElements.length)]).val(i);
});
// the relevant part (explained above):
$('input, select, textarea').eq(0).focus();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<td>LABEL</td>
<td></td>
</tr>
<tr>
<td>LABEL</td>
<td></td>
</tr>
<tr>
<td>LABEL</td>
<td></td>
</tr>
<tr>
<td>LABEL</td>
<td></td>
</tr>
</tbody>
</table>
References:
JavaScript:
Math.random().
Math.floor.
jQuery:
eq().
focus().
html().
would it be possible to us <th> tags to wrap your labels?
Then you could use
$('table').find('td').eq(0).children().eq(0).focus()
Although its a bit long winded, but would work if the child was a input, select or anything else (textarea etc)

How to pass parameters from dynamic table to the event handler without inline javascript hanlder

As I heard, I should avoid using inline javascript handler on html. e.g. onclick = "FUNCTION_NAME".
If I have a table that is generated dynamically, Each row has a button for its own.
If I don't use incline Javascript function, how can I pass some parameters from the table to the event handler?
Maybe passing the data from the table cell is not very hard. What if some data is not shown on the table cell (for security reason), for example, a secret ID number that is used internally within the application and is not supposed to exposure on the html (Setting it in the invisible cell in the table is not safe because people who knows html can still inspect it). How can we pass those data that is not shown on the table from dynamic table to event handler in this case?
If we use inline click attribute, i.e. onclick="javascript_function(parameter_1, parameter_2)" on each row, that's fairly easy to pass any data I want, and I do not need to show those kinds of secure data on the html in order to pass it.
If you use jQuery, I would recommand
<table class="with-val">
<td data-val="17">17 points</td>
</table>
and
$('.with-val').on('click', 'td', function() {
var val = $(this).data('val');
console.log(val) //17
});
This way (with the on(eventType, selector, handler) signature), you don't have to reset the events if rows are deleted or added,
and the markup is much lighter (and it is considred best practice, as you add only one event handler for the whole table).
Giving markup
<td class="val" data-val="17">17 points</td>
you can get value from binding like this:
$('.val').on('click', function() {
var val = $(this).data('val');
console.log(val) //17
});
For this:
Setting it in the invisible cell in the table is not safe because
people who knows html can still inspect it
You must not send secure data in any way to frontend. User can stop your code with breakpoints, debug or anything else, and get this data even when it is not visible in html. In addition this data will be visible in responses for the requests that browser send
You can use click event to call a function, that does the task of getting the value of any paramater you wish.
Hope this helps.
<td><button id="btn">Click me</button></td>
<td><input type="hidden" id="secret_id"></td>
$("#btn").click(function(){
var id = $("#secret_id").val();
alert(id);
});
This is a possible solution:
HTML:
<table border="1">
<thead>
<tr>
<th>HEAD1</th>
<th>HEAD2</th>
<th>HEAD3</th>
</tr>
</thead>
<tr>
<td class="hiddenField">row1 col1</td>
<td>row1 col2</td>
<td><button class="seeHidden">btn1</button></td>
</tr>
<tr>
<td class="hiddenField">row2 col1</td>
<td>row2 col2</td>
<td><button class="seeHidden">btn2</button></td>
</tr>
</table>
CSS:
th:nth-child(1), td:nth-child(1){
display: none;
}
jQuery:
$(".seeHidden").click(function(){
var hiddenField = $(this).parent()
.siblings("td.hiddenField")
.html();
alert(hiddenField);
});
Check this link jsfiddle to see a working example.
Hope it's useful!

Looping through table rows with Javascript/Jquery

So what I'm trying to do is get the last row of an HTML table. If this row then has a certain class I will ignore this row and select the previous one. This would then be cycled through from the end of the table until a row was found without this certain class.
I figured it's probably involving a for loop, a check for the row class and then JQuery's row.prev method, but still not quite sure how to approach this.
Thanks in advance!
To get the last table row that doesn't have a certain class, say targetClass, you can do this:
$("tr:not(.targetClass):last");
I'm not sure what you want to do with this table row, but if you were to add targetClass to the last row that didn't have it, it would look like this
$("tr:not(.targetClass):last").addClass("targetClass");
Check out this fiddle to see it in action
This example shows you how to get the last of each table on the current page: http://jsfiddle.net/JBnzK/
$('table').find('tr:last').each(function(){
if ($(this).hasClass('stupid')) {
$(this).css('color', 'red');
} else {
$(this).css('color', 'green');
}
});
Assuming you've got the following HTML:
<table id="mytable">
<tbody>
<tr>
<td>1</td>
</tr>
<tr id="YouFoundMe">
<td>1</td>
</tr>
<tr class="certainclass">
<td>1</td>
</tr>
<tr class="certainclass">
<td>1</td>
</tr>
<tr class="certainclass">
<td>1</td>
</tr>
</tbody>
</table>
You can do this:
var elWithoutClass = $('#mytable tr:not(.certainclass):last');
if (elWithoutClass.length) {
alert(elWithoutClass.get(0).id);
// alerts "YouFoundMe"
}
:not(.certainclass) will eliminate <tr> without class 'certainclass'
:last will get you the last one
I invite you to check the Selectors documentation page of jquery to learn more about them.

Categories

Resources