JavaScript table overwriting html file - javascript

I have an assignment to write JavaScript which will create a table(x columns, y rows...) and I managed to do that. I have a problem with writing to the html page.
My code:
document.write('<table border = \"3\">');
while(i <= y)
{
document.write('<tr>');
j = 0;
while(j <= x)
{
if (i == 0)
{
if (j == 0)
{
document.write(' <th></th> ');
}
else
{
document.write('<th>');
document.write("x = " + j);
document.write('</th>');
}
}
else
{
if (j == 0)
{
document.write('<th>');
document.write("y = " + i);
document.write('</th>');
}
else
{
document.write('<td>');
document.write(operation(i,j));
document.write('</td>');
}
}
j++;
}
document.write('</tr>');
i++;
}
document.write('</table>');
}
I guess it is overwriting the page because of document.write? How can I change this?

Please don't use document.write. See Why is document.write discouraged.
I think your use of document.write means it overwrite's the entire page. This is working as intended if you call the function after the page has loaded. All calls to document.write after the document is loaded creates a new document. We prefer DOM manipulation since it a safe way to directly edit the DOM that doesn't involve string manipulation.
Writing HTML as text should be left to the browser which parses a HTML file and loads it.
// feature detection for cross browser compliance.
var txt = function(node, text) {
if (node.innerText !== undefined) {
node.innerText = text;
} else if (node.textContent !== undefined) {
node.textContent = text;
}
}
function createTable(rows, columns) {
var table = document.createElement("table");
table.border = '3';
var tbody = document.createElement("tbody");
var thead = document.createElement("thead");
for (var i = 0; i < rows; i++) {
var tr = document.createElement("tr");
for (var j = 0; j < columns; j++) {
var cell;
if (i === 0) {
cell = document.createElement("th");
if (j !== 0) {
txt(cell, "x = " + j);
}
}
else {
if (j == 0) {
cell = document.createElement("th");
txt(cell, "y = " + i);
}
else {
cell = document.createElement("td");
txt(cell, operation(i, j));
}
}
tr.appendChild(cell);
}
if (i === 0) {
thead.appendChild(tr);
} else {
tbody.appendChild(tr);
}
}
table.appendChild(thead);
table.appendChild(tbody);
return table;
}
Using DOM manipulation you could do it like above. There may be a few optimisations I missed. Live example (only tested chrome 10)
At least it works as intended.
Let me know if you want to see an implementation in say jQuery or some other DOM manipulation library. The code will be a lot nicer and more robust in terms of cross browser compliance.

I see a number of problems here. First and foremost, I don't believe your code is properly balanced. I believe as a first step it should look more like:
<script language="javascript">
document.write('<table border = \"3\">');
while(i <= y) {
document.write('<tr>');
j = 0;
while(j <= x) {
if (i == 0) {
if (j == 0) {
document.write(' <th></th> ');
} else {
document.write('<th>');
document.write("x = " + j);
document.write('</th>');
}
} else {
if (j == 0) {
document.write('<th>');
document.write("y = " + i);
document.write('</th>');
} else {
document.write('<td>');
document.write(operation(i,j));
document.write('</td>');
}
}
j++;
}
document.write('</tr>');
i++;
}
document.write('</table>');
</script>
Secondarily it looks to me like you're never really ever instantiating i, and while you do it with j, you're not doing it early enough.
At the line while(i <= y) the interpreter is basically seeing:
while (undefined <= undefined) {
At some level, I'm honestly not sure that document.writes is really the best solution for you, I would probably recommend using document.createElement instead, but that's perhaps more of a personal preference.
Attempting to step a level further out yet sticking with your methodology, I might look toward something like:
function generateTable (width, height) {
document.write("<table border=\"3\">");
var i = 1;
while (i <= height) {
document.write("<tr>");
var j = 1;
while (j <= width) {
if (i == 0) {
// header row
document.write("<th>"+ j +" x "+ i +"</th>");
} else {
document.write("<td>"+ j +" x "+ i +"</td>");
}
j++;
}
document.write("</tr>");
i++;
}
document.write("</table>");
}
generateTable(5, 10);

Related

Function Being Called Before Current Function Code Complete

Made a JS Fiddle implementation of the "5 hunters, 3 rabbits" problem described here: https://twitter.com/Mathgarden/status/1039247616616194048
My code is here: https://jsfiddle.net/iPrash/o037fpam/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rabbit Hunters</title>
<link rel="stylesheet" href="htmltable.css">
<script language="JavaScript">
var size = 5;
var hunters = new Array();
var arena = new Array(side, side);
function hunter(row, col) {
this.row = row;
this.col = col;
}
function hunter(row, col) {
this.row = row;
this.col = col;
}
function resetArena() {
hunters = [];
redrawArena();
}
function generate_table() {
// get the reference for the body
var body = document.getElementsByTagName("body")[0];
// creates a <table> element and a <tbody> element
var tbl = document.createElement("table");
tbl.setAttribute("class", "huntertable");
var tblBody = document.createElement("tbody");
// creating all cells
for (var i = 0; i < size; i++) {
// creates a table row
var row = document.createElement("tr");
for (var j = 0; j < size; j++) {
// Create a <td> element and a text node, make the text
// node the contents of the <td>, and put the <td> at
// the end of the table row
var cell = document.createElement("td");
cell.addEventListener("click", cellClicked);
cell.bgColor = "green";
cell.innerHTML = "O";
row.appendChild(cell);
}
// add the row to the end of the table body
tblBody.appendChild(row);
}
// put the <tbody> in the <table>
tbl.appendChild(tblBody);
// appends <table> into <body>
body.appendChild(tbl);
// sets the border attribute of tbl to 2;
tbl.setAttribute("border", "5");
}
function cellClicked() {
var cellRow = this.parentNode.rowIndex;
var cellCol = this.cellIndex;
var cHunter = new hunter(cellRow, cellCol);
if (exists(cHunter)) {
// remove the hunter
remove(cHunter);
} else {
if (hunters.length == 5) {
alert("A maximum of 5 hunters are allowed!");
return;
}
hunters.push(cHunter);
redrawArena();
}
}
function exists(hunter) {
for (var i = 0; i < hunters.length; i++) {
if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col))
return true;
}
return false;
}
function remove(hunter) {
for (var i = 0; i < hunters.length; i++) {
if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col)) {
hunters.splice(i, 1);
break;
}
}
redrawArena();
}
function redrawArena() {
var arenaTable = document.getElementsByTagName("tbl")[0];
var arenaTBody = document.getElementsByTagName("tbody")[0];
// reset arena
for (var rowi = 0; rowi < size; rowi++) {
for (var coli = 0; coli < size; coli++) {
rRow = arenaTBody.getElementsByTagName("tr")[rowi];
rCell = rRow.getElementsByTagName("td")[coli];
rCell.innerHTML = "O";
rCell.bgColor = "green";
}
}
for (var hunterIndex = 0; hunterIndex < hunters.length; hunterIndex++) {
// for each hunter mark the attacked territory:
hunterRow = hunters[hunterIndex].row;
hunterCol = hunters[hunterIndex].col;
huntRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
huntCell = huntRow.getElementsByTagName("td")[hunterCol];
huntCell.innerHTML = "H";
huntCell.bgColor = "red";
// horizontal and vertical
for (var i = 0; i < size; i++) {
hRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
hCell = hRow.getElementsByTagName("td")[i];
hCell.bgColor = "red";
vRow = arenaTBody.getElementsByTagName("tr")[i];
vCell = vRow.getElementsByTagName("td")[hunterCol];
vCell.bgColor = "red";
}
// diagonals
for (var i = 1; i < size; i++) {
if (((hunterRow + i) < size) && ((hunterCol + i) < size)) {
dRow1 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
dCell1 = dRow1.getElementsByTagName("td")[hunterCol + i];
dCell1.bgColor = "red";
}
if (((hunterRow - i) >= 0) && ((hunterCol - i) >= 0)) {
dRow2 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
dCell2 = dRow2.getElementsByTagName("td")[hunterCol - i];
dCell2.bgColor = "red";
}
if (((hunterRow + i) < size) && ((hunterCol - i) >= 0)) {
dRow3 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
dCell3 = dRow3.getElementsByTagName("td")[hunterCol - i];
dCell3.bgColor = "red";
}
if (((hunterRow - i) >= 0) && ((hunterCol + i) < size)) {
dRow4 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
dCell4 = dRow4.getElementsByTagName("td")[hunterCol + i];
dCell4.bgColor = "red";
}
}
}
alert("Checking for win ...");
checkWin();
}
function checkWin() {
// check arena for 5 hunters and 3 rabbits...
if (hunters.length < 5)
return;
var arenaTable = document.getElementsByTagName("tbl")[0];
var arenaTBody = document.getElementsByTagName("tbody")[0];
var rabbits = 0;
for (var rowi = 0; rowi < size; rowi++) {
for (var coli = 0; coli < size; coli++) {
rRow = arenaTBody.getElementsByTagName("tr")[rowi];
rCell = rRow.getElementsByTagName("td")[coli];
if (rCell.bgColor == "green") {
rabbits++;
}
}
}
if (rabbits == 3)
alert("Congrats! You did it!")
}
</script>
</head>
<body onload="generate_table()">
<h1>Rabbit Hunters</h1>
<p>
<ol>
<li>The grid below represents a forest filled with rabbits (green).</li>
<li>Hunters can attack horizontally and diagonally in all directions (like a chess queen).</li>
<li>Once placed, hunters will kill all rabbits in their lines of sight (try clicking!).</li>
<li>To remove hunters just click on them again.</li>
<li>The Reset button clears the whole forest.</li>
</ol>
<strong>Can you place 5 hunters on the grid below so that they spare 3 rabbits (three green squares should remain)?</strong>
</p>
<p>
<input type="button" value="Reset" onclick="resetArena()" />
</p>
</body>
</html>
======================
My question is: Why does the alert "Checking for win ..." show up before the last hunter is drawn (or the arena is fully redrawn). I added this debug alert because even though I have the checkWin() function being called after the redraw loop is fully complete it seems to want to start executing checkWin() first. So the last clicked hunter square turns to "H" only after the alert while I want it to be before checking for win.
Thank you!
About problem
Your function checkWin isn't really invoked before any other code in function redrawArena. Described problem is caused by usage of code, that is blocking main UI thread of browser, which is used for updating rendered document and executing its JS code (except workers). So if you dynamically change properties (like CSS styles) of some elements in document, browser might not repaint (or reflow) document immediately, this will usually happen after all JS functions already present in call stack are popped out from it (i.e. they return value). Let us call some part of function code as "blocking code", if it will prevent this function from returning its value for longer time (long enough to make website noticeable unresponsive to user). Most common sources of blocking code are synchronous XMLHttpRequests, native JS dialogs, long running loops (e.g. some heavy computation), etc.
Here is a simple example demonstrating how will longer running loop (after first color change) block repainting of document, so you will never see "busy status" (red color):
#status {
display: inline-block;
width: 10px;
height: 10px;
background-color: gray;
}
<!DOCTYPE html>
<html>
<head>
<script>
function start(){
setStatus('red');
compute();
}
function compute(){
var a = [];
for (var i = 0; i < 10; i+=0.000001){
a.push(Math.sin(i) + Math.cos(i));
}
setStatus('green');
}
function setStatus(color){
document.getElementById('status').style.backgroundColor = color;
}
</script>
</head>
<body>
<div>Status: <div id="status"></div></div>
<button onclick="start()">Compute</button>
<button onclick="setStatus(null)">Reset</button>
</body>
</html>
Alert modal
Obviously, such a thread blocking behavior in your code can be caused only by alert() calls. But here arises question - Which browser(s) are you using for testing your code? According to alert spec:
Show message to the user.
Optionally, pause while waiting for the user to acknowledge the message.
and then pause spec:
If necessary, update the rendering or user interface of any Document or browsing context to reflect the current state.
Wait until the condition goal is met. While a user agent has a paused task, the corresponding event loop must not run further tasks, and any script in the currently running task must block. User agents should remain responsive to user input while paused, however, albeit in a reduced capacity since the event loop will not be doing anything.
alert should not block rendering (UI may be refreshed during pause), and theoretically it doesn't need to block any subsequent JS code while waiting for user (as spec says that pause is optional).
However, browsers are not always following specification (and in the case of pausing, experiments are allowed and encouraged), so I decided to test your code on several browsers I had available on current machine (Win7):
|-------------|---------------------|-----------------------|
| | | |
| browser | blocks UI refresh | executes subsequent |
| | during alert | code during alert |
| | | |
|-------------|---------------------|-----------------------|
| Chrome 69.0 | yes | no* |
|-------------|---------------------|-----------------------|
| Opera 55.0 | yes | no |
|-------------|---------------------|-----------------------|
| FF Dev 63.0 | no | no |
|-------------|---------------------|-----------------------|
| FF 62.0 | no | no |
|-------------|---------------------|-----------------------|
| FF 60.2 | no | no |
|-------------|---------------------|-----------------------|
| FF 52.9 | no | no |
|-------------|---------------------|-----------------------|
| IE 11 | no | no |
|-------------|---------------------|-----------------------|
As you can see from above table, I encountered described problem only in Chrome and Opera (same rendering engine, both in currently latest stable version). None of tested browsers will run any subsequent JS code during alert, however Chrome appears to push callbacks of input events into call stack, if these events are triggered before displaying first alert (*). E.g. if you manage to click on your table cell more than once fast enough, function cellClicked will be invoked more times (so if you confirm first alert, Chrome will update UI state and display another alert). Any other browser from table doesn't seem to have this behavior though.
Possible solutions
The most straightforward solution to avoid potential unwanted block of pending UI update is giving browser time to perform updates before blocking code will be executed. This can be achieved by moving alert and subsequent code into asynchronous callback, e.g. by using setTimeout function. In following snippet, I have moved calling of alert "Checking for win ..." and checkWin() in function redrawArena into callback of setTimeout function added there. This will allow browsers refresh your table before any alert is displayed, and thus bypassing unwanted UI blocking effect in affected browsers. You can also play with delay parameter of setTimeout to find minimal value that will allow to trigger UI refresh in all targeted browsers.
.huntertable tr {
cursor: pointer;
}
.huntertable td {
font-size: 40px;
text-align: center;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rabbit Hunters</title>
<script>
var size = 5;
var hunters = new Array();
function hunter(row, col) {
this.row = row;
this.col = col;
}
function resetArena() {
hunters = [];
redrawArena();
}
function generate_table() {
// get the reference for the body
var body = document.getElementsByTagName("body")[0];
// creates a <table> element and a <tbody> element
var tbl = document.createElement("table");
tbl.setAttribute("class", "huntertable");
var tblBody = document.createElement("tbody");
// creating all cells
for (var i = 0; i < size; i++) {
// creates a table row
var row = document.createElement("tr");
for (var j = 0; j < size; j++) {
// Create a <td> element and a text node, make the text
// node the contents of the <td>, and put the <td> at
// the end of the table row
var cell = document.createElement("td");
cell.addEventListener("click", cellClicked);
cell.bgColor = "green";
cell.innerHTML = "O";
row.appendChild(cell);
}
// add the row to the end of the table body
tblBody.appendChild(row);
}
// put the <tbody> in the <table>
tbl.appendChild(tblBody);
// appends <table> into <body>
body.appendChild(tbl);
// sets the border attribute of tbl to 2;
tbl.setAttribute("border", "5");
}
function cellClicked() {
var cellRow = this.parentNode.rowIndex;
var cellCol = this.cellIndex;
var cHunter = new hunter(cellRow, cellCol);
if (exists(cHunter)) {
// remove the hunter
remove(cHunter);
} else {
if (hunters.length == 5) {
alert("A maximum of 5 hunters are allowed!");
return;
}
hunters.push(cHunter);
redrawArena();
}
}
function exists(hunter) {
for (var i = 0; i < hunters.length; i++) {
if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col))
return true;
}
return false;
}
function remove(hunter) {
for (var i = 0; i < hunters.length; i++) {
if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col)) {
hunters.splice(i, 1);
break;
}
}
redrawArena();
}
function redrawArena() {
var arenaTable = document.getElementsByTagName("tbl")[0];
var arenaTBody = document.getElementsByTagName("tbody")[0];
// reset arena
for (var rowi = 0; rowi < size; rowi++) {
for (var coli = 0; coli < size; coli++) {
rRow = arenaTBody.getElementsByTagName("tr")[rowi];
rCell = rRow.getElementsByTagName("td")[coli];
rCell.innerHTML = "O";
rCell.bgColor = "green";
}
}
for (var hunterIndex = 0; hunterIndex < hunters.length; hunterIndex++) {
// for each hunter mark the attacked territory:
hunterRow = hunters[hunterIndex].row;
hunterCol = hunters[hunterIndex].col;
huntRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
huntCell = huntRow.getElementsByTagName("td")[hunterCol];
huntCell.innerHTML = "H";
huntCell.bgColor = "red";
// horizontal and vertical
for (var i = 0; i < size; i++) {
hRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
hCell = hRow.getElementsByTagName("td")[i];
hCell.bgColor = "red";
vRow = arenaTBody.getElementsByTagName("tr")[i];
vCell = vRow.getElementsByTagName("td")[hunterCol];
vCell.bgColor = "red";
}
// diagonals
for (var i = 1; i < size; i++) {
if (((hunterRow + i) < size) && ((hunterCol + i) < size)) {
dRow1 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
dCell1 = dRow1.getElementsByTagName("td")[hunterCol + i];
dCell1.bgColor = "red";
}
if (((hunterRow - i) >= 0) && ((hunterCol - i) >= 0)) {
dRow2 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
dCell2 = dRow2.getElementsByTagName("td")[hunterCol - i];
dCell2.bgColor = "red";
}
if (((hunterRow + i) < size) && ((hunterCol - i) >= 0)) {
dRow3 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
dCell3 = dRow3.getElementsByTagName("td")[hunterCol - i];
dCell3.bgColor = "red";
}
if (((hunterRow - i) >= 0) && ((hunterCol + i) < size)) {
dRow4 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
dCell4 = dRow4.getElementsByTagName("td")[hunterCol + i];
dCell4.bgColor = "red";
}
}
}
setTimeout(function() {
alert("Checking for win ...");
checkWin();
},20);
}
function checkWin() {
// check arena for 5 hunters and 3 rabbits...
if (hunters.length < 5)
return;
var arenaTable = document.getElementsByTagName("tbl")[0];
var arenaTBody = document.getElementsByTagName("tbody")[0];
var rabbits = 0;
for (var rowi = 0; rowi < size; rowi++) {
for (var coli = 0; coli < size; coli++) {
rRow = arenaTBody.getElementsByTagName("tr")[rowi];
rCell = rRow.getElementsByTagName("td")[coli];
if (rCell.bgColor == "green") {
rabbits++;
}
}
}
if (rabbits == 3)
alert("Congrats! You did it!")
}
</script>
</head>
<body onload="generate_table()">
<h1>Rabbit Hunters</h1>
<p>
<ol>
<li>The grid below represents a forest filled with rabbits (green).</li>
<li>Hunters can attack horizontally and diagonally in all directions (like a chess queen).</li>
<li>Once placed, hunters will kill all rabbits in their lines of sight (try clicking!).</li>
<li>To remove hunters just click on them again.</li>
<li>The Reset button clears the whole forest.</li>
</ol>
<strong>Can you place 5 hunters on the grid below so that they spare 3 rabbits (three green squares should remain)?</strong>
</p>
<p>
<input type="button" value="Reset" onclick="resetArena()" />
</p>
</body>
</html>
However, in most cases (like yours), it is better to display such a kind of output directly into document. This way, your message will be displayed alongside other changes in DOM on next UI refresh. With the help of HTML, CSS and JS you can create your personal method for displaying messages (including custom modals, infobars, etc.). In below snippet, I have created very simple example, how can your app output messages to user. Basically, I have added additional div for displaying messages, created two functions for showing/removing messages in this div (showMessage/removeMessage) and replaced alerts with showMessage calls (and removed pointless alert "Checking for win ...").
.huntertable tr {
cursor: pointer;
}
.huntertable td {
font-size: 40px;
text-align: center;
}
#infobar {
color: red;
font-weight: bold;
margin-bottom: 10px;
visibility: hidden;
min-height: 20px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rabbit Hunters</title>
<script>
var size = 5;
var hunters = new Array();
function hunter(row, col) {
this.row = row;
this.col = col;
}
function showMessage(msgText) {
var infobar = document.getElementById('infobar');
infobar.innerHTML = msgText;
infobar.style.visibility = 'visible';
}
function removeMessage() {
var infobar = document.getElementById('infobar');
infobar.innerHTML = '';
infobar.style.visibility = null;
}
function resetArena() {
removeMessage();
hunters = [];
redrawArena();
}
function generate_table() {
// get the reference for the body
var body = document.getElementsByTagName("body")[0];
// creates a <table> element and a <tbody> element
var tbl = document.createElement("table");
tbl.setAttribute("class", "huntertable");
var tblBody = document.createElement("tbody");
// creating all cells
for (var i = 0; i < size; i++) {
// creates a table row
var row = document.createElement("tr");
for (var j = 0; j < size; j++) {
// Create a <td> element and a text node, make the text
// node the contents of the <td>, and put the <td> at
// the end of the table row
var cell = document.createElement("td");
cell.addEventListener("click", cellClicked);
cell.bgColor = "green";
cell.innerHTML = "O";
row.appendChild(cell);
}
// add the row to the end of the table body
tblBody.appendChild(row);
}
// put the <tbody> in the <table>
tbl.appendChild(tblBody);
// appends <table> into <body>
body.appendChild(tbl);
// sets the border attribute of tbl to 2;
tbl.setAttribute("border", "5");
}
function cellClicked() {
removeMessage();
var cellRow = this.parentNode.rowIndex;
var cellCol = this.cellIndex;
var cHunter = new hunter(cellRow, cellCol);
if (exists(cHunter)) {
// remove the hunter
remove(cHunter);
} else {
if (hunters.length == 5) {
showMessage("A maximum of 5 hunters are allowed!");
return;
}
hunters.push(cHunter);
redrawArena();
}
}
function exists(hunter) {
for (var i = 0; i < hunters.length; i++) {
if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col))
return true;
}
return false;
}
function remove(hunter) {
for (var i = 0; i < hunters.length; i++) {
if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col)) {
hunters.splice(i, 1);
break;
}
}
redrawArena();
}
function redrawArena() {
var arenaTable = document.getElementsByTagName("tbl")[0];
var arenaTBody = document.getElementsByTagName("tbody")[0];
// reset arena
for (var rowi = 0; rowi < size; rowi++) {
for (var coli = 0; coli < size; coli++) {
rRow = arenaTBody.getElementsByTagName("tr")[rowi];
rCell = rRow.getElementsByTagName("td")[coli];
rCell.innerHTML = "O";
rCell.bgColor = "green";
}
}
for (var hunterIndex = 0; hunterIndex < hunters.length; hunterIndex++) {
// for each hunter mark the attacked territory:
hunterRow = hunters[hunterIndex].row;
hunterCol = hunters[hunterIndex].col;
huntRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
huntCell = huntRow.getElementsByTagName("td")[hunterCol];
huntCell.innerHTML = "H";
huntCell.bgColor = "red";
// horizontal and vertical
for (var i = 0; i < size; i++) {
hRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
hCell = hRow.getElementsByTagName("td")[i];
hCell.bgColor = "red";
vRow = arenaTBody.getElementsByTagName("tr")[i];
vCell = vRow.getElementsByTagName("td")[hunterCol];
vCell.bgColor = "red";
}
// diagonals
for (var i = 1; i < size; i++) {
if (((hunterRow + i) < size) && ((hunterCol + i) < size)) {
dRow1 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
dCell1 = dRow1.getElementsByTagName("td")[hunterCol + i];
dCell1.bgColor = "red";
}
if (((hunterRow - i) >= 0) && ((hunterCol - i) >= 0)) {
dRow2 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
dCell2 = dRow2.getElementsByTagName("td")[hunterCol - i];
dCell2.bgColor = "red";
}
if (((hunterRow + i) < size) && ((hunterCol - i) >= 0)) {
dRow3 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
dCell3 = dRow3.getElementsByTagName("td")[hunterCol - i];
dCell3.bgColor = "red";
}
if (((hunterRow - i) >= 0) && ((hunterCol + i) < size)) {
dRow4 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
dCell4 = dRow4.getElementsByTagName("td")[hunterCol + i];
dCell4.bgColor = "red";
}
}
}
checkWin();
}
function checkWin() {
// check arena for 5 hunters and 3 rabbits...
if (hunters.length < 5)
return;
var arenaTable = document.getElementsByTagName("tbl")[0];
var arenaTBody = document.getElementsByTagName("tbody")[0];
var rabbits = 0;
for (var rowi = 0; rowi < size; rowi++) {
for (var coli = 0; coli < size; coli++) {
rRow = arenaTBody.getElementsByTagName("tr")[rowi];
rCell = rRow.getElementsByTagName("td")[coli];
if (rCell.bgColor == "green") {
rabbits++;
}
}
}
if (rabbits == 3)
showMessage("Congrats! You did it!");
}
</script>
</head>
<body onload="generate_table()">
<h1>Rabbit Hunters</h1>
<p>
<ol>
<li>The grid below represents a forest filled with rabbits (green).</li>
<li>Hunters can attack horizontally and diagonally in all directions (like a chess queen).</li>
<li>Once placed, hunters will kill all rabbits in their lines of sight (try clicking!).</li>
<li>To remove hunters just click on them again.</li>
<li>The Reset button clears the whole forest.</li>
</ol>
<strong>Can you place 5 hunters on the grid below so that they spare 3 rabbits (three green squares should remain)?</strong>
</p>
<p>
<input type="button" value="Reset" onclick="resetArena()" />
</p>
<div id="infobar"></div>
</body>
</html>
TL;DR
In general, you should avoid using JS alerts neither for displaying usual textual output from your app, nor for debugging it. Alerts may prevent UI refresh in some browsers until they are confirmed by user, and also they may be blocked by browser, making them unreliable for displaying important information. If you need to display messages to user, you should create one or more methods for displaying them dynamically in document (you can actually find many different solutions over the web). For debugging, you should use Dev Tools panel integrated in every modern browser and methods from Console object. If you still insist on using alerts in your app, and you are experiencing unwanted blocking of page UI refresh or unrelated code because of them, you will probably need to put them into asynchronous callbacks.

How to add to HTML table cells a uniquely named JavaScript function

I have a Sudoku table which I've generated using JavaScript. After generating the table, I've also attached a number entry field as well as a function to each cell. Currently, the function passes in only the very last cell id that was generated (ex. R9C9), not the cell that was recently modified. I would rather have the function get passed the current cell id which the user modified so that I can check the row, column, and current square for conflicts and not every single cell on the board. I'm trying not to use jQuery, but any help would be appreciated.
This is the code that attaches the number field, as well as the function. I believe currently the function it attaches is anonymous, which wouldn't matter as long as I could get the attached anonymous function to be associated with the current cell.
for (i = 1; i <=9; i++)
{
for (j = 1; j <= 9; j++) {
temp = document.getElementById('R'+i+'C'+j);
if (temp.innerHTML == "")
{
var temp2 = temp.appendChild(document.createElement("input"));
temp2.type = "number";
temp3 = document.getElement('R'+i+'C'+j);
temp2.onkeyup = function(){conflictChecker(temp3)};
}
}
}
This is the code for generating the table.
function makeGrid(x, y, name) {
var tableDiv = document.getElementById("gameTable");
var table = document.createElement('table');
table.id = "theTable";
table.style.width = '100px';
table.style.border = 'thick solid black';
for (var i = 1; i <= x; i++) {
var row = table.insertRow();
row.id = "Row" + i;
for (var j = 1; j <= y; j++) {
var cell = row.insertCell();
cell.id = "R" + i + "C" + j;
cell.classList.add();
cell.style.border = '1px solid';
if((i%3) == 0)
{
cell.style.borderBottom = "thick solid black";
}
if(((j-1)%3) == 0)
{
cell.style.borderLeft = "thick solid black";
}
}
}
tableDiv.appendChild(table)
The original elements of the table I add using so the user cannot modify them.
document.getElementById("R_C_").innerHTML = "someNumber";
I tried taking the assingment of the functions out of the orinal function by adding this
for (i =1; i <= 9; i++) {
for (j = 1; j <= 9; j++) {
temp = document.getElementById('R' + i + 'C' + j);
temp.onkeyup = function(){conflictChecker(temp)};
}
}
Any help would be immensely appreciated. I've tried several different types of syntax, but they don't seem to be working.
What's happening is that the javascript is not computing the value of temp3 - it's keeping it as a variable until the function is called, and then it computes the value, which will be equal to what it was during the very last iteration...
Did you try this?
temp2.onkeyup = function(){conflictChecker(this.parent)};
I solved my problem. For some reason using the onkeyup function for my event wasn't working. I switched it to onchange() and was able to get the current cell by passing in 'this'
for (i = 1; i <=9; i++)
{
for (j = 1; j <= 9; j++) {
temp = document.getElementById('R'+i+'C'+j);
if (temp.innerHTML == "")
{
var temp2 = temp.appendChild(document.createElement("input"));
temp2.type = "number";
temp2.id = 'R'+i+'C'+j+'2';
temp2.onchange = function(){conflictChecker(this)};
}
else
{
theVal = temp.innerHTML;
temp.value = theVal;
temp.id = 'R'+i+'C'+j+'2';
}
}
}
I assigned different Id's as well so that I could look up the value of the cell from conflictChecker. Probably not the most elegant solution but it works.

Populating multidimensional array

The code below came as an included file with a beginner puzzle app tutorial I'm working through. The code works, however now that I've completed the tutorial, I'm trying to read through the files that came preloaded which were not explained.
I'm really tripped up over the "spacecount" variable, and what exactly it's doing. Can anyone comment each line in plain english, so that I can better understand how exactly the code below is populating the rowCount array. Thank you so much.
var totalRows = puzzle.length;
var totalCols = puzzle[0].length;
/* Loop through the rows to create the rowCount array
containing the totals for each row in the puzzle */
var rowCount = [];
for (var i = 0; i < totalRows; i++) {
rowCount[i]="";
spaceCount = 0;
for (var j = 0; j < totalCols; j++) {
if (puzzle[i][j] == "#") {
spaceCount++;
if (j == totalCols-1) rowCount[i] += spaceCount + " ";
} else {
if (spaceCount > 0) {
rowCount[i] += spaceCount + " ";
spaceCount = 0;
}
}
}
Here's a slightly more legible version:
var totalRows = puzzle.length;
var totalCols = puzzle[0].length;
/* Loop through the rows to create the rowCount array
containing the totals for each row in the puzzle */
var rowCount = [];
for (var i = 0; i < totalRows; i++) {
rowCount[i] = "";
spaceCount = 0;
for (var j = 0; j < totalCols; j++) {
if (puzzle[i][j] == "#") {
spaceCount++;
if (j == totalCols - 1) {
rowCount[i] += spaceCount + " ";
}
} else if (spaceCount > 0) {
rowCount[i] += spaceCount + " ";
spaceCount = 0;
}
}
}​
The confusing parts are probably the if blocks in the middle.
if (puzzle[i][j] == "#") { // If a puzzle piece is `#` (a space?)
spaceCount++; // Increment the spaceCount by 1.
if (j == totalCols - 1) { // Only if we are on the last column, add the text
// to the row.
rowCount[i] += spaceCount + " ";
}
} else if (spaceCount > 0) { // If the current piece isn't a `#` but
// spaces have already been counted,
// add them to the row's text and reset `spaceCount`
rowCount[i] += spaceCount + " ";
spaceCount = 0;
}​
From what I can tell, this code counts the number of consecutive pound signs and appends this text to each row.

ignoring hidden rows

How would I rewrite this function so that any hidden rows are completely ignored?
function stripeRows(table) {
var numRows = table.rows.length;
for (var i = 1; i < numRows; i++) {
var ID = table.rows[i].id;
if (i % 2 == 0) {
table.rows[i].className = "GridRow";
}
else {
table.rows[i].className = "GridRowAlt";
}
}
}
EDIT: turns out my suggestion to use CSS will not work - go figure... In any case you can use jQuery
$('tr:visible:odd').addClass('even');
$('tr:visible:even').addClass('odd');
(note the inversion, since jQuery counts from 0). No need to loop at all! :-)
See it working
However you detect hidden rows, you must at least decouple your counter from your row index, e.g. via for(... in ...). If you find a hidden row, continue your loop:
var i = 0;
for (var row in table.rows) {
if(row.style.visibility == 'hidden')
// or similar (see other answers), e.g.: if($('#element_id:visible').length > 0)
continue;
var ID = row.id;
if (i % 2 == 0) {
row.className = "GridRow";
}
else {
row.className = "GridRowAlt";
}
++i;
}

What is the best way to give support `nth-child` in one shot to all IE version?

What is the best way to give support nth-child in one shot to all IE version?
I want to give style like this. for some particular pages.
#products tr:nth-child(even) {
background-color: red;
}
#products tr:nth-child(odd) {
background-color: white;
}
You can do it in javascript.
var table = document.getElementById('products');
var rows = table.getElementsByTagName('tr');
for (var i = 0; i < rows.length; ++i)
{
if ( (i % 2) == 0 )
{
rows[i].className = 'even';
}
}
then do your CSS like this:
#products tr td
{
background-color: white;
}
#products tr.even td
{
background-color: red;
}
If you have used a javascript library, you could have done this :
$('#products tr:even').addClass('even');
That project gives you "native" support for these and many others CSS3 selectors for at least IE7/8.
But here you'll have a problem with IE7 which doesn't support background-color for tr.
I made something that should work in every browser:
https://gist.github.com/yckart/5652296
var nthChild = function (elem, num) {
var len = elem.length;
var ret = [];
// :nth-child(num)
if (!isNaN(Number(num))) {
for (var i = 0; i < len; i++) {
if (i === num - 1) return elem[i];
}
}
// :nth-child(numn+num)
if (num.indexOf("+") > 0) {
var parts = num.match(/\w/g);
for (var i = parts[2] - 1; i < len; i += parts[0] << 0) {
if (elem[i]) ret.push(elem[i]);
}
}
// :nth-child(odd)
if (num === "odd") {
for (var i = 0; i < len; i += 2) {
ret.push(elem[i]);
}
}
// :nth-child(even)
if (num === "even") {
for (var i = 1; i < len; i += 2) {
ret.push(elem[i]);
}
}
return ret;
};
var rows = document.getElementsByTagName('li');
var num = nthChild(rows, 2);
var formula = nthChild(rows, "3n+1");
var even = nthChild(rows, "even");
var odd = nthChild(rows, "odd");
// Note, forEach needs to be polyfilled for oldIE
even.forEach(function (li) {
li.className += " even";
});
odd.forEach(function (li) {
li.className += " odd";
});
formula.forEach(function (li) {
li.className += " formula";
});
num.style.backgroundColor = "black";
You can do this in jQuery too, and they'll likely have solved your cross browser issue.
$('#products').children('tr:even').css('background-color', 'red');
$('#products').children('tr:odd').css('background-color', 'white');

Categories

Resources