Javascript display div only once - javascript

So I have a calculator with an error message that displays, if they press the "calculate" button if the input is NaN. The error message keeps getting created everytime the user presses calculate. How do i make it so it only shows even after pressing "calculate" multiple times?
function displayErr() {
const formBox = document.querySelector("form");
const errorBox = document.createElement("div");
errorBox.className = "errorBox";
const errorText = document.createTextNode("Those are not numbers!");
errorBox.appendChild(errorText);
formBox.appendChild(errorBox);
}
if ((isNaN(billInput)) || (isNaN(peopleAmount)) || (billInput === "") || (peopleAmount === "")) {
displayErr();
}

The most straightforward way is to check if the element already exists.
function displayErr() {
// Get error element
const errorElement = document.getElementsByClassName('errorBox');
// If it already exists
if (errorElement && errorElement.length > 0) {
// Dont add another one
return;
}
// Add new errorBox
const formBox = document.querySelector("form");
const errorBox = document.createElement("div");
errorBox.className = "errorBox";
const errorText = document.createTextNode("Those are not numbers!");
errorBox.appendChild(errorText);
formBox.appendChild(errorBox);
}
Another option would to be using css classes to 'hide' the element;
Always render the element, but hide it with display: none
In the displayErr(), make the element visible with something like document.getElementsByClassName('errorBox')[0].style.display = block;

a better way of doing this is
to show and hide the element using CSS classes
create the element and hide it using
display: none;
and show it by adding a class to the element
display: block;
const element = document.getElementById("myDIV");
const button = document.getElementById("btn");
button.addEventListener("click", () => element.classList.toggle("show"));
#myDIV {
display: none;
}
.show {
display: block !important;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<button id="btn">Try it</button>
<div id="myDIV">
This is a DIV element.
</div>
</body>
</html>

For what it's worth, here is a pure JavaScript example of a show/hide interpretation:
function CheckInput() {
const billInput = document.getElementById("b").value;
const peopleAmount = document.getElementById("p").value;
if ((isNaN(billInput)) || (isNaN(peopleAmount)) || (billInput === "") || (peopleAmount === "")) {
showErr();
}
else{
hideErr();
}
}
function hideErr(){
console.log("hide");
const el = document.getElementById("error");
el.style.display = "none";
}
function showErr(){
console.log("show");
const el = document.getElementById("error");
el.style.display = "block";
el.innerHTML = "Hey sorry wrong input";
}
window.onload = function() {
hideErr();
}
You can see the HTML and try the code here: https://jsfiddle.net/0mrx5ev7/

You can pass a parameter to your displayErr function, then use it to set the hidden HTML attribute and textContent of a single target div, identified by its HTML id.
This way, the functionality becomes reusable, and you can set/unset the error message whenever you need.
const input = document.querySelector('#input')
const errDisplay = document.querySelector('#err-display')
function displayErr(msg) {
errDisplay.textContent = msg || ''
errDisplay.hidden = msg ? null : 'hidden'
}
input.addEventListener('input', () => {
displayErr(isNaN(input.value) ? "Not a number" : null)
})
#err-display {
font-family: sans-serif;
background: red;
color: white;
margin: .5em 0;
padding: .5em;
}
<input id='input' placeholder='Start typing'>
<div id='err-display' hidden></div>

try to use a counter. like if int i == 0 --> do the function. i would do so
int i = 0;
function displayErr() {
const formBox = document.querySelector("form");
const errorBox = document.createElement("div");
errorBox.className = "errorBox";
const errorText = document.createTextNode("Those are not numbers!");
errorBox.appendChild(errorText);
formBox.appendChild(errorBox);
}
if ((isNaN(billInput)) && i == 0 || (isNaN(peopleAmount)) && i == 0 ||
(billInput === "") && i == 0 || (peopleAmount === "") && i == 0)
{
displayErr();
i += 1;
}
now it will display an error only once, because i is never going to be 0 anymore

Related

Add a blinking effect to single character of array

I am trying to achieve blinking effect to a single character of an array.
For example, if "text" get load inside paragraph <p> tag or container then first character of text should blink and when user types the blinking character in input area blinking effect must move to blink on next character.
I need assistance in solving this problem. Any instructions or help will be so grateful.
Here what I've tried so far:
let displayElem = document.getElementById("me");
const inputElem = document.getElementById("input");
const text = "Hey It's bad day, not a bad life,you'll be okay...!"
text.split('').forEach(char => {
const chrspan = document.createElement('span')
chrspan.innerText = char;
displayElem.appendChild(chrspan);
});
inputElem.addEventListener('input', () => {
var vl = document.getElementById("input").value;
const arrayq = displayElem.querySelectorAll('span')
const arrayv = inputElem.value
let correct = true;
arrayq.forEach((chSpan, index) => {
const char = arrayv[index];
if (char == null) {
correct = false;
} else if (char === chSpan.innerText) {
chSpan.classList.add('blink-bg')
} else {
chSpan.classList.remove('blink-bg')
correct = false
}
})
})
.blink-bg {
text-align: center;
color: #fff;
display: inline-block;
border-radius: 3px;
animation: blinkingBackground 1s infinite;
}
#keyframes blinkingBackground {
from { background-color: #f1ebeb; }
to { background-color: #080808; }
}
<p id="me"></p>
<input id="input" type="input" />
let displayElem = document.getElementById("me");
const inputElem = document.getElementById("input");
const text = "Hey It's bad day, not a bad life,you'll be okay...!"
text.split('').forEach(char => {
const chrspan = document.createElement('span')
chrspan.innerText = char;
displayElem.appendChild(chrspan);
});
inputElem.addEventListener('input', () => {
var vl = document.getElementById("input").value;
const arrayq = displayElem.querySelectorAll('span')
const arrayv = inputElem.value
let correct = true;
arrayq.forEach((chSpan, index) => {
const char = arrayv[index];
if (char == null) {
correct = false;
} else if (char === chSpan.innerText) {
chSpan.classList.add('blink-bg')
// document.getElementById("p_id").innerHTML = chSpan.innerText;
} else {
chSpan.classList.remove('blink-bg')
correct = false
}
})
})
This might be a good starting point for you. I changed a couple things,
I made a helper function $ to grab items from the dom as a personal prefrence.
I created an array of all the spans on the document, making it easier to keep track of which element has the blinking class. I created a helper function to grab what the activeText is from the txtArr instead of checking the text content. This way I can avoid using the rendered screen as a storage area for information, and instead have the screen mirror what is happening in my js.
On input I check the last character entered, and if it is the character that is blinking, I increment increment which span is blinking.
This is meant to be a simple demo of how to accomplish this task, you may want to have different functionality, but hopefully this helps as a starting point!
const $ = str => [...document.querySelectorAll(str)];
let displayElem = $("#me")[0];
const inputElem = $("#input")[0];
const text = "Hey! It's a bad day, not a bad life, you'll be okay...!"
const txtArr = [...text];
const txtSpans = txtArr.map(char => {
const span = document.createElement("span");
span.innerText = char;
return span;
});
let activeIndex = -1;
const activeText = () => txtArr[activeIndex];
function renderSpans() {
displayElem.innerHtml = "";
txtSpans.forEach(span => displayElem.appendChild(span));
};
function updateActive() {
const firstRun = activeIndex == -1;
if (!firstRun)
txtSpans[activeIndex].classList.remove("blink-bg");
activeIndex++;
if (activeIndex == txtSpans.length) return;
txtSpans[activeIndex].classList.add("blink-bg");
}
updateActive();
renderSpans();
inputElem.addEventListener('input', e => {
const val = e.target.value;
if (val == "") return;
const lastChar = val[val.length - 1];
if (lastChar == activeText()) updateActive();
})
.blink-bg {
text-align: center;
color: #fff;
display: inline-block;
border-radius: 3px;
animation: blinkingBackground 1s infinite;
}
#keyframes blinkingBackground {
from { background-color: #f1ebeb; }
to { background-color: #080808; }
}
<p id="me"></p>
<input id="input" type="input" />

javascript add and remove className, keep setting

I want to make a button for night and day mode.I found this link but it doesn't work for me.
when i click the button, it applies css to change background and text color and button value which is night to day but if i click button again it doesn't work. it keeps execute "if" part not "else" part.
//nightmode
var mode = localStorage.getItem("mode");
if (mode != null) {
document.getElementById("body").classList.add(mode);
}
document.getElementById("nightButton").onclick = function() {
var nightButton = document.getElementById("nightButton")
var body = document.getElementById("body");
if (nightButton.value = "night") {
body.classList.add("nightMode");
nightButton.value = "day";
localStorage.setItem('mode', 'nightMode');
} else {
body.classList.remove("nightMode");
nightButton.value = "night";
localStorage.setItem("mode", null);
}
};
.nightMode {
background-color: black;
color: white;
}
<body id="body">
<input type="button" value="night" id="nightButton">
<div>abcd</div>
</body>
Just change the line
if (nightButton.value = "night") {
to
if (nightButton.value == "night") {
You can simplify the mode button handler. This snippet including handling of localStorage (can't be used in SO-snippets) can be found #JsFiddle
document.addEventListener("click", evt => {
if (evt.target.id === "nightButton") {
const body = document.body;
body.classList.toggle("nightMode");
evt.target.value = `set ${
body.classList.contains("nightMode") ? "day" : "night"}`;
document.querySelector("#currentMode").textContent = `Current mode: ${
body.classList.contains("nightMode") ? "NIGHT" : "DAY"}`;
}
});
.nightMode {
background-color: black;
color: white;
}
body {
margin: 2rem;
}
#currentMode {
margin-top: 1rem;
}
<div>abcd
<input type="button" value="set night" id="nightButton">
</div>
<div id="currentMode"></div>
there is a little error in your code.
This code: if (nightButton.value = "night") {
Can you change it: if (nightButton.value == "night") {
Example here: https://codepen.io/yasgo/pen/OJRLErL
thank you all
I don't know if this is good codes but i tried this and it works ...
var mode = localStorage.getItem("mode");
document.getElementById("body").classList.add(mode);
if (mode == "nightMode") {
document.getElementById("nightButton").value = "day"
} else {
document.getElementById("nightButton").value = "night"
}
document.getElementById("nightButton").onclick = function () {
var nightButton = document.getElementById("nightButton")
var body = document.getElementById("body");
if (nightButton.value == "night") {
body.classList.add("nightMode");
nightButton.value = "day";
localStorage.setItem('mode', 'nightMode');
} else {
body.classList.remove("nightMode");
nightButton.value = "night";
localStorage.setItem("mode", "daymode");
}
};

Javascript Can Push() and Pop() and Image Replacement Work within an Array

Can Push() and Pop() and Image Replacement Work within an Array?
8th Gr math teacher attempting to create a slide show of question images that pop() and push() through an image array based on student responses. If the student answers correctly the question is popped, but if they answer incorrectly it is added to the end of the queue. Additionally, since deleting elements in the DOM is bad, I am replacing the current image's src and id with that of the next element in queue. The array is then popped and pushed along, but whenever I enter in the incorrect answer twice the same image appears.
I have moved the global variable that holds the array, domEls, inside of the function retrieveAnsForImage to force it to randomize the images in the array. When I do this, the images change correctly so I believe it is the push() and pop() commands.
I included a snippet that doesn't work here, but works like a champ in Notepad ++. I just took a crash course in Javascript, HTML and CSS last month on Codecademy, I am very new to this. Thank you for reading.
//Jquery
$(document).ready(function() {
$(function() {
$('img.card').on('contextmenu', function(e) {
e.preventDefault();
//alert(this.id);
openPrompt(this.id);
});
});
});
//Provide and Shuffle array function
function shuffleImgs() {
var imgArr = [
"image1",
"image2",
"image3",
"image4",
"image5",
"image6",
"image7",
"image8",
"image9"
];
var currentIndex = imgArr.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = imgArr[currentIndex];
imgArr[currentIndex] = imgArr[randomIndex];
imgArr[randomIndex] = temporaryValue;
}
alert("shuffle");
return imgArr;
}
function arrStack() {
var imgArr = shuffleImgs();
//Map over the array to create Dom elements
var domElements = imgArr.map(function (imgName, index) {
var cardDiv = document.createElement('div');
var cardImage = document.createElement('img');
//Add img id and class
cardImage.id = imgName;
cardImage.classList.add('card');
//Set img source
cardImage.src = `images/${imgName}.jpg`;
//Put it all together
cardDiv.appendChild(cardImage);
return cardDiv;
});
//this notation to call nested function for Global var stack
this.nDomElements = function () {
stackDomEl = domElements;
return stackDomEl;
}
//Display last element in array
//this notation to call the nested function from outside the function
this.nDisplayLastArr = function displayLastArr() {
var lastImgArr = domElements[domElements.length - 1];
//alert(lastImgArr);
//Append the elements to the DOM
var modal = document.querySelector('div.modal');
modal.appendChild(lastImgArr);
return lastImgArr; //Use brackets when your are returning more than one variable
}
}
//Function called from Jquery to open prompt to answer question
function openPrompt(imageId) {
var userAns = prompt("Please enter your answer below and click OK");
if (userAns == null || userAns == "") {
alert("User cancelled the prompt. Exit and please try again!");
}
else {
/*Vain hope that I can pass imageId from click event through the user prompt
to the answer checking function retrieveAnsForImage*/
retrieveAnsForImage(imageId, userAns); //out of scope?
}
}
//Global variable
func = new arrStack();
window.domEls = func.nDomElements();
//Compare user responses with the question image by use of the click image id
function retrieveAnsForImage(imageId, userAns) {
//Change these variables to the correct answer whenever this website is reused in other assignments
var ansImage1 = "1";
var ansImage2 = "2";
var ansImage3 = "3";
var ansImage4 = "4";
var ansImage5 = "5";
var ansImage6 = "6";
var ansImage7 = "7";
var ansImage8 = "8";
var ansImage9 = "9";
//Give students a second chance to retry a question
//var hintCounter = 0; //include a while statement above the if statements to allow students a retry
/*Compare user response with correct case answer and correct clicked image.
Students may enter the right answer for the wrong image hence the &&.
Images will always be refered to as image1, image2, etc.*/
if (userAns === ansImage1 && imageId === "image1") {
correctAns(imageId);
}
else if (userAns === ansImage2 && imageId === "image2") {
correctAns(imageId);
}
else if (userAns === ansImage3 && imageId === "image3") {
correctAns(imageId);
}
else if (userAns === ansImage4 && imageId === "image4") {
correctAns(imageId);
}
else if (userAns === ansImage5 && imageId === "image5") {
correctAns(imageId);
}
else if (userAns === ansImage6 && imageId === "image6") {
correctAns(imageId);
}
else if (userAns === ansImage7 && imageId === "image7") {
correctAns(imageId);
}
else if (userAns === ansImage8 && imageId === "image8") {
correctAns(imageId);
}
else if (userAns === ansImage9 && imageId === "image9") {
correctAns(imageId);
}
else {
window.alert("Incorrect Answer");
incorrectAns();
}
function correctAns(){
//Second to last element in array
var SecLastElArr = domEls[domEls.length - 2];
//Pull image id from second to last element in array
var nextImgId = SecLastElArr.querySelector("div > img").id;
//Pull image id from document
var imgId = document.querySelector("div > img").id;
//Student incorrect answer change im
document.getElementById(imgId).src = `images/${nextImgId}.jpg`;
document.getElementById(imgId).id = nextImgId;
domEls.pop();
//Think about when the array is completely gone
//while domEls.length !== 0;
}
function incorrectAns(){
//Last element in array
var LastElArr = domEls[domEls.length - 1];
//Second to last element in array
var SecLastElArr = domEls[domEls.length - 2];
//Pull image id from second to last element in array
var nextImgId = SecLastElArr.querySelector("div > img").id;
//Pull image id from document
var imgId = document.querySelector("div > img").id;
//Student incorrect answer change image src and id to next element in queue
document.getElementById(imgId).src = `images/${nextImgId}.jpg`;
document.getElementById(imgId).id = nextImgId;
//Remove last element in array
domEls.pop();
//move the last element to the first element in the array for another attempt
domEls.push(LastElArr);
alert(domEls.length);
}
}
function overlay() {
var el = document.getElementById("overlay");
el.style.visibility = (el.style.visibility == "visible") ? "hidden" : "visible";
}
#overlay {
visibility: hidden;
position: absolute;
left: 0px;
top: 0px;
width:100%;
height:100%;
text-align:center;
z-index: 1000;
background-color: rgba(0,191, 255, 0.8);
}
#overlay div {
width:70%;
margin: 10% auto;
background-color: #fff;
border:1px solid #000;
padding:15px;
text-align: center;
}
body {
height:100%;
margin:0;
padding:0;
}
#close-img {
float: right;
clear: right;
width: 30px;
height: 30px;
bottom: 0;
right: 0;
}
<!DOCTYPE html>
<html>
<head>
<title></title>
<span> "Left click to view any questions. Right click (two finger tap) to answer the question and claim the tile. Each player must claim 4 tiles to successfully complete the assignment."</span>
<link href="https://fonts.googleapis.com/css?family=Oswald:300,700|Varela+Round" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="Stack Rnd Temp.css">-->
<script type="text/javascript" src="Stack Rnd Temp.js"></script>
<script src="jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="StackRndTempjq.js"></script>
</head>
<body>
<div class="title">
<h1></h1>
</div>
<div id="gameboard"> <!--Container for all nine divs-->
<a href='#' onclick='overlay()'>Click here to show the overlay</a>
</div>
<div class="modal" id="overlay">
<p> "Right click to answer the question"</p>
<script>
func = new arrStack();
func.nDisplayLastArr();
</script>
<img src="images/close.png" id="close-img" onclick="overlay()">
</div>
</body>
</html>
Your issue is that pop removes the last element from the array while push adds the element to end of the array.
What you probably want to do is use shift to remove the the first element from the array and pop it back to the end if the answer is wrong.
Alternately, you could pop the last element and use unshift to insert back into the beginning of you want to work in the other direction.
Here's a quick mockup without images.
var currentTest = null;
function getTest() {
$('#answer').html("").hide();
if (tests.length > 0) {
currentTest = tests.shift(); // remove the first question
$('#question').fadeIn(450).html(currentTest.q);
return currentTest;
} else {
$('#answer').html("Finished").fadeIn(500);
$('#btnCorrect').unbind();
$('#btnWrong').unbind();
}
}
var tests = [];
for (var i = 0; i < 5; i++) {
var question = "Question " + i;
var answer = "Answer " + i;
tests.push({
q: question,
a: answer
});
}
$('#btnCorrect').click(function() {
$('#question').hide();
$('#answer').fadeIn(450).html("Correct!");
window.setTimeout(getTest, 750);
});
$('#btnWrong').click(function() {
$('#question').hide();
tests.push(currentTest); // put the question back in the array
$('#answer').fadeIn(450).html("Incorrect!");
window.setTimeout(getTest, 750);
});
$(document).ready(function() {
getTest();
})
* {
font-family: arial;
}
#panel {
height: 50px;
}
#answer {
border: 1px solid #cccccc;
background: #dedede;
width: 400px;
}
#question {
border: 1px solid #999999;
background: #dedede;
width: 400px;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div id="panel">
<div id="answer"></div>
<div id="question"></div>
</div>
<input id="btnCorrect" value="Mock Correct Answer" type="button">
<input id="btnWrong" value="Mock Wrong Answer" type="button">
</body>
</html>

pure javascript to check if something has hover (without setting on mouseover/out)

I have seen this jQuery syntax:
if($(element).is(':hover')) { do something}
Since I am not using jQuery, I am looking for the best way to do this in pure javascript.
I know I could keep a global variable and set/unset it using mouseover and mouseout, but I'm wondering if there is some way to inspect the element's native properties via the DOM instead? Maybe something like this:
if(element.style.className.hovered === true) {do something}
Also, it must be cross browser compatible.
Simply using element.matches(':hover') seems to work well for me, you can use a comprehensive polyfill for older browsers too: https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
You can use querySelector for IE>=8:
const isHover = e => e.parentElement.querySelector(':hover') === e;
const myDiv = document.getElementById('mydiv');
document.addEventListener('mousemove', function checkHover() {
const hovered = isHover(myDiv);
if (hovered !== checkHover.hovered) {
console.log(hovered ? 'hovered' : 'not hovered');
checkHover.hovered = hovered;
}
});
.whyToCheckMe {position: absolute;left: 100px;top: 50px;}
<div id="mydiv">HoverMe
<div class="whyToCheckMe">Do I need to be checked too?</div>
</div>
to fallback I think it is ok #Kolink answer.
First you need to keep track of which elements are being hovered on. Here's one way of doing it:
(function() {
var matchfunc = null, prefixes = ["","ms","moz","webkit","o"], i, m;
for(i=0; i<prefixes.length; i++) {
m = prefixes[i]+(prefixes[i] ? "Matches" : "matches");
if( document.documentElement[m]) {matchfunc = m; break;}
m += "Selector";
if( document.documentElement[m]) {matchfunc = m; break;}
}
if( matchfunc) window.isHover = function(elem) {return elem[matchfunc](":hover");};
else {
window.onmouseover = function(e) {
e = e || window.event;
var t = e.srcElement || e.target;
while(t) {
t.hovering = true;
t = t.parentNode;
}
};
window.onmouseout = function(e) {
e = e || window.event;
var t = e.srcElement || e.target;
while(t) {
t.hovering = false;
t = t.parentNode;
}
};
window.isHover = function(elem) {return elem.hovering;};
}
})();
it occurred to me that one way to check if an element is being hovered over is to set an unused property in css :hover and then check if that property exists in javascript. its not a proper solution to the problem since it is not making use of a dom-native hover property, but it is the closest and most minimal solution i can think of.
<html>
<head>
<style type="text/css">
#hover_el
{
border: 0px solid blue;
height: 100px;
width: 100px;
background-color: blue;
}
#hover_el:hover
{
border: 0px dashed blue;
}
</style>
<script type='text/javascript'>
window.onload = function() {check_for_hover()};
function check_for_hover() {
var hover_element = document.getElementById('hover_el');
var hover_status = (getStyle(hover_element, 'border-style') === 'dashed') ? true : false;
document.getElementById('display').innerHTML = 'you are' + (hover_status ? '' : ' not') + ' hovering';
setTimeout(check_for_hover, 1000);
};
function getStyle(oElm, strCssRule) {
var strValue = "";
if(document.defaultView && document.defaultView.getComputedStyle) {
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
}
else if(oElm.currentStyle) {
strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1) {
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
};
</script>
</head>
<body>
<div id='hover_el'>hover here</div>
<div id='display'></div>
</body>
</html>
(function getStyle thanks to JavaScript get Styles)
if anyone can think of a better css property to use as a flag than solid/dashed please let me know. preferably the property would be one which is rarely used and cannot be inherited.
EDIT: CSS variable are probably better to use to check this. E.g.
const fps = 60;
setInterval(function() {
if(getComputedStyle(document.getElementById('my-div')).getPropertyValue('--hovered') == 1) {
document.getElementById('result').innerHTML = 'Yes';
} else {
document.getElementById('result').innerHTML = 'No';
};
}, 1000 / fps);
#my-div {
--hovered:0;
color: black;
}
#my-div:hover {
--hovered:1;
color: red;
}
<!DOCTYPE html>
<html>
<head>
<title>Detect if div is hovered with JS, using CSS variables</title>
</head>
<body>
<div id="my-div">Am I hovered?</div>
<div id="result"></div>
</body>
</html>
You can use an if statement with a querySelector. If you add ":hover" to the end of the selector, it will only return the element if it is being hovered. This means you can test if it returns null. It is like the element.matches(":hover) solution above, but I have had more success with this version.
Here is an example:
if (document.querySelector("body > p:hover") != null) {
console.log("hovered");
}
You can put it in an interval to run the code every time you hover:
setInterval(() => {
if (document.querySelector("body > p:hover") != null) {
console.log("hovered");
}
}, 10);

Javascript run a function at the same time with different vars

Sorry about the confusing title, I'll explain better.
I have a 20x20 grid of div's, so its 400 of them each with an id, going from 0 to 399.
Each div is given one of three random values - red, green or blue - and when a div is clicked, a function is run to check if the div to the left, right, over and under are of the same value, if it is of the same value it will be simulated a click and the same function will run again.
The problem, is that the function sets vars, so if it finds that the div below has the same value, it will overwrite the vars set by the first click, hence never click any of the others.
JSfiddle - http://jsfiddle.net/5e52s/
Here is what I've got:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>untiteled</title>
<style>
body {
width: 420px;
}
.box {
width: 19px;
height: 19px;
border: 1px solid #fafafa;
float: left;
}
.box:hover {
border: 1px solid #333;
}
.clicked {
background: #bada55 !important;
}
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$().ready(function(){
var colors = ['red', 'green', 'blue'];
var i = 0;
while(i<400){
var color = colors[Math.floor(Math.random() * colors.length)];
$('.test').append('<div class="box" id="'+i+'" value="'+color+'" style="background:'+color+';">'+i+'</div>');
i++;
}
$('.box').click(function(){
var t = $(this);
t.addClass('clicked');
id = t.attr('id');
val = t.attr('value');
//Set color
up = parseInt(id) - 20;
right = parseInt(id) + 1;
down = parseInt(id) + 20;
left = parseInt(id) - 1;
clickup = false;
clickdown = false;
if($('#'+down).attr('value') === val){
clickdown = true;
}
if(up > -1 && ($('#'+up).attr('value') === val)){
clickup = true;
}
if(clickdown == true){
$('#'+down).click();
}
if(clickup == true){
$('#'+up).click();
}
});
});
</script>
</head>
<body>
<div class="test">
</div>
</body>
I think the biggest root cause of your problem is you don't check if it already has class 'clicked' or not. That could make the infinite recursive. For example, if you click on the div#2 then the div#1 receives a simulated click, and div#2 receives a simulated click from div#1.
$('.box').click(function(){
var t = $(this);
if(t.hasClass('clicked')) {
return;
}
t.addClass('clicked');
var id = t.attr('id');
var val = t.attr('value');
//Set color
var up = parseInt(id) - 20;
var right = (id%20 != 19) ? ((0|id) + 1) : 'nothing' ;
var down = parseInt(id) + 20;
var left = (id%20 != 0) ? ((0|id) - 1) : 'nothing';
console.log(up, right, down, left);
if($('#'+down).attr('value') === val) {
$('#'+down).click();
}
if($('#'+right).attr('value') === val) {
$('#'+right).click();
}
if($('#'+up).attr('value') === val) {
$('#'+up).click();
}
if($('#'+left).attr('value') === val) {
$('#'+left).click();
}
});
You can schedule the clicks onto the event loop instead of calling them directly, eg:
if(clickdown == true){
setTimeout(function () {
$('#'+down).click();
});
}
I don't think that's your root cause though, it's probably a combination of global vars and scope issues. Try reformatting as such:
$('.box').click(function (event){
var t = $(this), id, val, up, right, down, left, clickup, clickdown;
//...
Your variables id and val are not in a var statement, thus are implicitly created as members of the window object instead of being scoped to the local function. Change the semicolon on the line before each to a comma so that they become part of the var statement, and your code should begin working.

Categories