Tabletop Map Tool Javascript Help - Creating characters on the fly cannot cycle cells

sikuneh

New Member
Messages
55
Reaction score
0
Points
0
After the earlier debacle with FireBug and that problem being fixed, I ran into a new one. I'm working on the create character entities input which will allow you to put a first/last name specify which cell (in a 20x20 grid) that character occupies and give it a codename to shorten the occupied space and prevent cols/rows getting too large. I've got it up to where it cycles through the cells and places it on the cell where the user specified x,y position but whenever it gets to the loop it says rows is undefined.

I used a loop to cycle through rows and then a nested loop to search through columns.

Code:
function createCharacters() {
    var form_els = document.forms['create_character'].elements;
    var bol_errors = false;
    // Check for empty fields
    for (var i = 0; i < form_els.length; i++) {
        if (form_els[i].value.length == 0) {
            document.getElementById(form_els[i].name + "_help").innerHTML = form_els[i].name + " is invalid.";
            bol_errors = true;
        }
    }
    
    // Check for position within 20,20
     if (form_els['posx'].value > 20 && form_els['posx'].value  < 1 || form_els['posy'].value > 20 &&  form_els['posy'].value < 1) {
        document.getElementById("posy_help").innerHTML = "Must be between 1 and 20";
        bol_errors = true;
    }
    
    if (bol_errors) {
        return false;
    }
    
    var rows = document.getElementById("battle_grid").getElementsByTagName("tr");
    for (var y = 1; y <= rows.length; y++) {
        var cols = rows[y].getElementsByTagName("td");
        for (var x = 1; x <= cols.length; x++) {
            if (form_els['posx'].value == x && form_els['posy'].value == y) {
                var cells = document.getElementById("battle_grid").getElementsByTagName("td");
                var newY = y * 20;
                var newX = x;
                var cur_cell = newY+newX;
                
                cells[cur_cell].setAttribute("occupant", form_els['key']);
            }
        }
    }
}

Thanks in advance.
 
Last edited:

descalzo

Grim Squeaker
Community Support
Messages
9,373
Reaction score
326
Points
83
Re: Tabletop Map Tool Javascript Help - Creating characters on the fly cannot cycle c

Since it depends on your HTML (ie, do you really have an element with id (not name) set to "battle_grid") and you don't show the HTML ...
 

sikuneh

New Member
Messages
55
Reaction score
0
Points
0
Re: Tabletop Map Tool Javascript Help - Creating characters on the fly cannot cycle c

My bad for not showing the HTML, although I dynamically create the HTML through Javascript. And yes I did make it an ID rather than a name so we can rule that out.

Here is the part that creates the battle grid:

Code:
window.onload = initPage;

function initPage() {
	const KN_ROW_NUM = 20;
	const KN_COL_NUM = 20;
	const KCOL_GRID_OUTPUT = document.getElementById("grid");
	var s_grid_output = "<table border='2' id='battle_grid'>";
	
	document.getElementById("status").style.display = 'none';
	
	for (var i = 1; i <= KN_ROW_NUM; i++) {
		s_grid_output += "<tr>";
		for (var j = 1; j <= KN_COL_NUM; j++) {
			if (j == 1 && i == 1) {
				s_grid_output += "<td posx='"+j+"' posy='"+i+"' title='("+j+","+i+")' altitude='0' obstacle='0' occupant='TEST'></td>";
			}
			else {
				s_grid_output += "<td posx='"+j+"' posy='"+i+"' title='("+j+","+i+")' altitude='0' obstacle='0' occupant=''></td>";
			}
		}
		s_grid_output += "</tr>";
	}
	s_grid_output += "</table>";
	
	KCOL_GRID_OUTPUT.innerHTML = s_grid_output;
	
	// Update the grid with this new information
	updateGrid();
	
	var col_cells = document.getElementById("grid").getElementsByTagName("td");
	
	for (var i = 0; i < col_cells.length; i++) {
		col_cells[i].onclick=function() {
			if (gn_input_type == GKN_INPUT_MOVE_KEY) {
				move(this);
			}
			else if (gn_input_type == GKN_INPUT_ALT_KEY) {
				changeAltitude(this);
			}
			else if (gn_input_type == GKN_INPUT_OBSTACLE_KEY) {
				createObstacles(this);
			}
			else {
				alert("There was an error when selecting the input key. Current value: " + gn_input_type);
			}
			updateGrid();
		}
	}
	
	return true;
}

I made a slight change to the code in the first post. I added a call to the updateGrid() function just before createCharacters() ends. Here is updateGrid in case you need it.

Code:
function updateGrid() {
	var cells = document.getElementById("battle_grid").getElementsByTagName("td");

	// Loop through each grid cell
	for (var i = 0; i < cells.length; i++) {
		var cell = cells[i];
		var occupant = cell.getAttribute("occupant");
		var altitude = cell.getAttribute("altitude");
		var obstacle = cell.getAttribute("obstacle");
		// Set the cell contents by occupant attribute
		cell.innerHTML = occupant;
		// Change the cell's color by its altitude
		cell.style.background = GKA_ALT_COLORS[altitude];
		
		// Check if this cell is an obstacle. If yes, override the altitude color change
		if (obstacle > 0) {
			cell.style.background = GKA_OBSTACLE_COLORS[obstacle];
		}
		
		cell.title = "(" + cell.getAttribute("posx") + "," + cell.getAttribute("posy") + ") | Altitude: " + altitude + " | Obstacle: " + GKA_OBSTACLE_VERBOSE[obstacle];
	}
	return true;
}
 

descalzo

Grim Squeaker
Community Support
Messages
9,373
Reaction score
326
Points
83
Re: Tabletop Map Tool Javascript Help - Creating characters on the fly cannot cycle c

You have a page on the Web to see if your dynamic code works?
 

sikuneh

New Member
Messages
55
Reaction score
0
Points
0
Re: Tabletop Map Tool Javascript Help - Creating characters on the fly cannot cycle c

I do not at the moment, it's local while it's testing. I can provide my entire code which is relatively small.

HTML:
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
	<head>
		<style type="text/css">
td {
	cursor: 		pointer;
	height:			40px;
	text-align: 	center;
	width:			40px;
}

ul, li {
	list-style-type:none;
	margin:			0;
	padding: 		0;
}

#inputs_container {
	height:			300px;
}

#inputs {
	border-right: 	1px solid black;
	clear: 			left;
	float: 			left;
	width: 			150px;
}

#input_options {
	float: 			left;
	width: 			500px;
}

#status {
	background: 	#fff url('images/status.gif') no-repeat;
	padding:		8px;
}
                </style>
                <script type="text/javascript" src="scripts/map.js"></script>
	</head>
	<body>
		<div id="titles">
		<h2 style="float: left; width:200px;">Input Type:</h2>
		<h2 style="float: left; width:300px;">Input Options:</h2>
		</div>
		<div id="inputs_container">
			<div id="inputs">
				<form action="#" method="get" id="inputs_form" name="inputs_form">
					<ul>
						<li onclick="changeInputType()"><input type="radio" name="input_types" id="input_type_move" value="0" /> <label for="input_type_move">Move</label></li>
						<li onclick="changeInputType()"><input type="radio" name="input_types" id="input_type_alt" value="1" /> <label for="input_type_alt">Altitude</label></li>
						<li onclick="changeInputType()"><input type="radio" name="input_types" id="input_type_create" value="2" /> <label for="input_type_create">Create Characters</label></li>
						<li onclick="changeInputType()"><input type="radio" name="input_types" id="input_type_obstacle" value="3" /> <label for="input_type_obstacle">Obstalces</label></li>
					</ul>
				</form>
			</div>
			<div id="input_options">
			</div>
		</div>
		<div id="status">&nbsp;</div>
		<div style="clear:both; height:20px;"></div>
		<div id="grid">
		</div>
		<div id="key">
		</div>
	</body>
</html>

And map.js

Code:
window.onload = initPage;

// Globals
var gs_buffer = '';
const GKN_INPUT_MOVE_KEY = 0;
const GKN_INPUT_ALT_KEY = 1;
const GKN_INPUT_CREATE_KEY = 2;
const GKN_INPUT_OBSTACLE_KEY = 3;
var ga_obstacle_colors_proto = new Array();
ga_obstacle_colors_proto[0] = "white";
ga_obstacle_colors_proto[1] = "gray";
ga_obstacle_colors_proto[2] = "black";
const GKA_OBSTACLE_COLORS = ga_obstacle_colors_proto; // Store in a constant value to avoid changes
var ga_alt_colors = new Array();
ga_alt_colors[0] = "white";
ga_alt_colors[1] = "yellow";
ga_alt_colors[2] = "orange";
ga_alt_colors[3] = "red";
const GKA_ALT_COLORS = ga_alt_colors; // Store in a constant value to avoid changes
var ga_obstacle_verbose = new Array();
ga_obstacle_verbose[0] = "None";
ga_obstacle_verbose[1] = "Half";
ga_obstacle_verbose[2] = "Full";
const GKA_OBSTACLE_VERBOSE = ga_obstacle_verbose;
var gn_input_type = 0;

function initPage() {
	const KN_ROW_NUM = 20;
	const KN_COL_NUM = 20;
	const KCOL_GRID_OUTPUT = document.getElementById("grid");
	var s_grid_output = "<table border='2' id='battle_grid'>";
	
	document.getElementById("status").style.display = 'none';
	
	for (var i = 1; i <= KN_ROW_NUM; i++) {
		s_grid_output += "<tr>";
		for (var j = 1; j <= KN_COL_NUM; j++) {
			s_grid_output += "<td posx='"+j+"' posy='"+i+"' title='("+j+","+i+")' altitude='0' obstacle='0' occupant=''></td>";
		}
		s_grid_output += "</tr>";
	}
	s_grid_output += "</table>";
	
	KCOL_GRID_OUTPUT.innerHTML = s_grid_output;
	
	// Update the grid with this new information
	updateGrid();
	
	var col_cells = document.getElementById("grid").getElementsByTagName("td");
	
	for (var i = 0; i < col_cells.length; i++) {
		col_cells[i].onclick=function() {
			if (gn_input_type == GKN_INPUT_MOVE_KEY) {
				move(this);
			}
			else if (gn_input_type == GKN_INPUT_ALT_KEY) {
				changeAltitude(this);
			}
			else if (gn_input_type == GKN_INPUT_OBSTACLE_KEY) {
				createObstacles(this);
			}
			else {
				alert("There was an error when selecting the input key. Current value: " + gn_input_type);
			}
			updateGrid();
		}
	}
	
	return true;
}

function changeInputType() {
	var col_inputs = document.forms[0].elements['input_types'];
	for (var i = 0; i < col_inputs.length; i++) {
		if (col_inputs[i].checked) {
			gn_input_type = col_inputs[i].value;
			break;
		}
	}
	
	// Show additional options for inputs
	var input_options_output = '';
	
	if (gn_input_type == GKN_INPUT_MOVE_KEY) {
		// Show nothing as there are no options
	}
	
	else if (gn_input_type == GKN_INPUT_ALT_KEY) {
		input_options_output = "<form name='alt_form' id='alt_form'>Altitude: <select name='altitude' id='altitude'>";
		for (i = 0; i <= 3; i++) {
			input_options_output += "<option value='" + i + "'>" + i + "</option>";
		}
		input_options_output += "</select></form>";
	}
	
	else if (gn_input_type == GKN_INPUT_CREATE_KEY) {
		input_options_output = "<form name='create_character' action='#' method='post'>";
		input_options_output += "<p><label>First Name: </label> <input type='text' name='first_name' /> <span id='first_name_help'></span></p>";
		input_options_output += "<p><label>Last Name: </label> <input type='text' name='last_name' /> <span id='last_name_help'></span></p>";
		input_options_output += "<p><label>Position: </label> (x,y) (<input type='text' name='posx' style='width:15px;' />, <input type='text' name='posy' style='width:15px;' />) <span id='posx_help'></span> <span id='posy_help'></span></p>";
		input_options_output += "<p><label>Key: </label> <input type='text' name='key' style='width:20px;' /> <span id='key_help'></span></p>";
		input_options_output += "<p><input type='button' onclick='createCharacters(); return false;' value='Create Character' /></p>";
		input_options_output += "</form>";
	}

	else if (gn_input_type == GKN_INPUT_OBSTACLE_KEY) {
		input_options_output = "<form action='#' method='get' name='obstacle_form'>";
		input_options_output += "<input type='radio' name='obstacle' value='0' id='obstacle_none' /> <label for='obstacle_none'>None</label><br />";
		input_options_output += "<input type='radio' name='obstacle' value='1' id='obstacle_half' /> <label for='obstacle_half'>Half</label><br />";
		input_options_output += "<input type='radio' name='obstacle' value='2' id='obstacle_full' /> <label for='obstacle_full'>Full</label>";
		input_options_output += "</form>";
	}
	
	document.getElementById("input_options").innerHTML = input_options_output;
	
	return true;
}

function updateGrid() {
	var cells = document.getElementById("battle_grid").getElementsByTagName("td");

	// Loop through each grid cell
	for (var i = 0; i < cells.length; i++) {
		var cell = cells[i];
		var occupant = cell.getAttribute("occupant");
		var altitude = cell.getAttribute("altitude");
		var obstacle = cell.getAttribute("obstacle");
		// Set the cell contents by occupant attribute
		cell.innerHTML = occupant;
		// Change the cell's color by its altitude
		cell.style.background = GKA_ALT_COLORS[altitude];
		
		// Check if this cell is an obstacle. If yes, override the altitude color change
		if (obstacle > 0) {
			cell.style.background = GKA_OBSTACLE_COLORS[obstacle];
		}
		
		cell.title = "(" + cell.getAttribute("posx") + "," + cell.getAttribute("posy") + ") | Altitude: " + altitude + " | Obstacle: " + GKA_OBSTACLE_VERBOSE[obstacle];
	}
	return true;
}

function move(e) {
	var occupant = e.getAttribute("occupant");
	// If the cell is an obstacle, don't let a unit occupy it
	if (e.getAttribute("obstacle") > 0) {
		alert("Cannot place unit on an obstacle");
		return false;
	}
	// Check if there is a character in the buffer waiting to be moved
	if (gs_buffer != '' && occupant == '') {
		e.setAttribute("occupant", gs_buffer);
		gs_buffer = '';
	}
	else if (gs_buffer == '' && occupant != '') {
		gs_buffer = occupant;
		e.setAttribute("occupant", '');
	}
	else {
		alert("This square is occupied or you do not have a unit to move.");
	}
	return true;
}

function changeAltitude(e) {
	var altitude_ddl = document.forms['alt_form'].elements['altitude'];
	new_altitude = altitude_ddl.options[altitude.selectedIndex].value;

	e.setAttribute("altitude",new_altitude);
	return true;
}

function createCharacters() {
	var form_els = document.forms['create_character'].elements;
	var bol_errors = false;
	// Check for empty fields
	for (var i = 0; i < form_els.length; i++) {
		if (form_els[i].value.length == 0) {
			document.getElementById(form_els[i].name + "_help").innerHTML = form_els[i].name + " is invalid.";
			bol_errors = true;
		}
	}
	
	// Check for position within 20,20
	if (form_els['posx'].value > 20 && form_els['posx'].value < 1 || form_els['posy'].value > 20 && form_els['posy'].value < 1) {
		document.getElementById("posy_help").innerHTML = "Must be between 1 and 20";
		bol_errors = true;
	}
	
	if (bol_errors) {
		return false;
	}
	
	var rows = document.getElementById("battle_grid").getElementsByTagName("tr");
	for (var y = 1; y <= rows.length; y++) {
		var cols = rows[y].getElementsByTagName("td");
		for (var x = 1; x <= cols.length; x++) {
			if (form_els['posx'].value == x && form_els['posy'].value == y) {
				var cells = document.getElementById("battle_grid").getElementsByTagName("td");
				var newY = y * 20;
				var newX = x;
				var cur_cell = newY+newX;
				
				cells[cur_cell].setAttribute("occupant", form_els['key']);
			}
		}
	}
	updateGrid();
}

function createObstacles(e) {
	var new_obstacle = document.forms['obstacle_form'].elements['obstacle'];
	var new_obstacle_value = 0;
	for (var i = 0; i < new_obstacle.length; i++) {
		if (new_obstacle[i].checked) {
			new_obstacle_value = new_obstacle[i].value;
			break;
		}
	}
	
	e.setAttribute("obstacle", new_obstacle_value);
	return true;
}

Edit1: The strange thing is, when I use an alert to debug it writing alert(rows[y].length), it always says 20 but Firefox's Error Console says "rows[y] is undefined."
 
Last edited:

sikuneh

New Member
Messages
55
Reaction score
0
Points
0
Re: Tabletop Map Tool Javascript Help - Creating characters on the fly cannot cycle c

So narrowing it down little by little, chrome game me a little insight. It is indeed the cols variable but it can't find all the elements with the tag of td. So in verbose, is this incorrect and is there a better way to do this?

document.getElementById("battle_grid").getElementsByTagName("tr").getElementsByTagName("td");

Edit: Those spaces in the line of code are put there by this forum, they are not in the actual code.
 
Last edited:

essellar

Community Advocate
Community Support
Messages
3,295
Reaction score
227
Points
63
Re: Tabletop Map Tool Javascript Help - Creating characters on the fly cannot cycle c

Are you just running into an off-by-one error? Arrays (and objects treated as arrays) are zero-indexed, so elementArray[elementArray.length] is an out-of-bounds index. Well, unless you have a text key (remember, JS arrays aren't arrays in the ordinary sense; they're key-value pairs with properties inherited from Array.prototype) in the array that would be the same as the array's length if it ere an integer, then automatic type coercion would fetch that element.

To put in another way, an array having a length of 20 has elements 0 to 19, but doesn't have an element 20.
 

sikuneh

New Member
Messages
55
Reaction score
0
Points
0
Re: Tabletop Map Tool Javascript Help - Creating characters on the fly cannot cycle c

It's all relative so I don't think it's an off-by-one. I get rows.length and start the loop at 0. It says rows[y] is undefined rather than rows[20] is undefined so I don't think it has to do with the loop.
 

essellar

Community Advocate
Community Support
Messages
3,295
Reaction score
227
Points
63
Re: Tabletop Map Tool Javascript Help - Creating characters on the fly cannot cycle c

The debugger/console is telling you that the entity represented by the expression rows[y] is undefined when the variable y has a value of rows.length. If you use a debugger, set a break point in the loop and watch the values — it's not a lexing or parsing issue, where the console will tell you that you're using something that hasn't been defined in code; it's a run-time problem. Watch the value of y on each loop. You'll see that you're getting the error on the last pass, where y becomes an out-of bound index.
 

sikuneh

New Member
Messages
55
Reaction score
0
Points
0
Re: Tabletop Map Tool Javascript Help - Creating characters on the fly cannot cycle c

Great! Thanks, I finally got it working. That off-by-one can kill you.
 
Top