I have a table that I am trying to display and activate form information in on a button click. The table is set to "display: none" and the button is connected to the javascript. The javascript works but only after the button has been clicked a second time. I would like for it to work the first time it is clicked.
Button & Table HTML:
<button data-bind="click: addPatient" style="margin-top: 20px; margin-bottom: 10px;">Add Patient</button>
<table id="newPatientForm">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th></th>
</tr>
<tbody data-bind="foreach:Patients">
<tr>
<td class="form-group"><input data-bind="value: FirstName, event: {change: flagPatientAsEdited}, hasfocus: true" /></td>
<td class="form-group"><input data-bind="value: LastName" /></td>
<td class="form-group"><button data-bind="click: $parent.removePatient">Delete</button></td>
</tr>
</tbody>
</table>
This is the table CSS:
#newPatientForm {
display: none;
}
This is the javascript that the button calls.
self.addPatient = function () {
var divElement = document.getElementById('newPatientForm');
var patient = new PatientViewModel({ SiteId: 0, FirstName: "", LastName: "", ObjectState: ObjectState.Added });
if (divElement.style.display == 'none') {
divElement.style.display = 'block';
self.Patients.push(patient);
}
else {
divElement.style.display = 'none';
self.Patients.pop(patient);
}
},
You need to check the computed style when it is set in a stylesheet
function isHidden(elem) {
var style = window.getComputedStyle(elem);
return style.display === "none";
}
Change
if (divElement.style.display == 'none') {
to
if (isHidden(divElement)) {
which uses the above method.
It would probably bet better to just have a class that is hidden or not and toggle the class.
And finally, I do not think self.Patients.pop(patient); is doing what you think it is doing. the pop method has no arguments. It does not find and remove elements.
Related
I have a HTML document with a table in it
<table>
<tr>
<th class="street">Street</th>
<th class="city">City</th>
<th class="country">Country</th>
</tr>
<tr>
<td class="street">street-1</td>
<td class="city">city-1</td>
<td class="country">country-1</td>
</tr>
<tr>
<td class="street">street-2</td>
<td class="city">city-2</td>
<td class="country">country-2</td>
</tr>
</table>
Now I am looking for a way to implement buttons.
For example
<button id="button_street">TOGGLE STREET</button>
<button id="button_city">TOGGLE CITY</button>
<button id="button_country">TOGGLE COUNTRY</button>
If a button is pressed, the corresponding column in the table should be hidden.
Even better (than hidden) it would be if I could change the css design with the buttons.
My (temporary) solution is quite cumbersome...
It's a javascript solution that changes one class (visible) to another (hidden).
Spry.Utils.addLoadListener(function() {
let buttonToggle = document.getElementById('button_street');
buttonToggle.addEventListener('click', toggleClass);
function toggleClass() {
let collection = document.getElementsByClassName("street");
for (var i=0; i<collection.length; i++) {
collection[i].classList.toggle('street_design2');
}
}
}
This javascript must be repeated for each button.
where the css is the following:
.street {
background-color: blue;
}
.street_design2 {
background-color: red;
}
If I want to hide the "street-column" I can use display: none
I haven't found any other javascript solution to address all elements with class street. Maybe there is a better solution?
also it would be good if i could pass the function to an array like this:
const array = ["street", "city", "country"]
instead of repeating it for each button. Maybe there is a solution (a loop?)?
You can use document.querySelectorAll to address all elements with same tagName, class, etc, and then use .forEach() to add the EventListener to all elements at once for your case.
function toggleClass(collection) {
document.querySelectorAll('.' + collection).forEach(function(element) {
element.classList.toggle(collection + '_design2');
});
}
document.querySelectorAll('button').forEach(function(button) {
button.addEventListener('click', function(e) {
let collection = this.id.split('_').pop();
toggleClass(collection);
});
});
.street {
background-color: blue;
}
.street_design2 {
background-color: red;
}
.city_design2 {
background-color: yellow;
}
.country_design2 {
background-color: green;
}
<table>
<tr>
<th class="street">Street</th>
<th class="city">City</th>
<th class="country">Country</th>
</tr>
<tr>
<td class="street">street-1</td>
<td class="city">city-1</td>
<td class="country">country-1</td>
</tr>
<tr>
<td class="street">street-2</td>
<td class="city">city-2</td>
<td class="country">country-2</td>
</tr>
</table>
<button id="button_street">TOGGLE STREET</button>
<button id="button_city">TOGGLE CITY</button>
<button id="button_country">TOGGLE COUNTRY</button>
You can use the data property to specify which node to target. It will help in avoiding the name conflicts in the future and also callback will only execute for the specific button where data-table-toggle property is specified so it will avoid conflicts with other buttons.
Code will adds an event listener to all the buttons in a web page that have a custom data attribute "data-table-toggle". When the user clicks on one of these buttons, the tableToggle() function is executed.
The tableToggle() function first retrieves the value of the "data-table-toggle" attribute of the clicked button using this.getAttribute("data-table-toggle"). It then uses this value to select all the table rows that have a class attribute matching the value of the "data-table-toggle" attribute. This is done using the document.querySelectorAll() method with a CSS selector string that targets all the elements that have a child element with the specified class.
Finally, the function toggles the class of each selected table row between two different styles: the original class associated with the value of "data-table-toggle" attribute and a new class with "-_design2" appended to it. This is done using the classList.toggle() method.
Overall, this code dynamically modifies the styles of table rows based on the clicked button, allowing for a simple toggle effect.
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.3.2/axios.min.js" integrity="sha512-NCiXRSV460cHD9ClGDrTbTaw0muWUBf/zB/yLzJavRsPNUl9ODkUVmUHsZtKu17XknhsGlmyVoJxLg/ZQQEeGA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="script.js"></script>
<style>
table .street-_design2 {
display:none;
}
table .city-_design2 {
display:none;
}
table .country-_design2 {
display:none;
}
</style>
</head>
<body>
<table>
<tr>
<th class="street">Street</th>
<th class="city">City</th>
<th class="country">Country</th>
</tr>
<tr>
<td class="street">street-1</td>
<td class="city">city-1</td>
<td class="country">country-1</td>
</tr>
<tr>
<td class="street">street-2</td>
<td class="city">city-2</td>
<td class="country">country-2</td>
</tr>
</table>
<button data-table-toggle="street">TOGGLE STREET</button>
<button data-table-toggle="city">TOGGLE CITY</button>
<button data-table-toggle="country">TOGGLE COUNTRY</button>
</body>
window.onload = function () {
Array.from(document.querySelectorAll("button")).forEach(function (element) {
if (!element.getAttribute("data-table-toggle")) return;
element.addEventListener('click', tableToggle);
});
function tableToggle() {
const attribute = this.getAttribute("data-table-toggle");
Array.from(document.querySelectorAll(`tr .${attribute}`)).forEach(function (selector) {
selector.classList.toggle(`${attribute}-_design2`);
})
}
};
I'm trying to get two buttons to switch between 2 tables with javascript, but whenever i test, both of the tables appear instead of just one
var table1 = document.getElementById("table1");
var table2 = document.getElementById("table2");
var btnTab1 = document.getElementById("showTable1");
var btnTab2 = document.getElementById("showTable2");
btnTab1.onclick = function() {
table1.style.display = "table";
table2.style.display = "none";
}
btnTab2.onclick = function() {
table1.style.display = "none";
table2.style.display = "table";
}
<table id=table1>
<table id=table2>
<input type="button" id="showTable1" value="Table 1">
<input type="button" id="showTable2" value="Table 2">
I expect 1 table to be showing at a time and the button to switch between them, but instead they both show at the same time and the buttons dont do anything
var table1 = document.getElementById("table1");
var table2 = document.getElementById("table2");
var btnTab1 = document.getElementById("showTable1");
var btnTab2 = document.getElementById("showTable2");
btnTab1.onclick = function() {
table1.style.display = "table";
table2.style.display = "none";
}
btnTab2.onclick = function() {
table1.style.display = "none";
table2.style.display = "table";
}
#table2 {
display: none;
}
<table id="table1">
<tr>
<td>Table1</td>
</tr>
</table>
<table id="table2">
<tr>
<td>Table2</td>
</tr>
</table>
<input type="button" id="showTable1" value="Table 1">
<input type="button" id="showTable2" value="Table 2">
Your code is right but you just forgot to initialize the display on your tables and to close the table tags :
(I added some content so we see which table is visible)
var table1 = document.getElementById("table1");
var table2 = document.getElementById("table2");
var btnTab1 = document.getElementById("showTable1");
var btnTab2 = document.getElementById("showTable2");
btnTab1.onclick = function() {
table1.style.display = "table";
table2.style.display = "none";
}
btnTab2.onclick = function() {
table1.style.display = "none";
table2.style.display = "table";
}
<table id=table1 style="display: table">
<thead>
<tr>
<th colspan="2">The table header 1</th>
</tr>
</thead>
</table>
<table id=table2 style="display: none">
<thead>
<tr>
<th colspan="2">The table header 2</th>
</tr>
</thead>
</table>
<input type="button" id="showTable1" value="Table 1">
<input type="button" id="showTable2" value="Table 2">
I'm replacing your tables with divs, because it's easier to show the thought behind the code with less clutter.
You can add listeners directly onto the buttons, and by doing that, it's easier to send in parameters. I made something a little bit more dynamic, where it's possible to add any number of tables with ease without having to change the code. All you need to do is change the parameter in showTable('table2') to showTable('table3') in the button.
This solutions wont demand as many variables, and it's not dependent on whether the page has loaded or not. Like #brianfit said, you're probably having your javascript code in the head element which means that the document doesn't exist when the code runs, and you can't therefor find any elements on the page.
I also think it's better to use classes to style elements, rather changing the style in code. The semantics looks better, and you get IMHO a better understanding of why some tables are hidden.
function showTable(tableIdToShow) {
hideAllTables();
let tableElement = document.getElementById(tableIdToShow);
tableElement.classList.add('show');
}
function hideAllTables() {
let allTableElements = document.querySelectorAll('.table');
for (tableElement of allTableElements) {
tableElement.classList.remove('show')
}
}
.table {
display: none;
}
.table.show {
display: block;
}
<div id="table1" class="show table">TABLE 1</div>
<div id="table2" class="table">TABLE 2</div>
<input type="button" onclick="showTable('table1')" value="Table 1">
<input type="button" onclick="showTable('table2')" value="Table 2">
If your code block is in the header, try moving it down to just before the closing body tag. I just ran it both ways, and it doesn't work in the header, does work in body.
The reason: the script is loaded in the header before the html, so the getelementbyid call returns null. By placing the script after the html, the div populates the id and the script loads it properly.
If you load your file in Chrome and turn on the Javascript console, you would have seen an error message similar to the following:
(index):12 Uncaught TypeError: Cannot set property 'onclick' of null
at (index):12
Your script is correct and is working. The problem was with the <table>. it was empty and not closed. So you were not able to notice the change in table display. try the below sample.
var table1 = document.getElementById("table1");
var table2 = document.getElementById("table2");
var btnTab1 = document.getElementById("showTable1");
var btnTab2 = document.getElementById("showTable2");
btnTab1.onclick = function () {
table1.style.display = "block";
table2.style.display = "none";
}
btnTab2.onclick = function () {
table1.style.display = "none";
table2.style.display = "block";
}
<table id=table1>
<tr>
<td>table 01</td>
</tr>
</table>
<table id=table2>
<tr>
<td>table 02</td>
</tr>
</table>
<input type="button" id="showTable1" value="Table 1">
<input type="button" id="showTable2" value="Table 2">
I have a table whose values are looped with my product list and I have a textbox for 'Quantity' and a button for 'Add to Cart'. I am trying to do when I click on 'Add to Cart' button, it will check the Quantity value if it has value or not then the 'Quantity' textbox will get disabled for that specific row only.
Product.cshtml
<table id ="productList" class="table table-dark">
<tr>
<th>Product ID</th>
<th>Title</th>
<th>Description</th>
<th>Quantity</th>
<th>Add to cart</th>
</tr>
#foreach (var item in Model)
{
<tr class="active">
<td>#item.Id</td>
<td>#item.Title</td>
<td>#item.Description</td>
<td>#Html.TextBox("Quantity", "0", new { #class = "form-control" })</td>
<td><button class="btn btn-default pull-left" onclick="disableText(Quantity);" id="btn-addToCart">Add To Cart</button></td>
</tr>
}
</table>
javascript
function disableText(QtyVal) {
if(QtyVal.value == '')
{
document.getElementById("Quantity").value = 1;
}
}
Currently the output of my project is everytime I click any submit button, it will always update the first row (which is incorrect).
The goal is it will update the Quantity and disable it for that specific row
Your problem lies in this button definition:
<button class="btn btn-default pull-left" onclick="disableText(Quantity);" id="btn-addToCart">Add To Cart</button>
The foreach loop generates duplicate id attribute values for all buttons, which builds invalid HTML and causing undesired behavior. Since your table also contains <input> tag, you should use for loop instead of foreach and put distinct class name to the button, e.g. buttonadd:
<!-- assumed that #model List<Product> is used -->
<table id ="productList" class="table table-dark">
<tr>
<th>Product ID</th>
<th>Title</th>
<th>Description</th>
<th>Quantity</th>
<th>Add to cart</th>
</tr>
#for (int i = 0; i < Model.Count; i++)
{
<tr class="active">
<td>#Model[i].Id</td>
<td>#Model[i].Title</td>
<td>#Model[i].Description</td>
<td>#Html.TextBox("Quantity", "0", new { #class = "form-control" })</td>
<td><button type="button" class="btn btn-default pull-left buttonadd">Add To Cart</button></td>
</tr>
}
</table>
Then you can reference buttonadd class as selector to find out which row the button contained, and setting <input> state to readonly (not disabled because you want to send quantity values on form submit; all inputs with disabled attribute are not included during submit) as in example below:
$(document).on('click', '#productList .buttonadd', function () {
var row = $(this).closest('tr');
// find quantity text box in the same row as clicked button
var qty = row.find('input');
// set quantity value and prevent user input
qty.val(parseInt(qty.val()) + 1);
qty.attr('readonly', 'readonly');
});
Note: If you want to use strongly-typed helper, use #Html.TextBoxFor:
#Html.TextBoxFor(m => m[i].Quantity, new { #class = "form-control" })
Live example: .NET Fiddle
Since all of your textboxes have the same Id you cannot specify which one you are trying to update in your js. Try giving a unique ID to your TextBox then when you getElementById("UniqueID") you can ensure you are selecting the correct text box.
You just need to add bellow code at button click event
(this).prev('input').attr("disabled", "disabled")
Use a partial view, save some headache.
<table id ="productList" class="table table-dark">
<tr>
<th>Product ID</th>
<th>Title</th>
<th>Description</th>
<th>Quantity</th>
<th>Add to cart</th>
</tr>
#foreach (var item in Model)
{
#Html.Partial("~/Views/_OrderRowPartial.cshtml", item)
}
</table>
_OrderRowPartial.cshtml:
#model item
<tr class="active">
<td>#item.Id</td>
<td>#item.Title</td>
<td>#item.Description</td>
<td>#Html.TextBoxFor(m => m.Quantity, new { #class = "form-control quantity" })</td>
<td><button class="btn btn-default pull-left" onclick="disableText();">Add To Cart</button></td>
</tr>
Javascript:
function disableText(t, el) {
var qty = el.parentElement.previousElementSibling.getElementsByTagName('input')[0];
mytd.style.border = "1px solid #00FF00";
console.log(qty.value);
if (qty.value == '') {
qty.value = 1;
}
}
Here is an example where I set borders on stuff to make it obvious what is going on. Unclear if you want to disable input or button so I did both.
function disableText(t, el) {
el.style.border = "2px solid #FF0000";
var mytd = el.parentElement;
mytd.style.border = "1px solid #00FF00";
var mytr = mytd.parentElement;
var qtytd = mytd.previousElementSibling;
var qty = qtytd.getElementsByTagName('input')[0];
qtytd.style.border = "2px solid orange";
qty.style.border = "2px solid cyan";
console.log(qty.value);
if (qty.value == '') {
qty.value = 1;
}
// disable button
el.setAttribute("disabled", "disabled");
qty.setAttribute("disabled", "disabled");
}
<table id="productList" class="table table-dark">
<tr>
<th>Product ID</th>
<th>Title</th>
<th>Description</th>
<th>Quantity</th>
<th>Add to cart</th>
</tr>
<tr class="active">
<td>1234d</td>
<td>Cherry Pie</td>
<td>Gooey cherry pie</td>
<td><input type="text" class="form-control quantity" value="2" .>
<td><button class="btn btn-default pull-left" onclick="disableText('',this);">Add To Cart</button></td>
</tr>
<tr class="active">
<td>1234hd</td>
<td>Hot Dog</td>
<td>Dog, With mustard</td>
<td><input type="text" class="form-control quantity" /></td>
<td><button class="btn btn-default pull-left" onclick="disableText('',this);">Add To Cart</button></td>
</tr>
</table>
I am in trouble, I've tried to no avail to do something I'm sure is simple to most seasoned developers.
As you can see, a plus button resides in each table row.
I want to achieve a two table system where I click the plus button on the table to the left transferring the player in the left table to the right table without deleting the player in the row. The subsequent clicks of any plus button should take the player from the row in which it was clicked and fill in the next open row of the table on the right, starting from top. The click of the plus button should disable this row from being picked again, and the minus button on the right table should remove the player and restore his active status on the left table. When the table is filled up I'm trying to have the ability to add players come back with a "table is full" alert. That seems easy enough, but I've been researching this and this is what I came up with. This feels like a jquery solution to me, but I can't even get started on it. I did the best I could below. For reference think of how fanduel.com does their two table drafting system.
$(document).ready( function() {
$('.addplayer').click(function (event) {
$('tr .select').eq(0).clone().appendTo("tr .selected").after();
});
$(".remove-player").click(function (event) {
$(".selected").remove();
});
});
tr:nth-child(even) {
background-color: #e3e3e3;
color:black;
}
tr:nth-child(odd) {
background-color:black;
}
table {
border: #5a5a5a medium solid;
width: 300px;
color:white;
}
input {
cursor: pointer;
}
th {
color:white;
background-color:black;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<table class="left-table">
<th>Player</th>
<th>add</th>
<tr>
<td class="select">Player1</td>
<td>
<input type="button" value="+" class="addplayer" />
</td>
</tr>
<tr>
<td class="select">Player2</td>
<td>
<input type="button" value="+" class="addplayer" />
</td>
</tr>
<tr>
<td class="select">Player3</td>
<td>
<input type="button" value="+" class="addplayer" />
</td>
</tr>
<tr>
<td class="select">Player4</td>
<td>
<input type="button" value="+" class="addplayer" />
</td>
</tr>
</table>
<table class="right-table">
<th>Player</th>
<th>Remove</th>
<tr>
<td class="selected"></td>
<td>
<input type="button" value="-" class="remove-player" />
</td>
</tr>
<tr>
<td class="selected"></td>
<td>
<input type="button" value="-" class="remove-player" />
</td>
</tr>
</table>
You migth want to try this:
$(document).ready( function() {
$('.right-table tr .selected').addClass('empty');
$('.addplayer').click(function (event) {
if($(".right-table tr .empty").length <= 0) {
alert("Table is full");
return;
}
var txt = $(this).closest("tr").find('.select').text();
$(".right-table tr .empty").eq(0).text(txt);
$(".right-table tr .empty").eq(0).attr("data-row",$(this).closest("tr").index());
$(".right-table tr .empty").eq(0).removeClass('empty');
$(this).attr('disabled', true);
});
$(".remove-player").click(function (event) {
var index = $(this).closest("tr").find('.selected').attr('data-row');
$(this).closest("tr").find('.selected').text("");
$(this).closest("tr").find('.selected').addClass("empty");
$('.left-table tr').eq(index).find('.addplayer').attr('disabled', false);
});
});
To run it you may visit it here: https://fiddle.jshell.net/dgu80ajz/5/
Use the following code
$(document).ready(function () {
$('.addplayer').click(function (event) {
var $player_row = $(this).closest('tr');
var $new_row = $player_row.clone();
$new_row.find('.addplayer').addClass('remove-player').removeClass('addplayer').val('-')
$new_row.data('row_num', $('.left-table').find('tr').index($player_row));
$('.right-table').find('tbody').append($new_row);
$player_row.find('.addplayer').attr('disabled', 'disabled');
});
$(".right-table").on('click', '.remove-player', function (event) {
var $row = $(this).closest('tr');
$('.left-table').find('tr').eq($row.data('row_num')).find('.addplayer').attr('disabled', false)
$row.remove();
});
});
You are basically cloning the row that the button is contained in and then putting it in your second table with a reference that can be used to delete it.
Notice the event handler for the remove-player class is a delegate event because you want to bind to the .remove-player buttons that do not yet exist in the right table.
https://jsfiddle.net/67g2cb8m/4/
for a working example
I have something that seems fairly simple but I'm stumped. I want a dropdown within a table that affects how many table rows are shown. By default, only 2 rows are shown. By selecting 4 in the dropdown, 4 rows should be shown. I am only seeing one of the hidden rows show up, and I've tried to wrap the 2 rows in a hidden div as well, no luck. Ideas?
<table border="1">
<tr>
<td class="noBG" colspan="3">
<select id="displayText" onchange="javascript:toggle();">
<option>2</option>
<option>4</option>
</select>Items
</td>
</tr>
<thead>
<tr>
<th>Dates</th>
<th>Time</th>
<th>Person</th>
</tr>
</thead>
<tr>
<td>12/3</td>
<td>12:45</td>
<td>John Doe</td>
</tr>
<tr>
<td>12/4</td>
<td>12:45</td>
<td>James Doe</td>
</tr>
<tr id="toggleText" style="display: none">
<td>12/4</td>
<td>12:45</td>
<td>Janey Doe</td>
</tr>
<tr id="toggleText" style="display: none">
<td>12/4</td>
<td>12:45</td>
<td>Janey Doe</td>
</tr>
</table>
<script language="javascript">
function toggle() {
var ele = document.getElementById("toggleText");
if(ele.style.display == "block") {
ele.style.display = "none";
}
else {
ele.style.display = "block";
}
}
</script>
Using display: block; doesn't work as the table rows will then displayed not in the right way. But you can toggle the visibility by adding and removing a class, which is defined with display: none;. So you must not switch display: none/block;, but the class.
This works (incl. jQuery): http://jsfiddle.net/Yuvvc/1/
You can use following code for JS function:
function toggle() {
$.each($('tr[name=toggleText]'), function() {
$(this).toggleClass("hiddenRow", $(this).attr('class') != "hiddenRow");
});
}
With the second parameter (bool) for .toggleClass you can add and remove the class.
EDIT
Here a non-jQuery version:
function toggle() {
var rows = document.getElementsByName("toggleText");
for(var i=0; i<rows.length; i++)
{
rows[i].className = (rows[i].className == "hiddenRow") ? "" : "hiddenRow";
}
}
Change all <tr id="toggleText" to <tr name="toggleText", and then change the toggle function to the following:
function toggle() {
var ele = document.getElementsByName("toggleText");
for (var i = 0; i < ele.length; i++) {
if (ele[i].style.display == "block") {
ele[i].style.display = "none";
}
else {
ele[i].style.display = "block";
}
}
}
You can toggle the hidden rows by giving each row an id like this:
<table class="table">
#foreach (var item in Model)
{
<tr>
<td onclick="toggle1(#item.ID)" colspan="3">
#Html.DisplayFor(modelItem => item.Name)
</td>
</tr>
<tr class="hidden" id="bluh_#item.ID">
<td>
#Html.DisplayFor(modelItem => item.Code)
</td>
<td>
#Html.DisplayFor(modelItem => item.Position)
</td>
</tr>
}
then use JavaScript to Hide and Show the Children Rows
<script>
function toggle1(something) {
$("#bluh_"+something).toggleClass('hidden');
}
</script>