I'm working on a personal project and I've run into an issue that I haven't been able to solve.
Here is a function that generates new table rows into a table (with id of "tableData") when a button is clicked:
function addNewRow(){
var tableEl = document.getElementById("tableData");
var newLine = '<tr class="newEntry">';
var classArray = ["classA", "classB", "classC", "classD"];
for (var i = 0; i < classArray.length; i++){
newLine += '<td><input class="' + classArray[i] + '"></td>';
}
newLine += '</tr>';
tableEl.insertAdjacentHTML("beforeend", newLine);
}
document.getElementById("addRow").addEventListener("click", addNewRow, false);
//the element with id="addRow" is a button
I've simplified the code for the above function for the sake of readability as it's not the focus of the problem. When the button is clicked, a new row is added successfully.
The problematic part involves another function that takes the sum of the respective classes of each row and displays them in a div.
The goal is to get the sum of the values of all input fields with matching class names. For example, let's say I use the addNewRow function to get six rows. Then I want to have the div showing the sum of the values of all input fields with the class name of "classA"; the number in that div should be the sum of those six values, which gets updated as I type in the values or change the existing values in any of the input fields with class name of "ClassA".
function sumValues(divId, inputClass){
var sumVal = document.getElementsByClassName(inputClass);
var addedUp = 0;
for (var j = 0; j < sumVal.length; j++){
addedUp += Number(sumVal[j].value);
}
document.getElementById(divId).innerHTML = addedUp;
}
Here are a couple (out of several) failed attempts:
document.input.addEventListener("keyup", sumValues("genericDivId", "classA"), false);
document.getElementsByClassName("classA").onkeyup = function(){sumValues("genericDivId", "classA");}
Unfortunately, after scouring the web for a solution and failing to find one, I just added an event listener to a button that, when clicked, would update the div to show the sum of values. Also had to modify the sumValues function to take values from an array rather than accepting arguments.
My question is: How can I modify the code so that the sum value updates as I type in new values or change existing values using pure Javascript (vanilla JS)?
You are very close, document.getElementsByClassName() returns an array of DOM objects, you need to set the onkeyup function for each and every element by looping through that array.
var classA = document.getElementsByClassName('classA'); // this is an array
classA.forEach(function(elem){ // loop through the array
elem.onkeyup = function(){ // elem is a single element
sumValues("genericDivId", "classA");
}
}
Hopefully this fixes your issue
Maybe the example below is not same with your situation, but you'll get the logic, easily. Anyway, do not hesitate to ask for more guide.
document.getElementById("row_adder").addEventListener("click", function() {
var t = document.getElementById("my_table");
var r = t.insertRow(-1); // adds rows to bottom - change it to 0 for top
var c = r.insertCell(0);
c.innerHTML = "<input class='not_important_with_that_way' type='number' value='0' onchange='calculate_sum()'></input>";
});
function calculate_sum() {
var sum = ([].slice.call(document.querySelectorAll("[type=number]"))).map(e=>parseFloat(e.value)).reduce((a, b) => a+b);
document.getElementById("sum").innerHTML = sum;
}
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div>
<p>
<strong>Sum</strong>:<span id="sum">0</span>
</p>
</div>
<button id="row_adder">
Click me
</button>
<table id="my_table">
</table>
</body>
</html>
I'm having a bit of an issue with some JS/JQuery. I am using some script to create an array from the data within the <TH> tags, then doing some formatting of that data to create new content and styles for a responsive table.
<script>
$( document ).ready(function() {
// Setup an array to collect the data from TH elements
var tableArray = [];
$("table th").each(function(index){
var $this = $(this);
tableArray[index] = $this.text();
});
console.log(tableArray);
alert(tableArray);
// Create class name based on th values and store as variable
var tableString = tableArray.join();
tableString = tableString.replace(/,/g, '_')
tableString = tableString.replace(/ /g, '-')
var tableClass = ".responsive-table."+tableString;
console.log(tableClass);
// Push tableClass variable into the table HTML element
var applyTableClass = tableClass;
applyTableClass = applyTableClass.replace(/\./gi, " ") //converts the style declaration into something i can insert into table tag (minus the dots!)
console.log(applyTableClass);
$( "table" ).addClass( applyTableClass );
// Create a loop which will print out all the necessary css declarations (into a string variable) based on the amount of TH elements
var i = 0;
var styleTag = "";
while (tableArray[i]) {
styleTag += tableClass+" td:nth-of-type("+[i+1]+"):before { content: '"+tableArray[i]+"'; }";
i++;
}
// Push the styleTag variable into the HTML style tag
$('style#jquery-inserted-css').html(styleTag);
// Below is just a test script to check that values are being collected and printed properly (use for testing)
//$('#css_scope').html('<p>'+styleTag+'</p>');
});
</script>
This works great when there is a single table within the page, but not if there is additional tables. The reason is that the loop that creates the array keeps going and does not know to stop and return at the end of one table, then create a new array for the next table. I am imagining that I need to set up a loop that creates the arrays as well.
This is where I am quit stuck with my limited scripting skills. Can anyone please suggest a way to get my code to loop through multiple tables, to create multiple arrays which then create separate style declarations?
You can loop through each table instead of querying all tables at once:
$( document ).ready(function() {
$("table").each(function () {
var tableArray = [];
$(this).find("th").each(function (index) {
var $this = $(this);
tableArray[index] = $this.text();
});
console.log(tableArray);
alert(tableArray);
// Create class name based on th values and store as variable
var tableString = tableArray.join();
tableString = tableString.replace(/,/g, '_')
tableString = tableString.replace(/ /g, '-')
var tableClass = ".responsive-table." + tableString;
console.log(tableClass);
// Push tableClass variable into the table HTML element
var applyTableClass = tableClass;
applyTableClass = applyTableClass.replace(/\./gi, " ") //converts the style declaration into something i can insert into table tag (minus the dots!)
console.log(applyTableClass);
$(this).addClass(applyTableClass);
// Create a loop which will print out all the necessary css declarations (into a string variable) based on the amount of TH elements
var i = 0;
var styleTag = "";
while (tableArray[i]) {
styleTag += tableClass + " td:nth-of-type(" + [i + 1] + "):before { content: '" + tableArray[i] + "'; }";
i++;
}
// Push the styleTag variable into the HTML style tag
$('style#jquery-inserted-css').append(styleTag);
// Below is just a test script to check that values are being collected and printed properly (use for testing)
//$('#css_scope').html('<p>'+styleTag+'</p>');
});
});
Note that I change $("table th") to $(this).find("th"), $("table") to $(this) and $('style#jquery-inserted-css').html(styleTag); to $('style#jquery-inserted-css').append(styleTag);.
Hope this help.
I have a tablesorter table that is generated dynamically with javascript and ajax. There are inputs for the user to change values, and when they click a button to go to the next or previous record, it needs to save the information in the table to a MySQL table. I have looked at many of the posts on here and tried numerous examples, and I still can't get any of the data in the table to post to my PHP page to save it.
Here is a sample of the first row of my table:
$("#guides").append('<tr><td id="amCodeOld">'+data.amCodeOld+'</td><td><input type="text" class="qty" id="amOldQty"/></td><td>'+data.amOldPrice+
'</td><td>'+data.am+
'</td><td>'+data.amCodeNew+'</td><td><input type="text" class="qty" id="amNewQty"/></td><td>'+data.amNewPrice+
'</td><td><input type="checkbox" id="amS" '+
'"/></td><td><input type="checkbox" id="amR"'+'"/></td><td><input type="checkbox" id="amCall"'+
'"/></td><td><input type="text" id="amComm" value="'+
'"/></td></tr>');
There are more rows like this one, each for a different guide I am using. What I need to find is the value of the inputs with id of amOldQty, amNewQty etc, and the different checkboxes.
I have buttons for Next, Prev, and Print, and when the button is clicked I am trying to call a function called save() that will get the info, and send it via ajax to another PHP page to be saved.
save() looks like this:
function save() {
var amOldQty
$('#guides tr').each(function(){
alert("searching ");
amOldQty= $(this).find('#amOldQty').val();
if (amOldQty=='' || amOldQty== null) {
amOldQty = "Not Showing Number";
}
console.log(amOldQty);
});// END OF EACH FUNCTION
I have tried:
var amOldQty;
$('#guides tbody tr').each(function() {
amOldQty = $(this).find('td:nth-child(1) input').val();
});
console.log(amOldQty);
and the log shows undefined. I have also tried a more direct approach by using
$('#guides tbody tr').each(function() {
amOldQty = $(this).find('#amOldQty').val();
});
and still have nothing. I am getting the alert of Searching, but in the console.log(amQtyOld) all it shows me is "Not Showing a number". I have even tried to have the input populated when the table first is created, and it still does not find that number.
Update if I add td to:
$(guides tr td).each(function(){...
It does not even give me the alert of "Searching".
How do I get all the data out of this table so I can save it? it seems that everything I am trying should be working.
If your users use browsers that support contenteditable elements, then you can check out the contenteditable widget I have available for my fork of tablesorter.
If you don't want to use contenteditable elements, then you can try the following code (copied and modified from the contenteditable demo, but not tested)
var $table = $('table');
$table.children('tbody').on('change', 'input', function(event){
var $input = $(this),
$cell = $input.closest('td'),
newContent = $input.val(),
cellIndex = $cell[0].cellIndex, // there shouldn't be any colspans in the tbody
rowIndex = $this.closest('tr').attr('id'); // unique row id
// update tablesorter
$table.trigger("updateCell", [$cell]);
$.post("mysite.php", {
"row" : rowIndex,
"cell" : cellIndex,
"content" : newContent
});
});
Hopefully, you're also using an input parser for that column.
The final answer seems to be to just hardcode the value of the inputs $("#amOldQty:).val() and pass them as data through ajax to my php file to save the information. Here is the completed code in case anyone has a similar issue:
function save() {
function GuidesSave(){
this.update();
}
GuidesSave.prototype.list = ['am','fol','tich','book','neb','ster','c','byte','ing'];
GuidesSave.prototype.update = function(){
for( var i = 0 ; i < this.list.length ; i++ ){
var guide = this.list[i];
this[ guide + 'S' ] = $("#" + guide+'S' ).is(":checked") ? 1 : 0;
this[ guide + 'R' ] = $("#" + guide+'R' ).is(":checked") ? 1 : 0;
this[ guide + 'Call' ] = $("#" + guide+'Call' ).is(":checked") ? 1 : 0;
}// end of for loop
}
var guides = new GuidesSave();
$.ajax({
type: "POST",
url: "poSave.php",
dataType: "json",
data: ({po: $('#po').val(),isbn:$("#isbn13").val(),amOldQty:$("#amOldQty").val(),amNewQty:$("#amNewQty").val(),amS:guides.amS,amR:guides.amR, amCall:guides.amCall,amComm:$("#amComm").val(),
folOldQty:$("#folOldQty").val(),folNewQty:$("#folNewQty").val(),folS:guides.folS,folR:guides.folR, folCall:guides.folCall,folComm:$("#folComm").val(),
tichOldQty:$("#tichOldQty").val(),tichNewQty:$("#tichNewQty").val(),tichS:guides.tichS,tichR:guides.tichR, tichCall:guides.tichCall,tichComm:$("#tichComm").val(),
bookOldQty:$("#bookOldQty").val(),bookNewQty:$("#bookNewQty").val(),bookS:guides.bookS,bookR:guides.bookR, bookCall:guides.bookCall,bookComm:$("#bookComm").val(),
nebOldQty:$("#nebOldQty").val(),nebNewQty:$("#nebNewQty").val(),nebS:guides.nebS,nebR:guides.nebR, nebCall:guides.nebCall,nebComm:$("#nebComm").val(),
sterOldQty:$("#sterOldQty").val(),sterNewQty:$("#sterNewQty").val(),sterS:guides.sterS,sterR:guides.sterR, sterCall:guides.sterCall,sterComm:$("#sterComm").val(),
cheggOldQty:$("#cOldQty").val(),cheggNewQty:$("#cNewQty").val(),cheggS:guides.cS,cheggR:guides.cR, cheggCall:guides.cCall,cheggComm:$("#cComm").val(),
byteOldQty:$("#byteOldQty").val(),byteNewQty:$("#byteNewQty").val(),byteS:guides.byteS,byteR:guides.byteR, byteCall:guides.byteCall,byteComm:$("#byteComm").val(),
ingOldQty:$("#ingOldQty").val(),ingNewQty:$("#ingNewQty").val(),ingS:guides.ingS,ingR:guides.ingR, ingCall:guides.ingCall,ingComm:$("#ingComm").val(),
qty1: $('#topqty').val(),price1: $('#topVal').html(),comp1:$('#topCo').html(),
qty2: $('#secqty').val(),price2: $('#secVal').html(),comp2: $('#secCo').html(),
qty3: $('#thrqty').val(),price3: $('#thrVal').html(),comp3: $('#thrCo').html()}),
success: function(data){
}
});
}// END OF SAVE FUNCTION
The GuideSave function loops through all the check boxes (27 different ones) to see whichones are checked so I can save them as either a 1 or 0 and then have them checked or not when the record is recalled.
It is not really clear what or how you are wanting to present the data. However, here is a JSFiddle that does what it appears you desire.
The function createRowsForTesting() just creates the rows in the table and populates the <input> fields to make testing easier.
The function getDataFromTable() walks the rows of a <table>, or all <tables>, sending the data you said you wanted to the console. From your own answer to this question it became clear that you really wanted to access many more <input> elements than was mentioned in your question. Thus, this this function now builds an array of Objects which contain key:value pairs for the data in <input> fields. There is one Object in the array for each row. The array is returned to the calling function.
The function wrapGetDataFromTable() wraps getDataFromTable() providing the table ID, that we are only looking in rows within the <tbody> tag and that we want output to the console. The array returned by getDataFromTable() is output so we can see the data structure. The function is set up to run each time you press the [print to console] button.
For a table that looks like (with header row):
The output is:
amOldQty=amOldQty0_text amNewQty=amNewQty0_text amS=on amR=off amCall=off amComm=amComm0_text
amOldQty=amOldQty1_text amNewQty=amNewQty1_text amS=off amR=on amCall=off amComm=amComm1_text
amOldQty=amOldQty2_text amNewQty=amNewQty2_text amS=off amR=off amCall=on amComm=amComm2_text
The array of row input data objects is:
[0] Object { amOldQty="amOldQty0_text", amNewQty="amNewQty0_text", amS="on", more...}
amOldQty: "amOldQty0_text"
amNewQty: "amNewQty0_text"
amS: "on"
amR: "off"
amCall: "off"
amComm: "amComm0_text"
[1] Object { amOldQty="amOldQty1_text", amNewQty="amNewQty1_text", amS="off", more...}
amOldQty: "amOldQty1_text"
amNewQty: "amNewQty1_text"
amS: "off"
amR: "on"
amCall: "off"
amComm: "amComm1_text"
[2] Object { amOldQty="amOldQty2_text", amNewQty="amNewQty2_text", amS="off", more...}
amOldQty: "amOldQty2_text"
amNewQty: "amNewQty2_text"
amS: "off"
amR: "off"
amCall: "on"
amComm: "amComm2_text"
JavaScript:
/**
* Runs through a table getting the values from <input> fields.
* It only looks in the <tbody>, not the <thead>
* #param tableId
* The DOM ID of the table from which we desire to obtain the
* input values.
* If tableId is not a string, then look in all table rows in all tables.
* #param keyAttr
* The attribute of the <input> which contains the value which will
* be used as the key for the key:value pair within the Object returned.
* This needs to be a value which is unique, wihin the table row.
* A normal use would be "id".
* If a value is duplicated a message is sent to the console and only
* the last value is kept.
* The default is "id".
* #param justBody
* If true, look only within the <tbody> tag, not any other part of
* the table (e.g. <thead>).
* The default is true.
* #param includeBlank
* Boolean indicating if the returned array should contain an entry for
* rows which are found to be blank.
* The default is true.
* #param consoleOutput
* Send a line to the console with the key:value pairs separated by
* tabs for each row.
* The default is false.
* #return Object
* Returns an Array of Objects with key:value pairs for the rows.
* If there were no <input>
* Copyright 2014 by Makyen.
* Released under the MPL 2.0. http://mozilla.org/MPL/2.0/.
*/
function getDataFromTable(tableId, keyAttr, justBody, includeBlank, consoleOutput) {
//This assumes that within the row each input has a unique attribute keyAttr.
//Set defaults:
var tableSelector = (typeof tableId === "string") ? "#" + tableId : "table";
keyAttr = (typeof keyAttr === "string") ? keyAttr : "id";
includeBlank = (typeof includeBlank === "boolean") ? includeBlank : true;
justBody = (typeof justBody === "boolean") ? justBody : true;
consoleOutput = (typeof consoleOutput === "boolean") ? consoleOutput : false;
var bodySelector = (justBody) ? " tbody" : "";
var toReturn = [];
var selector = tableSelector + bodySelector + ' tr';
$(selector).each(function () {
var inputs = {};
$(this).find('input').each(function () {
//Get the value for all inputs on this line.
var attrValue = $(this).attr(keyAttr);
if (typeof inputs[attrValue] !== "undefined") {
console.log("Warning: When attempting to get data from the table id=" //
+ tableId + " the value of the key attribute, " + keyAttr //
+ ", was not unique for value=" + attrValue);
}
//Get the value of the <input>.
if ($(this).is(':checkbox')) {
//Special case the checkboxes because .val() does not return
//the correct informaiton for them.
//First indicate that all checkboxes are off.
inputs[attrValue] = "off";
//Specifically determine if the current one is checked.
if ($(this).is(':checked')) {
inputs[attrValue] = "on";
}
} else {
//Add this input to the object
inputs[attrValue] = $(this).val();
}
});
var inputKeys = Object.keys(inputs);
if (inputKeys.length > 0) {
//There were <input> tags on this row.
var outputText = "";
if (consoleOutput) {
inputKeys.forEach(function (value) {
outputText += value + "=" + inputs[value] + "\t";
});
console.log(outputText);
}
toReturn.push(inputs);
} else {
//No <input> tags on this row
if (includeBlank) {
if (consoleOutput) {
console.log("A row without <input> tags was found.");
}
toReturn.push(inputs);
}
}
});
return toReturn;
}
function wrapGetDataFromTable() {
//This wraper is so the getDataFromTable() function remains
// generic. The wrapper merely defines which table from which to
// get the data,
// the attribute to use for unique keys = "id"
// to look only in the <tbody>
// to not include an object for the lines which are blank
// and output the row data to the console.
var toReturn = getDataFromTable("guides", "id", true, false, true);
if (typeof console.dir === "function") {
//Make sure console.dir() exists prior to using it.
console.log("The array of row input data objects is:");
console.dir(toReturn); //Let us see the Object in the console for checking.
}
return toReturn;
}
$('#to-console-button').click(wrapGetDataFromTable);
//The rest is setup for creating the table header and rows.
//It is only for testing.
function createRowsForTesting() {
const numRowsToCreate = 3;
var i;
var data = {
amCodeOld: "amCodeOld",
amOldPrice: "amOldPrice",
am: "am",
amCodeNew: "amCodeNew",
amNewPrice: "amNewPrice"
};
//Create the table
//First add a header.
$("#guides thead").append('<tr><th>amCodeOld_H</th>' //
+ '<th>amOldQty_H</th>' //
+ '<th>amOldPrice_H</th>' //
+ '<th>am_H</th>' //
+ '<th>amCodeNew_H</th>' //
+ '<th>amNewQty_H</th>' //
+ '<th>amNewPrice_H</th>' //
+ '<th>amS_H</th>' //
+ '<th>amR_H</th>' //
+ '<th>amCall_H</th>' //
+ '<th>amComm_H</th></tr>');
//Now the body rows.
for (i = 0; i < numRowsToCreate; i++) {
//From stackoverflow question: http://stackoverflow.com/questions/25998929/extract-data-from-a-tablesorter-table-with-javascript
$("#guides tbody").append('<tr><td id="amCodeOld">'+data.amCodeOld+'</td><td><input type="text" class="qty" id="amOldQty"/></td><td>'+data.amOldPrice+ //
'</td><td>'+data.am+ //
'</td><td>'+data.amCodeNew+'</td><td><input type="text" class="qty" id="amNewQty"/></td><td>'+data.amNewPrice+ //
'</td><td><input type="checkbox" id="amS" '+ //
'"/></td><td><input type="checkbox" id="amR"'+'"/></td><td><input type="checkbox" id="amCall"'+ //
'"/></td><td><input type="text" id="amComm" value="'+ //
'"/></td></tr>');
}
//*
//Fake having the table filled in, as I am tired of entering the input
//You have to try it without this, but with manual input in order to truly verify
var row = 0;
$('#guides tbody tr').each(function () {
$(this).find('#amOldQty').val("amOldQty" + row + "_text");
$(this).find('#amNewQty').val("amNewQty" + row + "_text");
$(this).find('#amComm').val("amComm" + row + "_text");
row++;
});
//*/
}
createRowsForTesting();
HTML:
<table class="tablesorter" id="guides">
<thead></thead>
<tbody></tbody>
</table>
<button type="button" id="to-console-button">print to console</button>
<!-- values to use to fill input:
amOldQty0_text
amOldQty1_text
amNewQty0_text
amNewQty1_text
amComm0_text
amComm1_text -->
Note: The selector ($("#guides").append('<tr>...) used in your line to add the table row to the table might be your problem. It currently adds your rows as the last elements in the <table>, not the last in the <tbody>. While the browser should compensate for this, it is possible that it is not doing so in your environment. Try $("#guides tbody").append('<tr>...
However, it appears more likely that the issue is a header row (or a row without input cells) in the <tbody>. The code now accounts for this possibility.
Below is a function where it controls whatever happens after a file has finished uploading in its own table row. Each table row consists of a file input where the user can upload a file and then the name of the file is appended within it's own table row.
If the upload was successful then it displays a successful message, if upload was not successful then it displays a message stating there is an error. But I also have another function within the function where the user can delete a file by clicking on the "Delete" button. The only problem I have is with this line of code:
$(".imagemsg" + counter).html(data);
Let's say that I have 2 table rows, and I delete a file in the first row, the message within .imagemsg should only be displayed in the first row as that was the row the deletion occured, it shouldn't display the message in the first and second row.
Another example is that if I have 4 table rows and I delete the file in the third row, then the message should be displayed in the 3rd row as that is where the deletion has occured.
So my question is what do I need to add to $(".imagemsg" + counter).html(data); so that the message is only displayed within the row the deletion of the file occured and not in all .imagemsg which is in every row?
Below is full code:
function stopImageUpload(success, imagefilename){
var result = '';
var counter = 0;
counter++;
if (success == 1){
result = '<span class="imagemsg'+counter+'">The file was uploaded successfully!</span><br/><br/>';
$('.listImage').eq(window.lastUploadImageIndex).append('<div>' + htmlEncode(imagefilename) + '<button type="button" class="deletefileimage" image_file_name="' + imagefilename + '">Remove</button><br/><hr/></div>');
}
else {
result = '<span class="imageemsg">There was an error during file upload!</span><br/><br/>';
}
$(".deletefileimage").on("click", function(event) {
var image_file_name = $(this).attr('image_file_name');
jQuery.ajax("deleteimage.php?imagefilename=" + image_file_name)
.done(function(data) {
$(".imagemsg" + counter).html(data);
});
$(this).parent().remove();
});
return true;
}
BELOW IS HTML CODE:
var $fileImage = $("<form action='imageupload.php' method='post' enctype='multipart/form-data' target='upload_target' onsubmit='return imageClickHandler(this);' class='imageuploadform' >" +
"Image File: <input name='fileImage' type='file' class='fileImage' /></label><br/><br/><label class='imagelbl'>" +
"<input type='submit' name='submitImageBtn' class='sbtnimage' value='Upload' /></label>" +
"</p><p class='listImage' align='left'></p>" +
"<iframe class='upload_target' name='upload_target' src='#' style='width:0;height:0;border:0px;solid;#fff;'></iframe></form>");
I believe that your counter variable will always be 1. So, all your span.imagemsg1 are the same. This is why you get the message in every row. Set the counter outside the function to increment the counter.
I believe that will stop the behavior that you are seeing, but I would like to give a shout out to the other answers as they are giving good advice to cleaning this code up.
Frankly, you should never use unique identifier in the class. Why not use an id or a data-image-count attribute?
In your html code you'll need to add a unique identifier, I would suggest using id. This way when you try to reference the element to add the error message in, it will only find one element. Currently it's looking for the first occurrence of the element with class = "imagemsg". You'll need a way to loop through each "row" and make the id's "imagemgs1", "imagemsg2", etc...Hope it helps.
It would be helpful to be able to see the HTML. Also, I cannot see in your script what you do with the "result" value. At this stage, I personally don't think there is enough info to help satisfactorily you yet.
However, an issue you will undoubtedly see is with your "counter" variable. Maybe that is your problem - hard to tell without the detail I asked for above. Your jQuery.ajax call will complete at some point but the value of "counter" may not be the same as when you called the jQuery.ajax() method. This is because the "counter" variable is being declared in a different scope.
E.g. Look at the code below. It sort of demonstrates your problem with the counter variable. It may look like at the end of 5 seconds it will spit out the numbers from 1 to 10 but it won't. It will spit out the value "10" ten times.
var x = 0;
for (var i = 0; i < 10; i++)
{
x++;
setTimeout(function() { console.log(x); }, 5000);
}
This problem applies to your code as well. You can fix the above by copying the variable value in to a variable of your local scope. E.g.:
var x = 0;
for (var i = 0; i < 10; i++)
{
var newScope = function() {
x++;
var y = x;
setTimeout(function() { console.log(y); }, 5000);
}();
}
I have a delete function within a function where if the user clicks on the "Delete" button, it displays a message stating that a file has been deleted. The code which does this is below:
$("#imagemsg").html(data);
But the problem is that let's say that I have 4 table rows and I delete a file in the 3rd row, the message should be displayed in the 3rd row only but instead it is displayed in the first row. Another example is that let's say that I have 8 table rows and I delete a file in the 6th row, the message should be displayed in the 6th row only but instead it is displayed in the first row.
Why is the message that is suppose to appear after a file is deleted is always displayed in the first row and not within the row the file has been deleted from?
Below is full code:
var counter = 0;
counter++;
function stopImageUpload(success, imagefilename){
var result = '';
if (success == 1){
result = '<span id="imagemsg'+counter+'">The file was uploaded successfully!</span><br/><br/>';
$('.listImage').eq(window.lastUploadImageIndex).append('<div>' + htmlEncode(imagefilename) + '<button type="button" class="deletefileimage" image_file_name="' + imagefilename + '">Remove</button><br/><hr/></div>');
}
else {
result = '<span id="imageemsg">There was an error during file upload!</span><br/><br/>';
}
$(".deletefileimage").on("click", function(event) {
var image_file_name = $(this).attr('image_file_name');
jQuery.ajax("deleteimage.php?imagefilename=" + image_file_name)
.done(function(data) {
$("#imagemsg" + counter).html(data);
});
$(this).parent().remove();
});
return true;
}
Below is the deleteimage.php script where the delete message is retrieved from:
<?php
$image_file_name = $_GET["imagefilename"];
echo "$image_file_name was Deleted";
unlink("ImagesFilesFolder/$image_file_name");
?>
The problem seems to be this:
.done(function(data) {
$("#imagemsg" + counter).html(data);
You set counter like this
var counter = 0;
counter++;
But you never seem to refer to the variable again. In any case, this variable is global - the command above will always target the ID with the current number of the counter, so it will not target the tr corresponding to the clicked button.
Since you use
$(this).parent().remove();
I assume that the parent is the tr concerned? In this case you could use a class instead of an ID 'imagemsg' and then do
$(this).parent().find(".imagemsg").html(data);
This would target the message inside the same row of the button.