Getting an infinite loop and can't see why - Javascript - javascript

I'm writing a simple little Connect 4 game and I'm running into an infinite loop on one of my functions:
var reds = 0;
var greens = 0;
function checkEmpty(div) {
var empty = false;
var clicked = $(div).attr('id');
console.log(clicked);
var idnum = parseInt(clicked.substr(6));
while (idnum < 43) {
idnum = idnum + 7;
}
console.log("idnum=" + idnum);
while (empty == false) {
for (var i = idnum; i > 0; i - 7) {
idnumStr = idnum.toString();
var checking = $('#square' + idnumStr);
var str = checking.attr('class');
empty = str.includes('empty');
console.log(empty);
var divToFill = checking;
}
}
return divToFill;
}
function addDisc(div) {
if (reds > greens) {
$(div).addClass('green');
greens++;
console.log("greens=" + greens);
} else {
$(div).addClass('red');
reds++;
console.log("reds=" + reds);
};
$(div).removeClass('empty');
}
$(function() {
var i = 1;
//add a numbered id to every game square
$('.game-square').each(function() {
$(this).attr('id', 'square' + i);
i++;
//add an on click event handler to every game square
//onclick functions
$(this).on('click', function() {
var divToFill = checkEmpty(this);
addDisc(divToFill);
})
})
})
Here is a link to the codepen http://codepen.io/Gobias___/pen/xOwNOd
If you click on one of the circles and watch the browser's console, you'll see that it returns true over 3000 times. I can't figure out what I've done that makes it do that. I want the code to stop as soon as it returns empty = true. empty starts out false because I only want the code to run on divs that do not already have class .green or .red.
Where am I going wrong here?

for (var i = idnum; i > 0; i - 7);
You do not change the i.
Do you want to decrement it by 7?
Change your for loop to the one shown below:
for (var i = idnum; i > 0; i -= 7) {
// ...
}
You also do not use loop variable in the loop body. Instead, you use idnum, I think this can be issue.
while (empty == false) {
for (var i = idnum; i > 0; i -= 7) {
idnumStr = i.toString(); // changed to i
var checking = $('#square' + idnumStr);
var str = checking.attr('class');
empty = str.includes('empty');
console.log(empty);
var divToFill = checking;
// and don't forget to stop, when found empty
if (empty) break;
}
}
I add break if empty found, because if we go to next iteration we will override empty variable with smallest i related value.
You can also wrap empty assignment with if (!empty) {empty = ...;} to prevent this override, but I assume you can just break, because:
I want the code to stop as soon as it returns empty = true
Offtop hint:
while (idnum < 43) {
idnum = idnum + 7;
}
can be easy replaced with: idnum = 42 + (idnum%7 || 7)

Change to this:
for (var i = idnum; i > 0; i = i - 7) {
You are not decrementing the i in your for loop

Building on what the others have posted You would want to change the value of empty inside the for loop. because obviously the string still checks the last string in the loop which would always return false.
while(empty==false){
for (var i = idnum; i > 0; i -= 7) {
// your other codes
if (!empty) {
empty = str.includes('empty');
}
}

Related

Is there possible to add a moreLink to display all events in a popup regardless the space available in the dayCell?

I am trying to add a moreLink in full calendar on each day i have events and in the more link i want to force displaying all the events on that day!
this is the solutin i choose because the title of the events are very long and do not fit in a tablet or phone screen!
so far i am unsuccesfull on the days i have one single event because the function computeRowLevelLimit returns false!
i am open to any crazy idea that helps me but keep in mind that i am a java dev with minimal kowledge of js and add some extra info if possible
since i was under presure i took the matter in my own hands so here is the resolve
here i added the last else in order to be able to execute 2 methods when levelLimit is false
limitRows: function (levelLimit) {
var rowStructs = this.eventRenderer.rowStructs || [];
var row; // row #
var rowLevelLimit;
for (row = 0; row < rowStructs.length; row++) {
this.unlimitRow(row);
if (!levelLimit) {
rowLevelLimit = false;
}
else if (typeof levelLimit === 'number') {
rowLevelLimit = levelLimit;
}
else {
rowLevelLimit = this.computeRowLevelLimit(row);
}
if (rowLevelLimit !== false) {
this.limitRow(row, rowLevelLimit);
} else {
this.unlimitRow2(row);
this.addMoreLink(row);
}
}
},
The added metod are:
- one for clearing the existing links
- second for adding new links - remember this is for days with only one event
the methods are the following:
unlimitRow2: function (row) {
var rowStruct = this.eventRenderer.rowStructs[row];
var cellMatrix;
var oneDayCell;
cellMatrix = rowStruct.cellMatrix;
var _this = this;
for (i = 0; i < cellMatrix.length; i++) {
// console.log("celmatrix ", cellMatrix[i]);
oneDayCell = cellMatrix[i];
console.log("outati", oneDayCell)
if (oneDayCell.moreEls) {
oneDayCell.moreEls.remove();
oneDayCell.moreEls = null;
}
if (oneDayCell.limitedEls) {
oneDayCell.limitedEls.removeClass('fc-limited');
oneDayCell.limitedEls = null;
}
}
},
and,
addMoreLink: function (row) {
// console.log("inside addMoreMethod", row);
var rowStruct = this.eventRenderer.rowStructs[row];
var cellMatrix;
var oneDayCell;
var coloana;
var nrCol;
var td, moreWrap, moreLink;
var moreNodes = [];
var segsBelow;
// console.log ("structura randului", rowStruct);
cellMatrix = rowStruct.cellMatrix;
var _this = this;
for (i = 0; i < cellMatrix.length; i++) {
// console.log("celmatrix ", cellMatrix[i]);
oneDayCell = cellMatrix[i];
for (j = 0; j < oneDayCell.length; j++) {
coloana = oneDayCell[j];
nrCol = j;
segsBelow = _this.getCellSegs(row, nrCol);
console.log($(coloana));
moreLink = _this.renderMoreLink(row, nrCol, segsBelow);
moreWrap = $('<div/>').append(moreLink);
coloana.append(moreWrap);
moreNodes.push(moreWrap[0]);
// rowStruct.limitedEls = $(limitedNodes);
}
rowStruct.moreEls = $(moreNodes); // for easy undoing later
}
},
and for the rest i manipulated a litle bit limitRow: function (row, levelLimit)
also i had to hide the text and i choose a nasty method, not proud of it but ...
in getMoreLinkText(num) i added a last else if
else if (num === 0 ){
return '';
}

How do I step though an array one cell at a time using onclick?

I am new to JavaScript and am trying to create a function that changes the background color of my HTML document each time I click on a DIV. The code I have just loops though my array all the way to the end instead of one loop per click on my DIV. I know I am missing something that increments each time I click on the DIV, but can't figure out what. In my HTML document I have a DIV
<div id="autoText" onclick="moveMe()"></div>.
Here is my JavaScript so far:
var multipleColors = [];
multipleColors[0] = "red";
multipleColors[1] = "blue";
multipleColors[2] = "yellow";
function moveMe() {
for (i=0; i < multipleColors.length; i++) {
console.log("This is step " + i);
this.document.body.style.backgroundColor=multipleColors[i];
}
}
I would really appreciate anyone's advice?
Thanks.
If you want to continue from the beginning of the after you reached its end, use %(modulo) operator:
var multipleColors = [];
multipleColors[0] = "red";
multipleColors[1] = "blue";
multipleColors[2] = "yellow";
var current = 0;
function moveMe() {
current++;
var newIdx = current % multipleColors.length;
this.document.body.style.backgroundColor = multipleColors[newIdx];
}
Instead of using a loop, use a counter variable that you increase whenever the element is clicked:
var index = 0;
function moveMe() {
console.log("This is step " + index);
this.document.body.style.backgroundColor=multipleColors[index];
index += 1;
}
If you want to continue from the beginning of the array after you reached its end, reset the variable:
var index = 0;
function moveMe() {
console.log("This is step " + index);
this.document.body.style.backgroundColor=multipleColors[index];
index += 1;
if (index === multipleColors.length) {
index = 0;
}
}
try something like this
var multipleColors = [];
multipleColors[0] = "red";
multipleColors[1] = "blue";
multipleColors[2] = "yellow";
function moveMe() {
this.document.body.style.backgroundColor=(multipleColors[multipleColors.indexOf(this.document.body.style.backgroundColor)+1])?multipleColors[multipleColors.indexOf(this.document.body.style.backgroundColor)+1]:multipleColors[0];
}
You can keep adding the more colors in array.
DEMO

Change 2 image each other when click on them

i have a div with multiple images inside and i need to click on a random image then again click on a random picture and when i clicked the second image to change images with each other. All images are interchangeable.Heres what i've done so far:
EDIT FIDDLE: http://jsfiddle.net/w53Ls/5/
$("#image1").click(function(){
imagePath = $("#image2").attr("src");
if(imagePath == "http://s15.postimg.org/oznwrj0az/image.jpg"){
$("#image3").attr("src", "http://s21.postimg.org/ojn1m2eev/image.jpg");
}else{
$("#image4").attr("src", "http://s23.postimg.org/epckxn8uz/image.jpg");
}
});
EDIT: The code i have tryed for check function is in EDIT FIDDLE and with the alert i check src of pictures.Now i simply need to make a condition to alert something after i change all the pieces in order and found the whole picture.Any hint?
DEMO
var clickCount = 0;
var imgSrc;
var lastImgId;
$("img.element").click(function(){
if (clickCount == 0)
{
imgSrc = $(this).attr("src");
lastImgId = $(this).attr("id");
clickCount++;
}
else {
$("#"+lastImgId).attr("src",$(this).attr("src"));
$(this).attr("src",imgSrc)
clickCount = 0;
}
});
Updated
This let's you know when you're done with the puzzle
DEMO
var clickCount = 0;
var imgSrc;
var lastImgId;
// Function for Comparing Arrays
// source: http://stackoverflow.com/questions/7837456/
Array.prototype.compare = function (array) {
if (!array) return false;
if (this.length != array.length) return false;
for (var i = 0, l = this.length; i < l; i++) {
if (this[i] instanceof Array && array[i] instanceof Array) {
if (!this[i].compare(array[i])) return false;
} else if (this[i] != array[i]) {
return false;
}
}
return true;
}
$(document).ready(function () {
// Store the correct order first in an array.
var correctOrder = $("#puzzle > img").map(function () {
return $(this).attr("src");
}).get();
// Randomize your images
var a = $("#puzzle > img").remove().toArray();
for (var i = a.length - 1; i >= 1; i--) {
var j = Math.floor(Math.random() * (i + 1));
var bi = a[i];
var bj = a[j];
a[i] = bj;
a[j] = bi;
}
$("#puzzle").append(a);
$("img.element").click(function () {
if (clickCount == 0) {
imgSrc = $(this).attr("src");
lastImgId = $(this).attr("id");
clickCount++;
} else {
$("#" + lastImgId).attr("src", $(this).attr("src"));
$(this).attr("src", imgSrc);
clickCount = 0;
// Get the current order of the images
var currentOrder = $("#puzzle > img").map(function () {
return $(this).attr("src");
}).get();
// Compare the current order with the correct order
if (currentOrder.compare(correctOrder)) alert("Puzzle completed");
}
});
});
http://jsfiddle.net/w53Ls/2/
var counter = 0;
The code was improvised but works XD
you try improve it
Here is a new version of your jsfiddle that I think will do what you want.
It applies the same click handler to every object with the class swapable. Each time a swapable element is clicked, the handler checks whether another element was previously clicked first. If so, it swaps them. If not, it just remembers that this element is the first one.
var firstId = ''; // Initially, no element has been clicked first
var firstSrc = '';
// Apply the following function to every element with 'class="swapable"
$('.swapable').click(function(){
if (firstId !== '') { // There is already a first element clicked
// Remember the information of the currently clicked element
var secondId = $(this).attr('id');
var secondSrc = $(this).attr('src');
// Replace the currently clicked element with the first one
$('#' + secondId).attr('src', firstSrc);
// Replace the first one with the current one
$('#' + firstId).attr('src', secondSrc);
// Forget the first one, so that the next click will produce a new first
firstId = '';
firstSrc = '';
}
else // This is the first element clicked (this sequence)
{
// Remember this information for when a second is clicked
firstId = $(this).attr('id');
firstSrc = $(this).attr('src');
}
});

What is the plain Javascript equivalent of .each and $(this) when used together like in this example?

What is the plain Javascript equivalent of .each and $(this).find when used together in this example?
$(document).ready(function(){
$('.rows').each(function(){
var textfield = $(this).find(".textfield");
var colorbox = $(this).find(".box");
function colorchange() {
if (textfield.val() <100 || textfield.val() == null) {
colorbox.css("background-color","red");
colorbox.html("Too Low");
}
else if (textfield.val() >300) {
colorbox.css("background-color","red");
colorbox.html("Too High");
}
else {
colorbox.css("background-color","green");
colorbox.html("Just Right");
}
}
textfield.keyup(colorchange);
}
)});
Here's a fiddle with basically what I'm trying to accomplish, I know I need to use a loop I'm just not sure exactly how to set it up. I don't want to use jquery just for this simple functionality if I don't have to
http://jsfiddle.net/8u5dj/
I deleted the code I already tried because it changed every instance of the colorbox so I'm not sure what I did wrong.
This is how to do what you want in plain javascript:
http://jsfiddle.net/johnboker/6A5WS/4/
var rows = document.getElementsByClassName('rows');
for(var i = 0; i < rows.length; i++)
{
var textfield = rows[i].getElementsByClassName('textfield')[0];
var colorbox = rows[i].getElementsByClassName('box')[0];
var colorchange = function(tf, cb)
{
return function()
{
if (tf.value < 100 || tf.value == null)
{
cb.style.backgroundColor = 'red';
cb.innerText = "Too Low";
}
else if (tf.value > 300)
{
cb.style.backgroundColor = 'red';
cb.innerText = "Too High";
}
else
{
cb.style.backgroundColor = 'green';
cb.innerText = "Just Right";
}
};
}(textfield, colorbox);
textfield.onkeyup = colorchange;
}
var rows = document.querySelectorAll('.rows');
for (var i=0; i<rows.length; i++) {
var row = rows[i];
var textfield = row.querySelector('.textfield');
var colorbox = row.querySelector('.box');
// ...
}
Note that you must use a for loop to iterate the rows because querySelectorAll() does not return an array, despite appearances. In particular, that means that .forEach() isn't valid on the returned list.

Create dynamic elements in javascript and visualize it on UI as soon as the node is created

I am working on a script that creates a tree. The problem i am facing is when large chunk of data comes it gets stuck for some time then rendering every thing at the end.
What i am looking for is can there be a way that make it more interactive. Like as soon as a node is being made it gets popup at the Interface.
For getting into the inside i am posting my code.
function recursiveGenerateTree(objNode, parntSpan, ulContainer, objEditParam) {
var cntLi = 0;
var spnApplyClass;
var rdbValue;
var cntrList = 0;
for (cntLi = 0; cntLi <= objNode.NodeList.length - 1; cntLi++) {
objEditParam.rdbGroup = objEditParam.rdbGroup;
rdbValue = objEditParam.orgRootID + '_' + objNode.NodeList[cntLi].Id;
objEditParam.rdbValue = rdbValue;
objEditParam.selector = 'radio';
objEditParam.selector = '';
objEditParam.isNewNode = false;
addChild('', parntSpan, ulContainer, objEditParam);
$('#txtParent').val(objNode.NodeList[cntLi].Name);
spnApplyClass = $('#txtParent').parents('span:first');
$('#txtParent').trigger('blur', [spnApplyClass]);
spnApplyClass.removeClass('bgLime');
var li = spnApplyClass.parents("li:first");
li.attr("nodeId", objNode.NodeList[cntLi].Id);
li.attr("rootnodeId", objNode.NodeList[cntLi].RootOrgUnitId);
var ulPrsnt = objNode.NodeList[cntLi].NodeList;
if (ulPrsnt != undefined && ulPrsnt.length > 0) {
recursiveGenerateTree(objNode.NodeList[cntLi], spnApplyClass, '', objEditParam);
}
}
}
Second function used is Add child
function addChild(currentbtn, parentSpn, parentUl, objEditParam) {
var spnElement;
if ($(currentbtn).length > 0) {
var dvClick = $(currentbtn).closest('div').siblings('div.OrgGroupLists')
spnElement = $(dvClick).find('span.bgLime');
}
else {
spnElement = parentSpn;
}
if (spnElement.length == 0 && parentUl.length == 0)
return;
var crtUlChild;
if (spnElement.length > 0) {
var dvCurrent = $(spnElement).closest("div");
crtUlChild = $(dvCurrent).find('ul:first');
}
if (parentUl.length > 0) {
crtUlChild = parentUl;
}
if (crtUlChild.length == 0) {
var ulChildrens = createUl();
}
//Next line needs to be updated.
var spnImage = $(dvCurrent).find("span:first");
$(spnImage).removeClass("SpanSpace");
$(spnImage).addClass("L7CollapseTree");
var liChildrens = document.createElement("li");
$(liChildrens).attr("isNew", objEditParam.isNewNode);
$(liChildrens).attr("isTextEdited", false);
var dvChildrens = createDivNode(objEditParam);
$(liChildrens).append(dvChildrens);
if (crtUlChild.length == 0) {
$(ulChildrens).append(liChildrens);
$(dvCurrent).append(ulChildrens);
}
else {
crtUlChild.append(liChildrens);
}
}
Feel free to ask any more details if required to understand the problem more clearly.
What #nnnnnn means is that you call subsequent recursion in setTimeout.
I did not spend time trying to replicate your code locally. Check out JavaScript code below for example. setTimeout works fine for the code below. You can vary variable j's length according to your browser. The inner loop j is to block JS thread.
<html><head>
<script>
var k = 0;
function recursiveTree(){
$("#holder").append("<div>Item</div>");
for(var j =0; j < 100000000; j++){
// blocking thread
}
if (k++ < 20) {
console.log(k);
setTimeout(function(){recursiveTree()},10);
}
}</script>
</head>
<body>
<div id='holder'></div></body>
</html>
I assumed that you are using JQuery.
Just to confirm you need to make recursive call as following
if (ulPrsnt != undefined && ulPrsnt.length > 0) {
setTimeout(function(){recursiveGenerateTree(objNode.NodeList[cntLi], spnApplyClass, '', objEditParam);},10);
}

Categories

Resources