How to stop the table from being built again - javascript

I've got a lovely table function from josh.trow # link that builds a 4x4 table which can be filled with color. The only problem is is that it rebuilds itself every time I call the function (hence the first time I call the file I see a 4x4 table, the second time I see a 8x4 table, and so on.) It doesn't multiply the rows because they are already limited to 4.
I can't see what check I need to put in that limits the table's columns to only being built once. (4)
Here's the JS code:
function repeatedFunction(L, G, P, D) {
jQuery.fn.reverse = [].reverse;
var completeData = [L, G, P, D];
var max = 4;
$.each(completeData, function(intIndex, objValue) {
if (objValue > max) max = objValue;
});
for (var i = 0; i < max; i++)
{
$('#statSheetTable').append('<tr id="resultRow' + i + '"></tr>');
$.each(completeData, function(intIndex, objValue) {
$('#resultRow' + i).append('<td name="column' + intIndex + '" ></td>');
});
}
$.each(completeData, function(intIndex, objValue) {
$('td[name=column' + intIndex + ']').reverse().each(function(idx, val) {
if (idx < objValue) $(this).addClass('complete' + intIndex);
});
});
}
I tried using a global variable, which stopped the table from being duplicated, but then my color filling code was not functioning (as it can't be refreshed with the new instructions to fill with color).
Essentially what I added to the above function was:
var firstTime = true;
.....
function repeatedFunction(L, G, P, D, 4) {
if(firstTime == true)
{
.....
// code from above block
.....
firstTime = false;
}
Do your experienced JS eyes see where I can limit the table from building more columns?

Here you go chief, updated as you asked.
EDIT: Whoops, now it works :)
http://jsfiddle.net/X83ya/2/

First, always use identical comparison operators when comparing boolean values:
if(firstTime === true)
That being said, you don't need it if you just want to rebuild your table. $('#statSheetTable').append(... will just "add to" whatever was in there. Make sure you clear it out in the beginning like such:
...
var max = 0;
$('#statSheetTable').html('');
...

Related

Clear Row Data Based on Value in Google Sheet ( Specific Data ) appscript

I am looking for a base on value clear specific row content/data and then hide row.
type value "Done" in { F Col } Clear Value of { E , C Col }
when everything is done then hide row.
function ClearContentThenHideRow() {
var s = SpreadsheetApp.getActive().getSheetByName('Main');
s.showRows(1, s.getMaxRows());
s.getRange('F:F')
.getValues()
.forEach( function (r, i) {
if (r[0] == 'Done')
//sheet.getRange(["C:C","E:E"]).clearContent(); // clear content not working
s.hideRows(i + 1);
});
}
Modification points:
About sheet.getRange(["C:C","E:E"]).clearContent();,
sheet is not declared.
When you want to use the range list, please use getRangeList().
When s.getRange('F1:F' + s.getLastRow()) is used instead of s.getRange('F:F'), I thought that the process cost will be able to be reduced a little.
When above points are reflected to your script, it becomes as follows.
Modified script:
function ClearContentThenHideRow() {
var s = SpreadsheetApp.getActive().getSheetByName('Main');
// s.showRows(1, s.getMaxRows()); <--- removed
s.getRange('F1:F' + s.getLastRow())
.getValues()
.forEach(function (r, i) {
if (r[0] == 'Done') {
s.getRangeList(["C" + (i + 1), "E" + (i + 1)]).clearContent();
s.hideRows(i + 1);
}
});
}
Reference:
getRangeList(a1Notations) of Class Sheet

Dynamically adding multiple rows to a datatable

I see similar questions have been asked before but I cannot find the answer I'm after.
My question is, how do I add multiple rows of data at the same time, I have a working example below which will slowly add 1000 rows using the row.add() but I cannot for the life of me work out how to add these rows in one batch using the rows.add()
$('#addRow').on( 'click', function () {
for (i =0; i < 1000; i++) {
r = [i+'.1', i+'.2', i+'.3', i+'.4', i+'.5', i+'.6', i+'.7'];
mytable.row.add( [ r ] ).draw( false );
}
});
I have gone through all the examples on the web I can find but all of the examples I have found work using a set amount of predefined data, not how to handle an unknown number of rows.
Any help would be greatly appreciated.
Regards, Chris
Without a proper example, I am unable to test the results. Consider the following code.
function addRows(n, tbl) {
var lastRow = tbl.row(":last");
var index = lastRow.index();
for (var i = (index + 1); i < (index + n); i++) {
r = [i + '.1', i + '.2', i + '.3', i + '.4', i + '.5', i + '.6', i + '.7'];
tbl.row.add(r).draw(false);
}
}
$('#addRow').on('click', function() {
addRows(1000, mytable);
});
Assuming you have an amount of data in place, you want to find the last row in the Table and then build on that. You cannot build rows with an infinite number of rows, but you can use a dynamic variable amount. You can also build an Object of Rows and then use table.rows.add().
See More:
https://datatables.net/reference/api/rows()
https://datatables.net/examples/api/add_row.html
https://datatables.net/reference/api/rows.add()
OK I've got it working, so if anyone else gets stuck on this same issue, this is my code now :
$('#addRow').on( 'click', function () {
var arrayAll = [];
for (i =0; i < 1000; i++) {
var arrayRow = [i+'.1', i+'.2', i+'.3', i+'.4', i+'.5', i+'.6', i+'.7'];
arrayAll = arrayAll.concat([arrayRow]);
}
t.rows.add( arrayAll ).draw();
} );
This will add a 1000 rows within a second :)

Populate a prompt with elements of an array and number them off

(Stack Overflow doesn't have a tag for 'prompt' so I have used alert as I am guessing it is similar enough to attract the right answerers.)
Hello,
I am currently making a JavaScript-based game for an assignment at university. I am usually pretty good with problem solving but have been stumped by this issue.
To explain, I have an array which names the possible armour slots the player can pick. In any order these can be picked, and each time the choice gets pushed to a second array which handles what has already been picked (and in what order) and that item gets spliced from the original array. There is a while loop which runs through until all 3 have been picked.
var armourSlotToPick = ["Head", "Chest", "Legs"],
armourSlotPicked = [],
armourLoop = 1,
indexArmour = 0;
function numInArray() {
indexArmour++;
return (indexArmour + ". " + armourSlotToPick[indexArmour - 1] + "\n");
}
function armour() {
while (armourLoop < 4) {
var armourPick = prompt("Pick an armour slot to generate an item for:\n" + armourSlotToPick.forEach(numInArray));
if (armourPick == 1) {
armourSlotPicked.push(armourSlotToPick[0]);
armourSlotToPick.splice(0,1);
} else if (armourPick == 2) {
armourSlotPicked.push(armourSlotToPick[1]);
armourSlotToPick.splice(1,1);
} else if (armourPick == 3) {
armourSlotPicked.push(armourSlotToPick[2]);
armourSlotToPick.splice(2,1);
} else {
alert("Invalid choice, you suck");
break;
}
armourLoop++;
}
}
I know it probably wouldn't be possible to do the whole return in numInArray() to the prompt, but it shows some working.
Now the problem: I got it working so that each item in the array was numbered (var armourSlotToPick = ["1. Head", "2. Chest", "3. Legs"],) but as you could see, if the player chose 2, then the next time it would show "1. Head (new line) 3. Legs" and when the player chooses 3, a problem would occur, as they were really meant to choose 2. How is it possible to number the items in the array, in a prompt?
I'm possibly over thinking this but I have suffered for a few hours now.
I thank you in advance for any insight you may have,
Daniel.
EDIT: Solved.
Below is the end result, a slight variation from the edited answer from Jonathan Brooks.
var armourSlotToPick = [null, "Head", "Chest", "Legs"]
var armourSlotPicked = [null];
var armourLoop = 1;
function armour() {
while (armourLoop < 4) {
var message = "Pick an armour slot to generate an item for:\n";
for (var i = 0; i < armourSlotToPick.length; i++) {
if (armourSlotToPick[i] !== null) {
message += "" + i + ". " + armourSlotToPick[i] + "\n";
}
}
var armourPick = prompt(message);
if (armourPick > armourSlotToPick.length-1 || armourPick < 1) {
alert("Invalid choice, you suck");
} else {
var insert = armourSlotToPick.splice(armourPick, 1);
armourSlotPicked.push(insert);
}
armourLoop++;
}
armourSlotPicked.splice(0,1);
}
armour();
alert(armourSlotPicked.join("\n"));
I thank all that have contributed to this discussion and the end result, and I hope this is a good example for future problems people may have similar to this.
Check out my fiddle, I think I have a working solution.
What you really want to be using are Object Literals with your own indexing (starting from 1) - if it were me, I would create my own way to iterate over this custom indexing by adding a method to the Object's prototype, but I digress.
You're overcomplicating your code by using a while loop, and that large bulk of if statements is unnecessary: instead, all you need is some basic validation on the input and then you can just trust whatever input passes this validation. That is demonstrated here:
if ( armourPick > armourSlotToPick.length || armourPick < 1 ) {
alert("Invalid choice, you suck");
}
else {
armourSlotPicked.push( armourSlotToPick[armourPick-1] )
alert (armourSlotPicked[armourSlotPicked.length-1].value);
}
Read my code carefully, and you should get a better understanding of how to deal with certain issues.
EDIT:
As per your request, I think I have a solution that suits your needs. Basically all you have to do to have the arrays "start" at an index of 1 is to fill the zeroth element with a null value, like so:
var armourSlotToPick = [null, "Head", "Chest", "Legs"]
var armourSlotPicked = [null];
You just have to remember to take this null object into account in your code, for example:
if (armourSlotToPick[i] !== null) {
message += "" + i + "\n";
}
The indices will update automatically. See this updated fiddle for more details.
use structures / objects as content in the array, instead of just values.
the basic concept:
armourSlotPicked.push({ "key": 1, "value":armourSlotToPick[1]})
alert("value: " + armourSlotPicked[0].value)
alert("key: " + armourSlotPicked[0].key)
edit: responding to comments can take some space.
IMHO a prompt is the completely wrong tool for this, since most browsers would ask the user permission to prevent multiple popups, and since a promt can only return 1 piece of information, you can only ask for 1 thing per popup. Instead you ought to use a div element, with checkboxes for each information..
That being said it can easily be used in a promt.
The prompt is just a built in function, that takes a string as an argument (which is shown as text in the popup) and returns a string with the users input.
what does the magic for you is in fact this:
array.foreach(): The forEach() method executes a provided function once per array element.
in your case that means it calls a function that returns a string for each element in the array, and concatenates the strings.
in the old days you would have written this:
var messageText= "Pick an armour slot to generate an item for:\n"
for(var i = 1; i < armourSlotToPick.length; i++){
messageText += i + ". " + armourSlotToPick[i- 1] + "\n";
}
var armourPick = prompt(messageText);
but in this modern age, you define a printing function, and use it to generate the loop:
function numInArray() {
indexArmour++;
return (indexArmour + ". " + armourSlotToPick[indexArmour - 1] + "\n");
}
//more code before we get to where the function is used....
indexArmour = 0;
var messageText = "Pick an armour slot to generate an item for:\n" + armourSlotToPick.forEach(numInArray);
var armourPick = prompt(messageText);
or in a single line as in your code:
indexArmour = 0; //you forgot this - otherwise the list will only be complete once?
var armourPick = prompt("Pick an armour slot to generate an item for:\n" + armourSlotToPick.forEach(numInArray));
It produces the same output, because it does the same thing, its just written very differently!
If the array holds "object literals" instead of simply values, as I suggest, the old fashioned code would look something like this:
function contains(a, value) {
try{
for (var i = 0; i < a.length; i++) {
if (a[i].value == value) {
return true;
}
}
}
catch(err) {
// do nothing
};
return false;
}
and later..
for(var j = 0; j < 4; j++){
for(var i = 0; i < Math.min(armourSlotToPick.length); i++){
if( contains(armourSlotPicked, armourSlotToPick[i- 1]) )
continue;
var messageText = "Generate an item for armour in slot: " + i + "\n"
messageText += armourSlotToPick[i- 1] + "\n";
}
var armourPick = prompt(messageText);
if (armourPick > 0 && armourPick < armourSlotToPick.length) {
armourSlotPicked.push({"key":j, "value":armourSlotToPick[armourPick]);
}
...
}
//now we have an array that holds information about when what was picked..
or something along those lines.. this is bt.w completely untested, it's just for illustration
You want to use the array index to number your items. Since your numbers are one-based and the index is zero-based, you will need to convert between the two when outputting and interpreting the response.
This approach will also allow you to eliminate all but two of the cases in your if-else statement.

Number value wrong in event bound through a for loop

var rows = document.getElementsByClassName('row');
for (var i = 0, l = rows.length; i < l; i++) {
if (i % 2 === 0) {
$(rows[i]).click(function () {
alert('I am line number ' + i);
}
}
}
Hi,
how would I get actual line number for each row ? since all I get when I trigger click event on an even row, upper bound value is alerted (i.e: rows.length = 7, i would be 6 for each row clicked).
The problem is that upon click event is triggered, the i variable was already changed by the loop iteration. Theoretically you can use closure to make things working, i.e.
for (var i = 0, l = rows.length; i < l; i++) {
if (i % 2 === 0) {
(function(i) {
$(rows[i]).click(function() {
alert("I am line number " + i);
});
)(i);
}
}
Practically, if you use jQuery (as I understood from the code), it is easier to use :even selector:
$(".row:even").click(function() {
alert("I am line number " + $(".row").index(this));
});
The reason you're getting the wrong number is that the event handler functions you're creating get an enduring reference to the i variable, not a copy of it as of when they're created.
The way to solve it is to have the handler close over something that won't change. Here are three ways to do that, the first is specific to jQuery (it looks like you're using jQuery):
jQuery's each
It looks like you're using jQuery, in which case you can use its each to get an index to use that won't change:
var rows = $(".row");
rows.each(function(index) {
if (index % 2 === 0) {
$(this).click(function() {
alert('I am line number ' + index);
});
}
});
Now, the event handler function closes over the index argument of the call to the function we give each, and since that index argument never changes, you see the right number in the alert.
Use a builder function
(Non-jQuery) You can solve this with a builder function:
var rows = document.getElementsByClassName('row');
for (var i = 0, l = rows.length; i < l; i++) {
if (i % 2 === 0) {
$(rows[i]).click(buildHandler(i));
}
}
function buildHandler(index) {
return function () {
alert('I am line number ' + index);
};
}
Here, the event handler function closes over the index argument in buildHandler, and since that index argument never changes, you see the right number in the alert.
forEach
(Non-jQuery) You can also use ES5's forEach function (which is one of the ES5 features you can shim on a pre-ES5 environment) to solve this:
var rows = document.getElementsByClassName('row');
Array.prototype.forEach.call(rows, function(row, index) {
if (index % 2 === 0) {
$(row).click(function () {
alert('I am line number ' + index);
});
}
});
This works the same way as the two above, by closing over index, which doesn't change.

Show/Hide image before filtering a table isn't working

I have web page with some really large tables that I'm filtering using some jquery routines I wrote. Anyway, when these tables get really large and the filtering functions can take some time to complete. So I figured I'd unhide a animated gif so the user had some feedback. However, the gif never appears when I call:
$('#loadingimg').show();
Unless I put an alert statement in front of it. I apologize for the ugly code, I'm not an experienced jquery/javascript programmer.
function filter()
{
var eles = ["mtmprogram","rate","stage"];
var tag;
var classes='';
$('#loadingimg').show();
//alert('hi');
$('.report').hide();
for (var i in eles)
{
tag = '#' + eles[i] + ' option:selected';
if ($(tag).val())
{
//$('.'+ $(tag).val()).show();
classes = classes + '.' + $(tag).val();
}
}
if (classes == '')
$('tr.report').show();
else
$(classes).show();
filterSubtables('Loan Number');
$('#loadingimg').hide();
}
Many thanks!
Maybe you aren't giving the #loadingimg element enough time to display. You could test this by running the rest of your code in a timeout:
function filter()
{
var eles = ["mtmprogram","rate","stage"],
classes = '';
$('#loadingimg').show();
//alert('hi');
setTimeout(function () {
$('.report').hide();
for (var i = 0, len = eles.length; i < len; i++)
{
var $tag = $('#' + eles[i] + ' option:selected');
if ($tag.val())
{
//$('.'+ $tag.val()).show();
classes = classes + '.' + $tag.val();
}
}
if (classes == '')
$('.report').show();
else
$(classes).show();
filterSubtables('Loan Number');
$('#loadingimg').hide();
}, 500);
}
Notice that I changed how the tag variable is used (this creates less CPU overhead to make less jQuery selections and to use as local a variable as possible). I also changed your loop to a better format that performs amazingly faster than for ( a in b ): http://jsperf.com/jquery-each-vs-for-loops/2

Categories

Resources