The images i'm trying to draw will not work. When I run functions in javascript, I get this error:
Uncaught TypeError: context.drawImage is not a function
at Image.drawing.onload (test.js:74)
test.js:82 Uncaught TypeError: context.drawImage is not a function
at Image.drawing.onload (test.js:82)
Here is my code:
HTML:
<html>
<head>
<title>Maze</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">
</script>
<script type="text/javascript" src="js/test.js"></script>
<style type="text/css">
#maze {
border-collapse: collapse;
}
#maze td {
width: 20px;
height: 20px;
}
</style>
</head>
<body>
<table id="maze">
<tbody></tbody>
</table>
<canvas>
<script>
var disp = newMaze(20,20);
for (var i = 0; i < disp.length; i++) {
$('#maze > tbody').append("<tr>");
for (var j = 0; j < disp[i].length; j++) {
var selector = i+"-"+j;
$('#maze > tbody').append("<td id='"+selector+"'> </td>");
if (disp[i][j][0] == 0) { $('#'+selector).css('border-top', '2px
solid black'); }
if (disp[i][j][1] == 0) { $('#'+selector).css('border-right', '2px
solid black'); }
if (disp[i][j][2] == 0) { $('#'+selector).css('border-bottom', '2px
solid black'); }
if (disp[i][j][3] == 0) { $('#'+selector).css('border-left', '2px
solid black'); }
}
$('#maze > tbody').append("</tr>");
}
</script>
</canvas>
</body>
</html>
JS:
function newMaze(x, y) {
// Establish variables and starting grid
var totalCells = x*y;
var cells = new Array();
var unvis = new Array();
for (var i = 0; i < y; i++) {
cells[i] = new Array();
unvis[i] = new Array();
for (var j = 0; j < x; j++) {
cells[i][j] = [0,0,0,0];
unvis[i][j] = true;
}
}
// Set a random position to start from
var currentCell = [Math.floor(Math.random()*y), Math.floor(Math.random()*x)];
var path = [currentCell];
unvis[currentCell[0]][currentCell[1]] = false;
var visited = 1;
// Loop through all available cell positions
while (visited < totalCells) {
// Determine neighboring cells
var pot = [[currentCell[0]-1, currentCell[1], 0, 2],
[currentCell[0], currentCell[1]+1, 1, 3],
[currentCell[0]+1, currentCell[1], 2, 0],
[currentCell[0], currentCell[1]-1, 3, 1]];
var neighbors = new Array();
// Determine if each neighboring cell is in game grid, and whether it has already been checked
for (var l = 0; l < 4; l++) {
if (pot[l][0] > -1 && pot[l][0] < y && pot[l][1] > -1 && pot[l][1] < x && unvis[pot[l][0]][pot[l][1]]) { neighbors.push(pot[l]); }
}
// If at least one active neighboring cell has been found
if (neighbors.length) {
// Choose one of the neighbors at random
next = neighbors[Math.floor(Math.random()*neighbors.length)];
// Remove the wall between the current cell and the chosen neighboring cell
cells[currentCell[0]][currentCell[1]][next[2]] = 1;
cells[next[0]][next[1]][next[3]] = 1;
// Mark the neighbor as visited, and set it as the current cell
unvis[next[0]][next[1]] = false;
visited++;
currentCell = [next[0], next[1]];
path.push(currentCell);
}
// Otherwise go back up a step and keep going
else {
currentCell = path.pop();
}
}
debugger
drawUser(currentCell[0],currentCell[1]);
var first = currentCell[0] + 10;
var second = currentCell[1] + 10;
if(first >= 20){
first = currentCell[0] - 10;
}
if(second >= 20){
second = currentCell[1] - 10;
}
drawJewel(first,second);
return cells;
}
function drawUser(x,y){
var context = document.getElementById("maze");
drawing = new Image();
drawing.src = "img/userpic.png";
drawing.onload = function() {
context.drawImage(drawing,x,y);
};
}
function drawJewel(x,y){
var context = document.getElementById("maze");
drawing = new Image();
drawing.src = "img/diamondpic.png";
drawing.onload = function() {
context.drawImage(drawing,x,y);
};
}
The canvas tags around the script tags in the HTML was one of my attempts at a fix, as well as the debugger in the JS code.
I'm new to jQuery but it needs to be used for generating the maze.
All i'm trying to do is display the images, diamondpic.png and userpic.png, both images 20x20 px drawn in paint, in the maze located in two separate, random cells. I'm not familiar with "canvas" or "context" and I think it is the issue.
Any help appreciated.
A couple of problems here. First, the #maze element is a table, not a canvas. If you want to use a canvas to draw your images, you need to draw on the canvas element, not a table.
Second, you need to define context as a 2d context of your canvas element.
Replace
var context = document.getElementById("maze");
with
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
Related
I am basically trying to implement the bubble code (this code will just display the data from the csv file and display them in a bubble ellipse) into a data visualization template and the bubble code should be in an external constructor (meaning it should be in another file with the constructor name). To give u a general idea, i'm making a few tabs and once a tab is clicked - its designated code(constructor) will run.
I created a tab named as 'Food Data in Bubbles' and when that tab is clicked, the constructor should be called.
I don't have any problem with calling the constructor. The thing im having an issue with is that when the constructor is called, it gives an console error as you can see in this image
I want to know how i can resolve this Issue. Just for reference, I'll provide you with the files and code.
This is the index file that I'm using:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="lib/p5.min.js"></script>
<!-- Main sketch file -->
<script src="sketch.js"></script>
<script src="bubble.js"></script>
<body>
<div id="app" class="container">
<ul id="visuals-menu"></ul>
</div>
</body>
</html>
This is the sketch file:
var dataTable;
var myScatter;
var data;
var bubbles = [];
var maxAmt;
var years = [];
var yearButtons = [];
function preload()
{
dataTable = loadTable('data/tech-diversity/gender-2018.csv', 'header');
data = loadTable("data/bubble/foodData.csv", "csv", "header");
}
function setup() {
// Create a canvas to fill the content div from index.html.
var c = createCanvas(1024, 576);
c.parent('app');
// Add the visualisation objects here.
gallery.addVisual(new BubbleData());
}
function draw() {
background(255);
if (gallery.selectedVisual != null) {
gallery.selectedVisual.draw();
}
translate(width/2, height/2);
for(var i = 0; i < bubbles.length; i++)
{
bubbles[i].update(bubbles);
bubbles[i].draw();
}
}
and this is the main Constructor file:
function BubbleData() {
// Name for the visualisation to appear in the menu bar.
this.name = 'Food Data in Bubbles';
// Each visualisation must have a unique ID with no special
// characters.
this.id = 'bubble-data';
// Property to represent whether data has been loaded.
this.loaded = false;
this.setup = function() {
var rows = data.getRows();
var numColumns = data.getColumnCount();
for(var i = 5; i < numColumns; i++)
{
var y = data.columns[i];
years.push(y);
b = createButton(y,y);
b.parent('years')
b.mousePressed(function()
{
changeYear(this.elt.value);
})
yearButtons.push(b);
}
maxAmt = 0;
for(var i = 0; i < rows.length; i++)
{
if(rows[i].get(0) != "")
{
var b = new Bubble(rows[i].get(0));
for(var j = 5; j < numColumns; j++)
{
if(rows[i].get(j) != "")
{
var n = rows[i].getNum(j);
if(n > maxAmt)
{
maxAmt = n; //keep a tally of the highest value
}
b.data.push(n);
}
else
{
b.data.push(0);
}
}
bubbles.push(b);
}
}
for(var i = 0; i < bubbles.length; i++)
{
bubbles[i].setData(0);
}
};
function changeYear(year)
{
var y = years.indexOf(year);
for(var i = 0; i < bubbles.length; i++)
{
bubbles[i].setData(y);
}
};
// Create a new pie chart object.
this.pie = new PieChart(width / 2, height / 2, width * 0.4);
this.draw = function() {
translate(width/2, height/2);
for(var i = 0; i < bubbles.length; i++)
{
bubbles[i].update(bubbles);
bubbles[i].draw();
}
};
this.Bubble = function(_name)
{
this.size = 20;
this.target_size = 20;
this.pos = createVector(0,0);
this.direction = createVector(0,0);
this.name = _name;
this.color = color(random(0,255), random(0,255), random(0,255));
this.data = [];
this.draw = function()
{
push();
textAlign(CENTER);
noStroke();
fill(this.color);
ellipse(this.pos.x, this.pos.y, this.size);
fill(0);
text(this.name,this.pos.x,this.pos.y);
pop();
};
this.update = function(_bubbles)
{
this.direction.set(0,0);
for(var i = 0; i < _bubbles.length; i++)
{
if(_bubbles[i].name != this.name)
{
var v = p5.Vector.sub(this.pos,_bubbles[i].pos);
var d = v.mag();
if(d < this.size/2 + _bubbles[i].size/2)
{
if(d > 0)
{
this.direction.add(v)
}
else
{
this.direction.add(p5.Vector.random2D());
}
}
}
}
this.direction.normalize();
this.direction.mult(2);
this.pos.add(this.direction);
if(this.size < this.target_size)
{
this.size += 1;
}
else if(this.size > this.target_size)
{
this.size -= 1;
}
};
this.setData = function(i)
{
this.target_size = map(this.data[i], 0, maxAmt, 20, 250);
};
};
}
there is another file which is the helper function file through which I'm calling this function and other task but that's unnecessary here, the only problem here is with the constructor file
I have a maze game ( making player and end goal later) and im trying to make a span that will show up once the game is completed. so far its doing nothing.
Ive tried this
var mazeComplete = document.getElementById("mazeComplete");
var gameComplete = false;
function checkGameFinished() {
if(gameComplete = false){
mazeComplete.style.visibility = 'hidden'
} else if (gameComplete = true) {
mazeComplete.style.visibility = 'visible'
}
}
but with all my code its not working at all. just wanted to see if i could hide the text.
html:
<head>
<title>Maze</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="mazegenerator.js"></script>
<script type="text/javascript" src="Maze.js"></script>
<style type="text/css">
#maze {
border-collapse: collapse;
}
#maze td {
width: 20px;
height: 20px;
}
canvas {
position: absolute;
}
</style>
</head>
<body>
<table id="maze">
<tbody></tbody>
</table>
<script>
var disp = newMaze(20,20);
for (var i = 0; i < disp.length; i++) {
$('#maze > tbody').append("");
for (var j = 0; j < disp[i].length; j++) {
var selector = i+"-"+j;
$('#maze > tbody').append("<td id='"+selector+"'> </td>");
if (disp[i][j][0] == 0) { $('#'+selector).css('border-top', '2px solid black'); }
if (disp[i][j][1] == 0) { $('#'+selector).css('border-right', '2px solid black'); }
if (disp[i][j][2] == 0) { $('#'+selector).css('border-bottom', '2px solid black'); }
if (disp[i][j][3] == 0) { $('#'+selector).css('border-left', '2px solid black'); }
}
$('#maze > tbody').append("");
}
</script>
<br>
<span id="mazeComplete">Maze complete <button id="resetMaze" onclick="mazeReset()">New Maze</button> </span>
Completed <span id="mazeCompletions">0</span>
</body>
</html>
Javascript:
function newMaze(x, y) {
// Establish variables and starting grid
var totalCells = x*y;
var cells = new Array();
var unvis = new Array();
for (var i = 0; i < y; i++) {
cells[i] = new Array();
unvis[i] = new Array();
for (var j = 0; j < x; j++) {
cells[i][j] = [0,0,0,0];
unvis[i][j] = true;
}
}
// Set a random position to start from
var currentCell = [Math.floor(Math.random()*y), Math.floor(Math.random()*x)];
var path = [currentCell];
unvis[currentCell[0]][currentCell[1]] = false;
var visited = 1;
// Loop through all available cell positions
while (visited < totalCells) {
// Determine neighboring cells
var pot = [[currentCell[0]-1, currentCell[1], 0, 2],
[currentCell[0], currentCell[1]+1, 1, 3],
[currentCell[0]+1, currentCell[1], 2, 0],
[currentCell[0], currentCell[1]-1, 3, 1]];
var neighbors = new Array();
// Determine if each neighboring cell is in game grid, and whether it has already been checked
for (var l = 0; l < 4; l++) {
if (pot[l][0] > -1 && pot[l][0] < y && pot[l][1] > -1 && pot[l][1] < x && unvis[pot[l][0]][pot[l][1]]) { neighbors.push(pot[l]); }
}
// If at least one active neighboring cell has been found
if (neighbors.length) {
// Choose one of the neighbors at random
next = neighbors[Math.floor(Math.random()*neighbors.length)];
// Remove the wall between the current cell and the chosen neighboring cell
cells[currentCell[0]][currentCell[1]][next[2]] = 1;
cells[next[0]][next[1]][next[3]] = 1;
// Mark the neighbor as visited, and set it as the current cell
unvis[next[0]][next[1]] = false;
visited++;
currentCell = [next[0], next[1]];
path.push(currentCell);
}
// Otherwise go back up a step and keep going
else {
currentCell = path.pop();
}
}
return cells;
}
function mazeReset(){
document.location.reload()
}
var mazeComplete = document.getElementById("mazeComplete");
var gameComplete = false;
function checkGameFinished() {
if(gameComplete = false){
mazeComplete.style.visibility = 'hidden'
} else if (gameComplete = true) {
mazeComplete.style.visibility = 'visible'
}
}
I expect when game is launched for
<span id="mazeComplete">Maze complete <button id="resetMaze" onclick="mazeReset()">New Maze</button> </span>
to be hidden until gamecomplete becomes true. but at the moment its not hidden at all.
js fiddle:
https://jsfiddle.net/pma17sfq/3/
There are several things wrong with your code:
You are never calling checkGameFinished(), so the check and the hiding are never performed
By the time your script is executed, the element #mazeComplete does not exist yet. Move your script to the end of the body.
In your if conditions, gameComplete = false and gameComplete = true are not comparing the value, but setting it! Change the operator to === to perform a type-safe comparison.
var disp = newMaze(20, 20);
for (var i = 0; i < disp.length; i++) {
$('#maze > tbody').append("<tr>");
for (var j = 0; j < disp[i].length; j++) {
var selector = i + "-" + j;
$('#maze > tbody').append("<td id='" + selector + "'> </td>");
if (disp[i][j][0] == 0) {
$('#' + selector).css('border-top', '2px solid black');
}
if (disp[i][j][1] == 0) {
$('#' + selector).css('border-right', '2px solid black');
}
if (disp[i][j][2] == 0) {
$('#' + selector).css('border-bottom', '2px solid black');
}
if (disp[i][j][3] == 0) {
$('#' + selector).css('border-left', '2px solid black');
}
}
$('#maze > tbody').append("<tr>");
}
function newMaze(x, y) {
// Establish variables and starting grid
var totalCells = x * y;
var cells = new Array();
var unvis = new Array();
for (var i = 0; i < y; i++) {
cells[i] = new Array();
unvis[i] = new Array();
for (var j = 0; j < x; j++) {
cells[i][j] = [0, 0, 0, 0];
unvis[i][j] = true;
}
}
// Set a random position to start from
var currentCell = [Math.floor(Math.random() * y), Math.floor(Math.random() * x)];
var path = [currentCell];
unvis[currentCell[0]][currentCell[1]] = false;
var visited = 1;
// Loop through all available cell positions
while (visited < totalCells) {
// Determine neighboring cells
var pot = [
[currentCell[0] - 1, currentCell[1], 0, 2],
[currentCell[0], currentCell[1] + 1, 1, 3],
[currentCell[0] + 1, currentCell[1], 2, 0],
[currentCell[0], currentCell[1] - 1, 3, 1]
];
var neighbors = new Array();
// Determine if each neighboring cell is in game grid, and whether it has already been checked
for (var l = 0; l < 4; l++) {
if (pot[l][0] > -1 && pot[l][0] < y && pot[l][1] > -1 && pot[l][1] < x && unvis[pot[l][0]][pot[l][1]]) {
neighbors.push(pot[l]);
}
}
// If at least one active neighboring cell has been found
if (neighbors.length) {
// Choose one of the neighbors at random
next = neighbors[Math.floor(Math.random() * neighbors.length)];
// Remove the wall between the current cell and the chosen neighboring cell
cells[currentCell[0]][currentCell[1]][next[2]] = 1;
cells[next[0]][next[1]][next[3]] = 1;
// Mark the neighbor as visited, and set it as the current cell
unvis[next[0]][next[1]] = false;
visited++;
currentCell = [next[0], next[1]];
path.push(currentCell);
}
// Otherwise go back up a step and keep going
else {
currentCell = path.pop();
}
}
return cells;
}
function mazeReset() {
document.location.reload()
}
var gameComplete = false;
var mazeComplete = document.getElementById("mazeComplete");
function checkGameFinished() {
if (gameComplete === false) {
mazeComplete.style.visibility = 'hidden'
} else if (gameComplete === true) {
mazeComplete.style.visibility = 'visible'
}
}
checkGameFinished();
#maze {
border-collapse: collapse;
}
#maze td {
width: 20px;
height: 20px;
}
canvas {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="maze">
<tbody></tbody>
</table>
<br>
<span id="mazeComplete">Maze complete <button id="resetMaze" onclick="mazeReset()">New Maze</button> </span> Completed <span id="mazeCompletions">0</span>
I agree with #Constantin in general but your most "immediate" problem you have, where "the span is always visible" is because, in your fiddle at least, the <span> is not hidden at start.. you have to make it like:
`<span hidden id="mazeComplete">`
Then later on when the time comes you will set its visibility property like you do.
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.
I've been a long time lurker on Stack Overflow but I couldn't seem to find a suitable existing solution...
I'm learning JS and HTML, and I've been playing around with 2D arrays to make game board. So far I made a custom # of rows/columns for a game board with all white tiles (represented as 0 for now).
My goal is to use an input field for a % of black tiles (represented as 1) to fill up the board (2D Array), but the black tiles have to be randomly distributed/shuffled among it.
Here's what I've got so far..
https://jsfiddle.net/5pvm4mmy/6/
function generateArray() {
var myNode = document.getElementById("table");
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
rows = $("#rows-field").val();
cols = $("#cols-field").val();
concentration = $("#concentration-field").val()
source = $("#source-field").val();
target = $("#target-field").val();
var table = document.getElementById("table");
for (var i = 0; i < rows; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < cols; j++) {
var td = document.createElement('td');
if (i%2 == j%2) {
td.className = "white";
} else {
td.className = "black";
}
tr.appendChild(td);
}
table.appendChild(tr);
}
document.body.appendChild(table);
}
Thanks in advance for any help or advice.
If you need a random selection of a predefined set of values, you can use a stack. Think of it as a deck of cards and you pick a random card each time from the number of card left in the deck. In this case you have only 2 values but you may want to set the number of black and white. For this you can use a pseudo stack.
var black = 29; // 29 blacks the rest white
var white = (rows * cols) - black;
function getRandSquare(){
var select = Math.floor(Math.random() * (black + white));
if(select > black){
white -= 1;
return "white";
}
black -= 1;
return "black";
}
If you have many options like a deck of cards you use an array.
Example of a random stack.
// create a deck
var cards = [];
for(var c = 0; c < 52; c++){
cards[c] = c;
}
function shuffle(cards){
var shuf = []; // to hold the shuffled deck
while(cards.length > 0){ // pick a random item, take it from the stack and
// put on the new stack until there are no items
// left
shuf.push(cards.splice(Math.floor(Math.random() * cards.length),1));
}
return shuf; // return shuffled deck
}
cards = shuffle(cards); // get shuffled deck.
Which will work for anything where you need to pick randomly from a predefined set. It only takes one pass and the set is as random as the random number generator
To show psuedo stack working ... Always has 60 black
var cont;
function draw(){
var rows = 15;
var cols = 15;
var black = 60; // 29 blacks the rest white
var white = (rows * cols) - black;
function getRandSquare(){
var select = Math.floor(Math.random() * (black + white));
if(select > black-1){
white -= 1;
return "white";
}
black -= 1;
return "black";
}
var bCount = 0;
cont = document.createElement("div");
for(var y = 0; y < rows; y++){
for(var x = 0; x < cols; x++){
var s = document.createElement("span");
s.className = getRandSquare();
if(s.className === "black"){
s.textContent = bCount;
bCount += 1;
}
s.style.top = ((y+2) * 20) + "px";
s.style.left = (x * 20) + "px";
s.style.width = "20px";
s.style.height = "20px";
cont.appendChild(s);
}
}
document.body.appendChild(cont);
}
document.body.onclick = function(){
document.body.removeChild(cont);
cont = null;
draw();
}
draw();
span {
position:absolute;
border : 1px solid;
font-size : small;
text-align : center;
}
.black {
background : black;
border-color :white;
color : white;
}
.white {
background : white;
border-color :black;
}
<h3>Click to randomise</h3>
Never mind. I got it done, thanks!
https://jsfiddle.net/5pvm4mmy/8/
function generateArray() {
var myNode = document.getElementById("table");
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
rows = $("#rows-field").val();
cols = $("#cols-field").val();
concentration = $("#concentration-field").val();
source = $("#source-field").val();
target = $("#target-field").val();
var table = document.getElementById("table");
for (var i = 0; i < rows; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < cols; j++) {
var td = document.createElement('td');
if (concentration < Math.floor((Math.random() * 100) + 1)) {
td.className = "white";
} else {
td.className = "black";
}
tr.appendChild(td);
}
table.appendChild(tr);
}
document.body.appendChild(table);
}
Im having an arbitrary 2d array and each field has an id and a teamid (here illustrated as colors 1).
I want for every neighborhood an array with the ids
in it.
A neighborhood consists of fields with neighbors with the same teamid horizontally and vertically (not diagonally)
e.g.:
This is what i have:
array[0][0] = {id:1,teamId:1}
array[1][0] = {id:2,teamId:1}
array[2][0] = {id:3,teamId:0}
array[3][0] = {id:4,teamId:2}
array[4][0] = {id:5,teamId:2}
array[5][0] = {id:6,teamId:0}
array[0][1] = {id:7,teamId:1}
array[1][1] = {id:8,teamId:1}
array[2][1] = {id:9,teamId:1}
array[3][1] = {id:10,teamId:2}
array[4][1] = {id:11,teamId:2}
array[5][1] = {id:12,teamId:0}
//and so on..
This is what i want:
neighborhood[1] = [1,2,7,8,9,13,14]
neighborhood[2] = [4,5,10,11]
neighborhood[3] = [16,22,23,24,29,30]
neighborhood[4] = [25,31,32,37,38]
neighborhood[5] = [35,41]
I am not searching for the images, but for the array
neighborhood
thanks in advance!
You can use the logic from dots and block games. A block belongs to a player if he has surrounded it with the walls. So, you need for each cell also 4 walls except for the outer cells. To test if a cell is closed you can use 4 class variables:
var Block = function() {
this.isclosed=0;
this.left=0;
this.top=0;
this.right=0;
this.bottom=0;
return this;
}
Block.prototype = {
isClosed : function () {
if (this.isclosed==true) {
return false;
} else if (this.left && this.top && this.right && this.bottom) {
this.isclosed=true;
return true;
} else {
return this.left && this.top && this.right && this.bottom;
}
}
}
You can try my implementations of dots and blocks game # https://dotsgame.codeplex.com/.
The method for solving this issue is refered as Connected Component Labelling
A similar question was asked once before from which i have my solution:
// matrix dimensions
var row_count = 20;
var col_count = 20;
var numOfTeams = 2;
// the input matrix
var m = [];
// the labels, 0 means unlabeled
var label = [];
var source = document.getElementById("source");
for (var i = 0; i < row_count; i++) {
var row = source.insertRow(0);
m[i] = [];
label[i] = [];
for (var j = 0; j < col_count; j++) {
//m[i][j] = Math.round(Math.random());
m[i][j] = getRandomInt(0, numOfTeams + 1);
label[i][j] = 0;
var cell1 = row.insertCell(0);
cell1.innerHTML = m[i][j];
}
}
// direction vectors
var dx = [1, 0, -1, 0];
var dy = [0, 1, 0, -1];
function dfs(x, y, current_label, team) {
if (x < 0 || x == row_count) return; // out of bounds
if (y < 0 || y == col_count) return; // out of bounds
if (label[x][y] || team != m[x][y]) return; // already labeled or not marked with 1 in m
// mark the current cell
label[x][y] = current_label;
// recursively mark the neighbors
for (var direction = 0; direction < 4; ++direction) {
dfs(x + dx[direction], y + dy[direction], current_label, team);
}
}
function find_components() {
var component = 0;
for (var i = 0; i < row_count; ++i) {
for (var j = 0; j < col_count; ++j) {
if (!label[i][j] && m[i][j]) dfs(i, j, ++component, m[i][j]);
}
}
}
find_components();
var result = document.getElementById("result");
for (var i in label) {
var string = ""
var row = result.insertRow(0);
for (var j in label[i]) {
string += label[i][j] + " "
var cell1 = row.insertCell(0);
cell1.innerHTML = label[i][j];
}
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
table tr td {
min-width: 14px
}
<div style="float:left">
<table id="source"></table>
</div>
<div style="float:right">
<table id="result"></table>
</div>