Show/Hide Multiple DIVs in a variable - javascript

I am using nested for loops to generate multiple instances of a table with details of projects; under which I wish to have a show/hide button that will give a short description of each project at a high level.
I am tring to manipulate code I found here: https://forums.digitalpoint.com/threads/javascript-to-show-hide-tables.1009918/
The following code produces a "Show/Hide" link that does not work on my page (see screenshot). Am I missing something?
FYI - "Separate" in the code below is an array containing unique project references to facilitate the separation of the tables per project. So where Separate contains 4 elements, there should be 4 projects, 4 tables, and so on.
Many Thanks,
Karl
function showhide(id){
if (document.getElementById){
obj = document.getElementById(id);
if (obj.style.display == "none"){
obj.style.display = "";
} else {
obj.style.display = "none";
}
}
}
for(i in Separate){
DescID[i] = "DescID"+i;}
var Table = "";
for(i in Separate){
Table += "<table id='dashboard' summary='Project Dashboard'>";
Table += "<THEAD>";
Table += "<TR><TH scope='col' colspan=4><B>"+ Separate[i] +"</B></TH></TR>";
Table += "<TR><TH scope='col'>Task Names</TH><TH scope='col'>Task Summary</TH><TH scope='col'>RAG</TH><TH scope='col'>Timeline</TH></TR></THEAD>";
Table += "</THEAD>";
Table += "<TBODY>";
for(j in Project){
if(Project[j] == Separate[i]){
Table += "<TR><TD title='" + Comments[j] + "'>"+ Task[j] +"</TD><TD>"+ Summary[j] +"</TD><TD><img src='/images/RAG/" + RAG[j] + "'></TD><TD>"+ DateType[j] +" "+ Status[j].substring(0,10) +"</TD></TR>";
}
}
Table += "</TBODY>";
Table += "</table>";
Table += "<a onclick ='javascript:ShowHide('" + DescID[i] + "')' href='javascript:;' >Show/Hide Project Description</a>";
Table += "<div class='mid' id='" + DescID[i] + "' style='DISPLAY: none' >Placeholder for Project Description</div>";
Table += "<BR>";
}

You're missing the fact that HTML tag "id"s (e.g., for your Projects' tables) should be unique, otherwise "getElementById" won't work. Now they're all set to "dashboard". At the very least you could add the index, and make them "dashboard1", "dashboard2", etc...
Also, IIRC, a better opposite for 'display = "none";' is 'display = "inline";', instead of 'display = "";'. Although this needs more testing on different browsers to select the best option.
Thirdly, your JavaScript call within the onclick events use single quotes BOTH for the attribute value definition (surrounding the JS call) and for the string parameter (the id to show/hide)... That's not valid syntax, and you need one of these two use-cases to be double-quotes.
And the other main problem your code has is as mikez302 already spotted: "Javascript function names are case-sensitive. Your function is called showhide but you are trying to call ShowHide." Correcting these two issues (the quotes and the function name) will allow the code to work, i've just tested it. :)

Related

How do I add a radio button to HTML table via jquery

I'm using Jquery $.post to get string array from SQL based on selection from a drop-down that I'm then reading into an HTML table. Each time they change selection on drop down, it clears all but the header row from the table and reloads new values. That is working fine.
It is just a basic 3 row table; a unique identifier, a value and a count shown as string. Every record has all 3, so I'm just using for loop with counters to control start/end of rows. In my form it's header is defined as such:
<div class="col-md-10">
<table id="attEditTable" style="width:100%" cellpadding="0" cellspacing="0" border="1" class="row">
<tbody>
<tr style="background-color: #F0F8FF;">
<th></th>
<th>Attribute Value</th>
<th>Item Count</th>
</tr>
</tbody>
</table>
</div>
I'm now trying to change the 1st cell of each row to a radio button with the value set to the value I was displaying in that cell.
Currently when the view displayed it is showing [object Object] in the first cell of every row instead of a radio button.
Sorry I am a newb at this with no training on Java or MVC - so hoping just a simple syntax issue...
Trying this basic one returned syntax error on input:
<input type="radio" name="SelERow" value='+editValues[i]+' />
I've also tried (both had same result of [object Object]):
$("input:radio[name=\"SelERow\"][value="+editValues[i]+"]")
$('<input type="radio" id="ERow" name="SelERow" value='+editValues[i]+' />')
Per David's suggestions I've now also tried (both resulted in no data and no error):
'<input type="radio" name="SelERow" value='+editValues[i]+' />'
var tblCell = $('<td />'); // create an empty <td> node
if (x == 1 || (x - 1) % 3 == 0) {
var input = $('<input />', { type: 'radio', id: 'ERow', name: 'SelERow', value: editValues[i] });
tblCell.append(input); // append an <input> node to the <td> node
} else {
tblCell.text(editValues[i]); // or just set the text of the <td> node
}
With the 2nd one I also changed the line: tblRow = tblRow + ""; to instead be tblRow = tblRow + tblCell + "";
Current Script
<script>
$(function () {
$("#EditAttributeName").change(function () {
var selectedName = $(this).val();
// Delete all but first row of table
$("#attEditTable").find($("tr")).slice(1).remove();
var url2 = "EditDD2changed?selectedName=" + selectedName;
$.post(url2, function (editValues) {
var x = 0;
var tblRow = "<tr>";
for (var i=0; i < editValues.length; i++)
{
x++;
if (x == 1 || (x - 1) % 3 == 0) {
tblRow = tblRow + "<td>" + $('input:radio[name="SelERow"][value="' + editValues[i] + '"]');
}
else {
tblRow = tblRow + "<td>" + editValues[i] + "</td>";
}
if (x % 3 == 0)
{
tblRow = tblRow + "</tr>";
$("#attEditTable").append(tblRow);
tblRow = "<tr>";
}
}
})
});
});
</script>
Console is showing no error messages.
Looks like it's close. To start, there's an important difference in the two attempts. This is a jQuery selector syntax:
$('input:radio[name="SelERow"][value="' + editValues[i] + '"]')
So it's not creating an <input/>, but looking for an <input/>. Which isn't what you want in this case. Your other attempt uses the syntax for creating an element:
$('<input type="radio" id="ERow" name="SelERow" value=editValues[i] />')
Though an issue here (which may have just been a copy/paste error in posting the question? but for completeness and for future readers it may as well be addressed...) appears to be that the editValues[i] is just part of the string. You want to concatenate it into the string. There are a couple ways to do that. Either direct concatenation:
$('<input type="radio" id="ERow" name="SelERow" value="' + editValues[i] + '" />')
Or string interpolation (take note of the different overall quotes, using back-ticks this time):
$(`<input type="radio" id="ERow" name="SelERow" value="${editValues[i]}" />`)
The latter is newer syntax but should be widely enough supported by now. (Though in any given business environment who knows what ancient browsers one may need to support.) Could just be personal preference between the two.
it is showing [object Object]
The main issue producing the result you've observing is that you're concatenating the result of that jQuery operation directly as a string:
tblRow + "<td>" + $('<input type="radio" id="ERow" name="SelERow" value="' + editValues[i] + '" />')
(Coincidentally, whether you're creating an element or looking for an element, this observed output would be the same because both operations return an object.)
The result of an $() operation is not itself a string, but a more complex object. When concatenated with a string it has to be interpreted as a string, and unless the object has a meaningful .toString() implementation (this one doesn't appear to) then the default string representation of a complex object is exactly that: "[object Object]"
There are a couple approaches you can take here. One would be to just use strings entirely, you don't necessarily need a jQuery object here:
tblRow + '<td><input type="radio" id="ERow" name="SelERow" value="' + editValues[i] + '" /></td>'
Since you're using jQuery later to append the result to the HTML, you can just build up all the HTML you like as plain strings and let jQuery handle turning them into DOM objects when you send them to .append().
The other option, if you definitely want to "use jQuery" in this situation or are otherwise being instructed to, would be to build the hierarchy of HTML elements as jQuery objects and then pass that hierarchy to .append(). Constructing such a hierarchy can look something like this:
var tblCell = $('<td />'); // create an empty <td> node
if (x == 1 || (x - 1) % 3 == 0) {
var input = $('<input />', { type: 'radio', id: 'ERow', name: 'SelERow', value: editValues[i] });
tblCell.append(input); // append an <input> node to the <td> node
} else {
tblCell.text(editValues[i]); // or just set the text of the <td> node
}
Note that each $() operation creates an HTML element node, and you can supply attributes for it as a second argument to the $() function. Then those nodes can be .append()-ed to each other just like you .append() the HTML string to $("#attEditTable").
In your particular case this may get a little more cumbersome because your loop isn't just looping through cells or just through rows, but through both and using a hard-coded count to determine whether it's reached the end of a row or not. So, as part of learning/practicing jQuery, it may be worth the effort to try this approach. But I suspect the shortest path to getting your code working with minimal changes would be the string concatenation approach above.
Side note: This code is using the same id value for the radio buttons created within this loop. The result is that there is expected to be multiple elements on the page with the same id. This is technically invalid HTML. If you ever try to use that id to reference an element, the resulting behavior will be undefined. (It might work in some cases, might not in others, purely coincidentally.) Though if you don't need to use that id to reference the elements, you may as well remove it entirely.

How to pass a value from a Javascript generated button to a controller?

My code generates a table with a button at the end of each row. When the user clicks a button how can I pass a property u.userEmail to the controller via the button? Will the value being sent to the controller be a string?
My (non-working) attempt:
<script>
$(document.body).append("waiting on async table to load<br>");
$(document).ready(function () {
$.getJSON("/Account/LoadClaimsTable", function (crewResponse) {
//returns a List<UserClaims>
$(document.body).append("<table>")
crewResponse.forEach(function (u) {
var s = "";
s+="<tr><td>" + u.userEmail + "</td>";
u.userClaims.forEach(function (k) {
console.log("added claim"+k.value);
s += ("<td>" + k.type + "</td><td>" + k.value + "</td><td>" +
"<input type=\"hidden\" name=\"userEmail\" value=\"`${u.userEmail}`\" />"+
"<input type=\"button\" value=\"Create\" onclick=\"location.href='#Url.Action("EditClaims", "Account")'" />
+"</td>");
});
s += "</tr>";
$(document.body).append(s);
s = "";
});
$(document.body).append("</table>")
});
});
</script>
AccountController.cs contains:
public ActionResult EditClaims(string userEmail)
{
return View("StringView", userEmail);
}
You have to pass it on the url of the action. Not sure if you want to pass u.userEmail, but it could looks like this:
crewResponse.forEach(function (u) {
var s = "<tr><td>" + u.userEmail + "</td>";
u.userClaims.forEach(function (k) {
console.log("added claim"+k.value);
s += ("<td>" + k.type + "</td><td>" + k.value + "</td><td>" +
"<input type=\"hidden\" name=\"userEmail\" value=\"`${u.userEmail}`\" />"+
"<input type=\"button\" value=\"Create\" onclick=\"location.href='#Url.Action("EditClaims", "Account")?userEmail=" + u.userEmail + "'\"/></td>");
});
s += "</tr>";
$(document.body).append(s);
});
There are multiple ways to do it. One is mentioned in the answer above by Felipe. Here is another alternate approach using unobtrusive js
Add the email as html5 data attributes to the button along with another attribute which we will use bind the click behavior.
u.userClaims.forEach(function (k) {
// Add quotes as needed if you want multiline ( i just removed those)
s += "<td>" + k.type + "</td><td>" + k.value + "</td><td>
<input type='button'
clickablebutton data-email='" + u.email + "' value='Create'/></td>";
});
Now, in your document ready, bind a click event handler to those elements (with our custom attribute) and read the data attribute and build the url you need.
$(document).on("click", "input[clickablebutton]", function (e){
var url = '#Url.Action("EditClaims", "Accounts")?useremail=' + $(this).data("email");
window.location.href = url;
});
Some other suggestions
Use the appropriate element. Button is better than input (Consider accessibility)
If it is for navigation, Use an anchor tag instead of a button.
Inline javascript is not great. Let the browser parses your markup without any interruptions and you can add the behavior scripts later (that is the whole point of uobutrisive js approach)
The approach you appear to be taking would be Ajax, response, render a template. With that being said, you may want to rethink your approach.
Step 1.
Build a template
<template id="...">
<button type="button" value="[action]" onclick="[url]">[text]</button>
</template>
Step 2.
Create your request.
axios.get('...').then((response) => {
// Retrieve template.
// Replace bracket with response object model data.
html += template.replace('[action]', response.action);
});
Step 3.
Have the JavaScript render your template.
The above can create a clear concise codebase that is easier to maintain and scale as the scope changes, rather than an individual request performing a change with embedded markup. This approach has worked quite well for me, also I feel it'll make you troubleshooting and definition easier, as the controller is handing an object back to your JavaScript instead of a markup / view data. Which will be a better finite control for the frontend and clear modifications in future.

SPFx with REACT.js - Render table with variable number of TD

I am currently working on refactoring my existing javascript into a SharePoint Framework solution with REACT. I got most parts working but I am stumped on the Render function to return html contents for a table with a variable number of columns. The number of columns is set from a web part property. How can I convert the original javascript (below) into REACT code?
var html = "<table>";
for (var i = 0; i < data.d.results.length; i++) //data is from AJAX SP REST query
{
if ((i % tdWidth) == 0 ) { html += "<tr>"; } //if this is the first column in the row
html += "<td><div>" + data.d.results[i] + "</div></td>";
if ((i % tdWidth) == (tdWidth - 1) ) { html += "</tr>"; } //if this is the last column in the row
}
html += "</table>";
$("#tableContainerDiv").html(html); //Set the html tag of the container div
You have to create your own component to use. Check the below link.
http://buildwithreact.com/tutorial/components
OR
best thing in react is reuse the ready made component. instead of making the html from js try to use the react-super-responsive-table.
https://www.npmjs.com/package/react-super-responsive-table
You can easily loop through rows and count with headers. I hope it may help you.
OR
Another approach is to set the response into array i.e. data.d.results put in array and render your control inside the render method.
MyrenderItems = this.state.UserArray.map(function (item, i) {
.....
}

How to get the total of the list ordered, with Javascript

I'm making a code of a online delivery webpage, and I having a hard time trying to figure out how to output the total of the list ordered by the user.
function ListOrder(){
document.getElementById('order').innerHTML += "<div id=\"YourOrders\">" + + document.getElementById('FoodName').value + document.getElementById('quantity').value + document.getElementById('Totality').value + "</div><br>";}
Edited: I want to know how I can get the sum of the total price. So, I placed a parseInt between the document.getElementById('Totality').value . It looks like this now,
function ListOrder(){
document.getElementById('order').innerHTML += "<div id=\"YourOrders\">" + + document.getElementById('FoodName').value + document.getElementById('quantity').value + parseInt(document.getElementById('Totality').value) + "</div><br>";}
Can someone help me make a function or something for that? Javascript only, please. I'm still kinda new at it.
function ListOrder(){
document.getElementById('order').innerHTML +=
"<div id=\"YourOrders\">" +
parseInt(document.getElementById('FoodName').value) +
parseInt(document.getElementById('quantity').value) +
parseInt(document.getElementById('Totality').value) +
"</div><br>";
}
the kernel of your code should look like the following (double + operator deleted, reformatted):
function ListOrder(){
document.getElementById('order').innerHTML +=
"<div id=\"YourOrders\">" + (
document.getElementById('FoodName').value
+ document.getElementById('quantity').value
+ document.getElementById('Totality').value
)
+ "</div><br>"
;
}
You've phrased your question in a way that suggests you wish to output an order list assembled from the content of all (html) elements with certain ids.
this won't work reliably:
Ids should be document unique.
The Js functions you use do not iterate over lists.
instead, proceed along the following lines (which assume that you import jquery, a cross-browser dom-handling and ajax library (which you should use anyway :)):
function ListOrder(){
var e_orders = $("<div id=\"YourOrders\">");
$("#order").append(e_orders);
$(".FoodName").each ( function ( idx_fn, e_fn ) {
$(e_orders).append(
$("<div/>").append(
$(e_fn).val()
+ $(e_fn).nextAll('.quantity').val()
+ $(e_fn).nextAll('.Totality').val()
);
);
$(e_orders).append("<br>");
});
return e_orders;
}
The code template assumes that the source data are elements with value attributes being marked with css classes quantity, Totality and 'FoodName``, that these elements are siblings and unique within a container element for each item incl. quantity information. It should be flexible enough to be tailored to your actual needs and html structure.

JQuery click event firing multiple times

I know that there's lot here on already on multiple click events being fired off, I think I've read them all but still can't see what's going wrong here.
Hope fully I'm missing something obvious that someone else can pick up easily...
Some background
My code works inside an Enterprise Social Networking platform and creates a BI dashboard for content analysis (about a 1000 lines of the stuff, mostly domain specific, so too much to post in it's entirety).
The part that is causing me grief is the function that builds the dashboard visualisation itself.
Here goes...
function makePage(){
$("#policyCount").text(policyCount);
var docTypes=getGlobalDocTypes(polOwners); //returns a constrained vocab array
var statusTypes=getGlobalStatusTypes(polOwners); //returns a constrained vocab array
$.each(polOwners,function(){ // polOwners is a global array that contains the BI data to be visualised
html=""
var ownerName = this.name.split(":")[1]; // name is a str in format "Owner:HR"
html += "<div id='" + ownerName + "' class='ownerData'>";
html += "<div class='ownerHeading'>" + ownerName + "</div>";
html += this.policies.length + " Policy documents maintained<br />"; // policies is an array of docs managed by owner
divIDReview = "dboard_" + ownerName + "reviewchart";
html += "<div id='" + divIDReview + "' class='dboardelement'></div>";
divIDType = "dboard_" + ownerName + "typechart";
html += "<div id='" + divIDType + "' class='dboardelement'></div>";
divIDStatus = "dboard_" + ownerName + "statuschart";
html += "<div id='" + divIDStatus + "' class='dboardelement'></div>";
html += "<div id='" + ownerName + "ToggleTable' class='toggletable' owner='" + ownerName + "'>";
html += "Click to display all " + ownerName + " documents<br /></div>";
html += "<div id='" + ownerName + "polTable' class='poltable'>";
html += getPolTable(this.policies); // Returns an HTML table of doc metadata
html += "</div>";
html += "</div>";
  $("#owners").append(html); // When this function is called #owners is an empty div
$(".toggletable").mouseover(function(){
$(this).css({'cursor':'pointer','text-decoration':'underline'});
});
$(".toggletable").mouseout(function(){
$(this).css( {'cursor':'default','text-decoration':'none'});
});
$(".toggletable").each(function(i, elem){
$(elem).click(function(){
if ($(this).next(".poltable").css("display")=="none"){
// Currently hidden - so show
if (debug){console.log($(this).attr("id") + " was clicked")}
$(this).html("Click to hide " + $(this).attr('owner') + " documents<br/>");
$(this).next(".poltable").css("display","block");
} else {
if (debug){console.log($(this).attr("id") + " was clicked")}
$(this).html("Click to display all " + $(this).attr('owner') + " documents<br />");
$(this).next(".poltable").css("display","none");
}
});
});
// the next section calls functions that use the Google vis api to draw pie charts
drawPie(300,200, "Review Status", "Status", "Policies", getReviewStatus(this.policies), ["green","orange","red"], divIDReview);
drawPie(300,200, "Document Types", "Type", "Docs", getDocTypes(this.policies, docTypes), [], divIDType);
drawPie(300,200, "Document Status", "Status", "Docs", getStatusTypes(this.policies, statusTypes), [], divIDStatus);
});
}
Hopefully that's enough to illustrate the problem.
You'll see that the code builds a dashboard display for each polOwner consisting of three pie charts and an option to hide or display a table of underlying data.
I started by applying the click event to the .toggletable class. When that fired multiple times I used the method described on another answer here with the .each to attach a unique event to each instance of the class.
So, what happens?
There are currently 9 polOwners and at first glance, the click event only seems to be toggling the display state of every other table. The console log however shows that this is because it is firing 9 times for the first instance, 8 for the second, 7 for the third etc. with the odd numbers leaving the table in the alternate state (when this works the display will change to a .toggle animation).
For info, While I'm a text editor person, I do have a copy of MS Expression Web 4 which is a useful tool for error checking HTML. I've pasted in a copy of the entire generated markup (nearly 4000 lines) and can't see any bad nesting or structure errors.
Any ideas folks?
You've got some nested loops:
// jQuery each on polOwners
$.each(polOwners,function(){
// ... code that appends .toggletable class
// jQuery each on .toggletable class
$(".toggletable").each(function(i, elem){
// code that runs on the toggletable element
});
});
For each polOwner you are adding a div with the toggletable class. Then inside there you are looping through each div with a toggletable class and adding a click event.
This adds 1 click for the first polOwner, 2 for the second, three for the third and so on.
Move the toggletable each outside of the polOwner each and you should be good

Categories

Resources