I have the most peculiar issue I can't seem to solve without writing band-aid code.
FULL SOURCE:
http://sinsysonline.com/tictactoe_test.html
or Fiddle:
http://jsfiddle.net/JsXEP/
I am using a multidimensional array for the JS and a table for the HTML for data and display purposes.
So, our data will look something like this for JS:
var b = [ ["","",""], ["","",""], ["","",""] ];
The table will be a standard table with some CSS tweaks for our HTML:
<table id="board">
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
</table>
So, here is the problem. The input works just fine, resetting the game appears to work fine, but let's make a use-case so I can demonstrate the problem.
If I utilize a grid size of 3 initially, then try to make a game with a grid size of 4 without refreshing the page, the data seems to display at the top right for my array that the code is working as intended.
However, when you console.log(b), it never actually updates b and anything after the first three rows in the table don't respond.
I'm given a console error of:
TypeError: b[row] is undefined
Why isn't b actually updating with the increased value, even though I define it as var b=[]; at the beginning of my click-handler for #go?
And why is my #alert box for debugging filling in the correct b value?
Scratches Head
Any help is appreciated...
HTML:
<div id="dashboard">
<p>How large is your grid? (3-10)</p>
<input type="text" id="size" size="1" />
<p>Which icon would you like?</p>
<select name="mydropdown" id="mark">
<option value="check">Check-Mark</option>
<option value="x">Traditonal X</option>
<option value="o">Traditional O</option>
</select>
<h3 id="title">Troubleshooting Console</h3>
<input type="button" id="go" value="Create Board / Reset" />
<p id="alert">Alerts Live Here</p>
</div>
<table id="board">
</table>
Javascript:
$("#go").click(function () {
var b=[],
s = parseInt($("#size").val()),
v = $("#mark").val();
if (s<3 || s>10) { alert("That is not a valid input. Please select 3-10"); return; }
$('#board tr').remove();
$('#alert').text("Your Turn!");
for (var i = 0; i < s; i++) {
var t = [];
var $tr = $('<tr />').data('index', i + 1);
for (var j = 0; j < s; j++) {
t[j] = "";
$('<td />').data('index', j + 1).appendTo($tr);
}
b.push(t);
$("#board").append($tr);
}
$("#board").on('click', 'td', function () {
var $td = $(this),
td = $td.data('index')-1,
row = $td.parent().data('index')-1;
b[row][td] = "X";
$td.removeClass().addClass(v);
$('#alert').text(b);
});
});
FULL SOURCE:
http://sinsysonline.com/tictactoe_test.html
or Fiddle:
http://jsfiddle.net/JsXEP/
The problem is because of the closure property and multiple definitions of on click function. Updated Fiddle
$("#board").on('click', 'td', function () {
var $td = $(this),
td = $td.data('index')-1,
row = $td.parent().data('index')-1;
b[row][td] = "X";
$td.removeClass().addClass(v);
$('#alert').text(b);
});
This function is registered every time user changes the settings and whenever it is defined, it captures the b. So, first time, lets say, b is [3][3] and the on click function is created with that. Next time, b is [4][4] and a new on click function is created (old one still exists, with b [3][3]). So both of them are fired now onwards. If I select anything outside 3 x 3, the old one fails. That's why you are facing this problem.
So, I just moved the definition of on click function, b and v outside and it works fine now.
Related
I've been tasked with a demo for a query API our company has. For that, my boss wanted me to create a simple HTML site containing a search feature to use said API. So far, I've managed to create almost everything she wanted, but am now stuck on a rather... Peculiar problem.
I've managed to create a flexible filtering feature, allowing the user to add/remove filters as they see fit. I've done it like this:
window.onload = function init()
{
var filterSet = document.getElementsByTagName("fieldset")[0];
var filterSection = filterSet.getElementsByTagName("section");
var set = filterSection[1];
var elements = set.getElementsByTagName("*");
set.getElementsByTagName("*")[5].addEventListener("click", cloneSection);
if (filterSection.length > 2)
{
elements[6].hidden = false;
elements[6].addEventListener("click", deleteSection);
}
else
{
elements[6].hidden = true;
}
}
function cloneSection(e)
{
var filterSet = document.getElementsByTagName("fieldset")[0];
var filterSection = filterSet.getElementsByTagName("section");
var newId = parseInt(filterSection[filterSection.length - 1].id) + 1;
var set = filterSection[1];
var newSet = set.cloneNode(true);
var elements = newSet.getElementsByTagName("*");
newSet.id = "" + newId;
elements[5].addEventListener("click", cloneSection);
elements[6].hidden = false;
elements[6].addEventListener("click", deleteSection);
filterSet.appendChild(newSet);
}
function deleteSection(e)
{
var target = e.target;
var filterSet = document.getElementsByTagName("fieldset")[1];
var filterSection = target.parentElement;
filterSection.remove();
}
<fieldset>
<section>
<input type="checkbox" id="filter_query" name="feature" value="search" />
</section>
<section id="0">
<input type="text" name="filter_name">
<select id="comperason_type">
<option value="=">Equal to</option>
<option value="!=">Different then</option>
</select>
<input type="text" name="filter_value">
<input type="submit" value="+" id="AddButton">
<input type="submit" value="-" id="RemoveButton" hidden="true">
</section>
</fieldset>
It's rather basic (I'm kinda new to networking and Javascript as a whole, I'll admit), but for the most part it works just fine. Except for one single issue.
I want, when the user adds a new filter, to allow them to remove the default one created by the original markup. The button is there, I just need to set it to hidden = false and that's it. Except... When I try to do that, for some reason, the code doesn't recognize the extra section created into it. For example, if there are 3 sections in the fieldset, one created by my own code, the length count will only return 2.
Does anyone know why? Or what can I do to get it to count properly? I'm sorry if this is a newb question, but like I said, I'm a bit out of my depth here.
EDIT: Made a snippet that works this time. Sorry for making it confusing, but it should be good now.
I am trying to create div using javascript and jquery.
My Code so far:
<script>
var numOfWindows = 3;
var arrayDiv = new Array();
for (var i = 0; i < numOfWindows; i++)
{
var newDiv = $('#server div:first').clone();
$('#server').append(newDiv);
}
</script>
<input type="text" name="numserver"><br>
<button onclick="new_server()">GO</button>
<br>
<div id="server">
<div id="1">
<table border="3"><tbody>
<tr><th colspan="4" style="background-color:#b0c4de;">Server 1</th></tr>
<br>
<tr><td>Technology<select name="tech[]"><option value="w">Web</option><option value="d">DB</option><option value="m">Mail</option><option value="o">Other</option></select><br>
<br></td>
<td>CPU? <input type="text" name="cpu[]"><br></td>
<td>Memory? <input type="text" name="memory[]"><br></td>
<td>Disk Space? <input type="text" name="space[]"><br></td></tr>
<br>
</tbody></table>
</div>
</div>
My end result is for the user to be able to enter the amount of servers and click GO and then the divs are automatically created.
I know how to get the numOfWindows value but i think it should work with a static value for now.
The code is correct, but how says adeneo, you don't have a DOM ready handler.
Use instead something like this:
function LoadMyJs(){
var numOfWindows = 3;
var arrayDiv = new Array();
for (var i = 0; i < numOfWindows; i++)
{
var newDiv = $('#server div:first').clone();
$('#server').append(newDiv);
}
}
<body onLoad="LoadMyJs()">
You are trying to run the script which works with undefined HTML. To solve this you can either move your script after defining HTML or use jquery shortcut document.ready function such as $(function(){<your code here>});.
In first case event handler has to be a global variable right away. Otherwise you declare this variable before shortcut scope.
Next you define that event handler function obtains input value ($('input[name="numserver"]').val()). And now you can generate as many divs as defined by the value(Array.apply(null, [+value]).map(function(){<generator here>})). To generate copies clone method may be used and generated divs should be inserted into div container as you do.
Looks like you're trying to generate divs by "GO" button click. In your case to do that event handler function should be named "new_server". And you don't call this function in "onclick" attribute you just declare it.
I'm trying to create a new use for an already implemented tool we use here at work, but I'm very sure I'm doing something wrong.
I can't figure out how to make it delete a row. And even more, I can figure out how to clone everything within .pt-entry, and have it replicated inside of the incremental .pt-entry...but without the user filled in info.
Hopefully this makes sense.
You can check out my Pen here, but here's the code breakdown for the rest of yous:
HTML:
<table class="manage-pt" id="0">
<tr class="pt-entry">
<td class="pt-toggle-group">
<input type="button" class="pt-button togPTbutton" value="?" />
<input type="button" class="pt-button addPTbutton" value="+" />
<input type="button" class="pt-button delPTbutton" value="-" />
</td>
<td class="pt-values">
<div>
<input type="text" class="vendor" placeholder="Vendor Name" />
</div>
<div>
<textarea class="ptCode" name="ptCode" placeholder="Pixel Tag Code" ></textarea>
</div>
<div class="page-select">
<select>
<option value="AllPages">All Pages</option>
<option value="HomePage">HomePage</option>
<option value="VehicleDetailsPage">VehicleDetailsPage</option>
<option value="VehicleSearchResults">VehicleSearchResults</option>
<option value="ContactUsForm">ContactUsForm</option>
</select>
</div>
<div class="area-checkboxes">
<p class="wheretosave">Where?</p>
<input type="checkbox" name="head" /><label for="head">Head</label>
<input type="checkbox" name="body" /><label for="body">Body</label>
</div>
<div class="save-pt">
<input value="SAVE" type="submit" />
</div>
</td>
</tr>
</table>
JavaScript:
// HIDES CURRENT PT & CHANGES TOGGLE BUTTON ICON WHEN CLICKED
$('.togPTbutton').click(function(){
$('.pt-values').slideToggle(25, function() {
if ($('.pt-values').is(':hidden')) {
$('input.togPTbutton').val('?');
}else{
$('input.togPTbutton').val('?');
}
});
});
$(document).ready(function () {
var table = $('.manage-pt'),
rows = $(table).find('tr'),
rowCount = $(rows).length,
addedRow = $(document.createElement('tr')),
addButton = $('.addPTbutton'),
removeButton = $('.delPTbutton');
function addRow(){
var thisRow = $(addedRow).clone(true);
$(thisRow).attr('class','.pt-entry-' + rowCount);
rowCount += 1;
$(thisRow).html('<td>row</td>');
$(table).append(thisRow);
}
function removeRow(){
var items = $(table).querySelectorAll('tr');
if (rowCount > 1) {
$(table).remove(items[rowCount - 1]);
rowCount -= 1;
}else{
alert('CANNOT DELETE LAST ROW');
}
}
addButton.click(function(e){
addRow();
});
removeButton.click(function(e){
removeRow();
});
});
Should look close to something like this mockup ...
Alright, I think I found your problem. You are trying to call $(table).querySelectorAll('tr'). .querySelectorAll is a javascript function that you are using with a JQuery selector. This is where your removeRow() function bombs. Try commenting that line out. Then, you will need to find a new way to select the last row, which can easily be done with this:
$(table).find('tr:last').remove();
Final form:
function removeRow(){
//var items = $(table).querySelectorAll('tr');
if (rowCount > 1) {
//$(table).remove(items[rowCount - 1]);
$(table).find('tr:last').remove();
rowCount -= 1;
}
else
{
alert('CANNOT DELETE LAST ROW');
}
}
If you want this to work in IE8 and older, you can use this JQuery since you have the number of rows:
$(table).find('tr').eq(rowCount - 1).remove();
in place of:
$(table).find('tr:last').remove();
JSFiddle: http://jsfiddle.net/Em8Q5/2/
EDIT: ------------------------------------------------------------------------------------
Alright, I found a solution being able to delete the current row.
First, allow a parameter into your removeRow function and switch the selector to use closest:
function removeRow(currRow){
//var items = $(table).querySelectorAll('tr');
if (rowCount > 1) {
//$(table).remove(items[rowCount - 1]);
currRow.closest('tr').remove();
rowCount -= 1;
}
else
{
alert('CANNOT DELETE LAST ROW');
}
}
Then you will need to modify your .click function so that it will change dynamically as you add/remove rows/buttons. Also, note the parameter so it knows which row's button is clicked.
//removeButton.click(function(e){
$('body').on("click", ".delPTbutton", function(e) {
removeRow($(this)); //Note the parameter that we added so it knows which row to remove
});
JSfiddle: http://jsfiddle.net/Em8Q5/3/
I check up your code using chrome developer tool and when i clicked the delPTbutton button to remove last row it showed an error in your removeRow Method , The Error Message Is :
(Uncaught TypeError: Object [object Object] has no method 'querySelectorAll')
The Issue Here Is That 'querySelectorAll' Is One Of javascript base api but you use it after a jquery object.
Consider Using $(table).find('tr') Instead .
Ok so thank you #Chad for your help. You were seriously a life saver.
I took what you did, one step further in this new version of the code:
New CodePen
However I now have a very js minor issue. I have it set to turn the ".vendor" input to read only when the relevant stack is closed, however I need to target something other than ".vendor", because it's effecting each clones ".vendor" as well.
Would it be something like:
$(this).parent().next().child().child();?
I've been tasked with building a very simple app that that has a series of dropdowns in rows of 2, when 2 are selected, a simple functions concatenates the 2 values and gives an output next to them like so:
dropdown1 dropdown2 Output
What I'm trying to get is, once the second dropdown value is chosen the function runs and displays the output where it says output. But currently, what seems to happens is the output is displayed in a new window.
Here's what I have so far (HTML):
<form>
<select id="test">
<option>Arena/Quantum Barcelona LTBC</option>
<option>Arena/Quantum Spain LTES</option>
</select>
<select id="name" onchange="tryThis()">
<option>Name</option>
<option>Name1</option>
</select>
</form>
JavaScript:
function tryThis() {
var string, string1 = '';
var e = document.getElementById("test");
string = e.options[e.selectedIndex].text;
var a = document.getElementById("name");
string1 = a.options[a.selectedIndex].text;
document.write(string+'_'+string1);
}
Am I making this more difficult than it needs to be?!
That's because document.write clears the page before displaying something. You should never need to use that function.
Instead, you could append it to e.g. the body:
document.body.appendChild(
document.createTextNode(string + '_' + string2)
);
Have you noticed that your JS function is called tryThis() and on the event handler you're calling tryThsis()
However in your case I'd refrain from using document.write, good alternatives are appending to the body or having a DIV and changing the innerHTML of that DIV
First put an id on your form so that it is easier to access.
var a = (function () {
var myForm = document.getElementById("myForm"),
magic = function () {
var a = myForm.getElementsByTagName("select"),
b,
c = [];
for (b = a.length - 1; b > -1; b -= 1) {
c.push(a[b].value);
}
alert(c.join(" ") + " output");
};
myForm.onclick = magic;
}());
You were not specific as to what the extra "output" is supposed to be or how you want the data returned, but here you go. Instead of using an alert you could push the result into the value of a different form element. Do not use document.write as this cannot be deferred. If you attempt to defer a document.write operation it will replace the entirety of the body contents of the current page.
Surely, this shouldn't be so hard?
I have a <select>, which has, of course, <options>. These options are always in number format, because they are dynamically added to the list by the user.
I then need to get all of these options from the list, put them an array and then perform logic on the array. I've tried searching around, but everything relates to jquery or php - and I'm using plain old HTML and JavaScript.
The select is in a scrolling-box format:
<select id="selectBox" name="select" size="15" style="width:190px;">
<!-- <options> are added via javascript -->
</select>
Currently, I'm using this JavaScript to get the elements, but it's not working:
//Calculate all numbers
var x=[];
function calculate()
{
for (var i = 0; i < 999; i++)
{
x[i]=selectbox.options[i].value;
alert(x[i]);
}
}
Calculate() is called by a button. Something is going terribly wrong, and I can't work it out. selectbox is previously defined as var selectbox = document.getElementById("selectBox"); and I know this works.
The alert is only being called so I can try to debug the thing...
I'm using the figure of 999 because I can't work out how to get a number of how many elements are in the <select> (because it is in scrolling-box format).
The solution must be javascript, and the listbox must be in that scrolling-box format.
Thanks in advance for your help!
Edit -- Okay, more coding to help this.
<form id="aggregateForm">
<input id="inputNum" value="" type="text"><input id="addnum" value="Add" onclick="add();" type="button">
<br>
<select id="selectBox" name="select" size="15" style="width:190px;">
</select>
<br>
<input id="calculate" value="Calculate" onclick="calculate();" type="button"><input id="reset" value="Reset" onclick="reset();" type="button">
</form>
<script type="text/javascript">
var selectbox = document.getElementById("selectBox");
function add()
{
//Function to add a new number to the list of digits
//Parses an integer to ensure everything works okay
if(IsNumeric(document.getElementById("inputNum").value) == true)
{
selectbox.options[selectbox.options.length] = new Option(document.getElementById("inputNum").value, document.getElementById("inputNum").value);
inputNum.focus();
}
else if(IsNumeric(document.getElementById("inputNum").value) == false)
{
alert("I'm sorry, but you have entered an invalid number. Please enter a number into the input box.");
inputNum.focus();
}
}
//Calculate all numbers
var x=[];
function calculate()
{
for (var i = 0; i <selectbox.options.length; i++)
{
x[i]=selectbox.options[i].value;
alert(x[i]);
}
}
//IsNumeric function coding taken from http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric, code by Joel Coehoorn
function IsNumeric(input)
{
return (input - 0) == input && input.length > 0;
}
</script>
The problem is that calculate is the ID of the element too. And oddly enough it believes that calculate is that DOM object not you function: proof. I changed the function name to calculates.
I only found out last week that you can reference your elements with IDs with said IDs.
<div id="really">Yep for some reason</div>
... later in javascript
// no document.getElementById, just
// let me be very clear, THIS IS WRONG TO USE VERY BAD SO EVERYONE CAN KNOW NOT TO USE THIS
// but it does work this way so be aware
really.innerHTML = "I guess he was right.";
check this jsfiddle
var selectbox = document.getElementById("selectBox");
var x = [];
function calculate()
{
for (var i = 0; i <selectbox.options.length; i++)
{
x[i]=selectbox.options[i].value;
alert(x[i]);
}
}
calculate();
This will alert EVERY option element in the select.