Building calculator - Not getting it to work - javascript

I'm building a calculator, and while it's starting to work, I'm kind of stuck. I've pasted the HTML and the JS down here.
I've been trying to figure out what went wrong, and I think it's because of the functions calling each other and not properly... "closing/ending". I've been trying to see what went wrong with adding console.logs. I'll break it down here:
I press "on" and function listenFirst gets run.
listenFirst has an eventListener and waits for the user to click a number (firstNumber). As soon as a number is clicked, waitforOperator is run (which now has the firstNumberas an attribute. Now,
waitforOperator waits for a click on the plus sign, and as soon as the user clicks on the plus sign, listenSecondgets run. Now this is where it
goes wrong: as soon as I click the second number, waitforOperatorgets run again and now firstNumber and secondNumber are the same.
To be clear: I just want this problem solved before I delve into the other operators, so please don't pay attention to the other operators like minus and multiplication.
A nudge in the right direction would be nice! Thanks in advance.
let displayBox = document.getElementById("displayBox");
let pressButton = document.querySelectorAll(".column");
let turnOn = document.getElementById("turnOn");
let minus = document.getElementById("minus");
let plus = document.getElementById("plus");
let firstNumber = Number();
let secondNumber = Number();
turnOn.addEventListener("click", function() {
displayBox.textContent = "CALCULATOR ON. GIVE FIRST NR.";
console.log("launching function listenFirst");
listenFirst();
});
let listenFirst = () => {
console.log("launched listenFirst");
for (var i = 0; i < pressButton.length; i++) {
pressButton[i].addEventListener("click", function() {
firstNumber = displayBox.textContent = Number(this.id);
waitForOperator(firstNumber);
});
}
};
let listenSecond = () => {
console.log("launched listenSecond");
console.log(`first number is still ${firstNumber}`)
console.log(`waiting for you to press second number`)
for (var i = 0; i < pressButton.length; i++) {
pressButton[i].addEventListener("click", function() {
secondNumber = displayBox.textContent = Number(this.id);
console.log(`After click on second number, first number is ${firstNumber} and second number is ${secondNumber}`)
});
}
console.log(
`Now first number is ${firstNumber} and second number is ${secondNumber}`
);
};
let waitForOperator = () => {
console.log("launched waitForOperator");
console.log(`First number is ${firstNumber}`);
console.log("Waiting for you to press plus");
plus.addEventListener("click", function() {
listenSecond(firstNumber);
});
};
let calculateSum = () => {
console.log(`Second number is ${secondNumber}`);
};
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hello Bulma!</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css"
/>
<script
defer
src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"
></script>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<section class="section">
<h1 class="title has-text-centered" id="titleText">Calculator.</h1>
<div class="container">
<div class="field">
<a class="button is-fullwidth is-static" id="displayBox"></a><a class="button is-success" id="turnOn">ON</a>
</div>
<div class="calculator">
<div class="columns">
<div class="column" id="7">7</div>
<div class="column" id="8">8</div>
<div class="column" id="9">9</div>
<div class="column" id="minus">-</div>
</div>
<div class="columns">
<div class="column" id="4">4</div>
<div class="column" id="5">5</div>
<div class="column" id="6">6</div>
<div id="plus">+</div>
</div>
<div class="columns">
<div class="column" id="1">1</div>
<div class="column" id="2">2</div>
<div class="column" id="3">3</div>
<div class="column" id="equals">=</div>
</div>
<div class="columns">
<div class="column" id="0">0</div>
<div class="column" id="dot">.</div>
</div>
</div>
</div>
</section>
<script src="script.js"></script>
</body>
</html>

You never remove the event listener you added to number number buttons in listenFirst. So when the user presses the buttons after waitForOperator both the code in listenFirst and listenSecond runs. listenFirst call waitForOperator again.
JavaScript: remove event listener Have a look at this answer for how to remove event listeners.

Related

How to change the id of an element in html using javascript?

It is my first time using JavaScript. I am trying to make a button where every time visitors click, it'll show another extra line of text. I often get an error on my JavaScript, and I'm not sure how to fix it. Thank you so much!
HTML;
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div class="container">
<div class="text">
<div class="one hide">
One
</div>
<div class="two hide">
Two
</div>
<div class="three hide">
Three
</div>
</div>
</div>
</body>
<script src="script.js"></script>
</html>
JS;
const text = document.querySelector('.text');
const hide = document.querySelector('.hide');
const one = document.querySelector('.one');
const two = document.querySelector('.two');
var hr1 = document.getElementById('hr1');
var hr2 = document.getELementById('hr2');
var hr3 = document.getElementById('hr3');
hr1.addEventListener('click', () => {
one.classList.remove('hide');
hr1.id = "hr2";
})
// I often get an error on hr2.addEventListener
hr2.addEventListener('click', () => {
two.classList.remove('hide');
hr2.id = "hr3";
})
Your code throws error because you are trying to set hr2 and hr3 when they are not exist.
You need to set hr2 and hr3 variables after setting id's of them like below:
hr1.id = "hr2";
hr2= document.getElementById('hr2');
const text = document.querySelector('.text');
const hide = document.querySelector('.hide');
const one = document.querySelector('.one');
const two = document.querySelector('.two');
var hr1 = document.getElementById('hr1');
var hr2 = null;
var hr3 = null;
hr1.addEventListener('click', () => {
//one.classList.remove('hide');
hr1.id = "hr2";
hr2= document.getElementById('hr2');
console.log(hr2);
hr2.addEventListener('click', () => {
two.classList.remove('hide');
hr2.id = "hr3";
hr3 = document.getElementById('hr3');
console.log(hr3);
})
})
// I often get an error on hr2.addEventListener
<div class="container">
<div class="text">
<div class="one hide">
One
</div>
<div class="two hide">
Two
</div>
<div class="three hide">
Three
</div>
</div>
clickme
</div>

parameter is coming out undefined

I am in the process of building a hangman game. In my checkLetter function is where i am going to write code to check if a letter matches a chosen word. However I have noticed that when i passed in letter to onclick for function checkLetter, the console log will come out undefined but it will also display collection of html tags from the button that is pressed. In the function checkLetter i passed in pickLetter as a parameter. This is where buttons work but it also comes out undefined. I am very certain that I wrote this correctly but i know something is missing. Any help? I hope i made myself clear.
document.body.onload = createButtons;
//keyboard added dynamically
function createButtons() {
const buttons = alphabet.map(letter =>
`<button id = "${letter}"
class="btn btn-primary letterKey"
button type="button"
value="${letter}"
onclick = "checkLetter(${letter})"
>
${letter}
</button>`).join('');
keyboardBtn.innerHTML = buttons;
//prints letters to answer input/screen
Array.from(document.getElementsByClassName("letterKey"))
.forEach((e) =>
e.addEventListener("click", () => placeLetters.innerHTML += e.value))
}
//check letter of chosen word, if its there or not
function checkLetter(pickLetter) {
console.log(pickLetter)
}
checkLetter();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/css/bootstrap.min.css"
integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css">
<title>Hangman 2021</title>
</head>
<body>
<!--header-->
<header class="container-fluid bg-success text-dark">
<div>
<span>
<h1 class="text-center">Hang that man</h1>
</span>
</div>
</header>
<div class="container">
<br>
<h3>Please choose a letter on your keyboard to reaveal the secret word</h3>
<br>
<!--each letter will display after all lives are gone-->
<div id="keyboard"></div>
<br>
<br>
<!--choices will be inserted here-->
<div id="answer-input">
<p id="letter-input">_ _ _ _ _ _ _ _ _ _ _ </p>
</div>
<!--number of lives will be tracked here-->
<p id="lives">You have 8 lives left</p>
<!--everytime a guess is wrong, a limb is added to animation-->
<section class="animation">
<div class="justify-content-center">
<canvas id="gallows" width="300" height="150" style="border:1px solid #d3d3d3"></canvas>
</div>
<button id="reset">Play Again</button>
</section>
<br>
</div>
<!--footer-->
<footer class="container-fluid bg-success text-dark">
<div class="justify-content-center">Hang that man © 2021</div>
<div class="social justify-content-center">
<i class="fab fa-linkedin"></i>
|
<i class="fab fa-github"></i>
</div>
<div class="github">
<a>Andres Ramirez</a>
</div>
</footer>
<script type="text/javascript"src="index.js" defer></script>
</body>
</html>
have you tried using :
Array.from(document.getElementsByClassName("letterKey"))
.forEach((e) =>
e.addEventListener("click", () => placeLetters.innerText+= e.value))
(note that innerHtml returns everything from html) you could try:
placeLetters.textContent
placeLetters.innerText
placeLetters.value
I created an example with comments. You can use it to see how to handle the clicks. I would advise to use event delegation to handle click events instead of 26 separate handlers. See this blog for more info. I added comments to the code below so it's pretty clear but let me know if you have any questions. The game obviously isn't 100% finished, but should be enough so you can take it from there.
//Your HTML elements that will be used for the game UI
const btnContainerEl = document.querySelector("#btn-container");
const guessTrackerEl = document.querySelector("#guess-tracker");
const winningWordEl = document.querySelector("#winning-word");
//Some const variables to avoid magic numbers in the code
const CHAR_CODE_a = 97;
const STRIKES_TIL_HANGED = 5;
//A variable to keep track of the remaining letters the player needs to win
let lettersNeededToWin;
//A variable to keep track of unsuccessful guesses
let strikes = 0;
initGame();
function initGame() {
getWinningWord();
createLetterButtons();
}
function getWinningWord() {
//Get the winning word from a prompt and create an array of it's letters
lettersNeededToWin = window.prompt("Enter winning word").toLowerCase().split('').sort();
}
function createLetterButtons() {
//Loop 26 times to create a button for each letter in the English alphabet
for (let i = 0; i < 26; i++) {
const char = String.fromCharCode(CHAR_CODE_a + i);
const btnText = document.createTextNode(char);
const buttonEl = document.createElement("BUTTON");
buttonEl.appendChild(btnText);
//Append the button the UI
btnContainerEl.appendChild(buttonEl);
}
//Add a click event to the parent container instead of each individual button.
btnContainerEl.addEventListener('click', handleLetterButtonClick);
//1 event handler is more efficient than 26 seperate handlers.
}
function handleLetterButtonClick(e) {
if (e.target.tagName != "BUTTON") {
return;
}
e.target.disabled = true;
updateGuessTracker(e.target.textContent);
if (lettersNeededToWin.includes(e.target.textContent)) {
while (lettersNeededToWin.includes(e.target.textContent)) {
//Remove these letters from the array (in-place operation)
lettersNeededToWin.splice(lettersNeededToWin.indexOf(e.target.textContent), 1);
}
} else {
//Add another strike since players guess was incorrect
strikes++;
}
//Check the number of letters left and number of strikes to determine win/lose outcome
if (lettersNeededToWin.length == 0) {
console.log("YOU WIN!");
} else if (strikes == STRIKES_TIL_HANGED) {
console.log("YOU LOSE!");
}
}
function updateGuessTracker(guessLetter) {
guessTrackerEl.innerHTML += `<font color="${lettersNeededToWin.includes(guessLetter) ? 'green' : 'red'}">${guessLetter}</font> `;
}
<div id="winning-word">Winning word:</div>
<br/>
<div id="btn-container"></div>
<div id="guess-tracker">Guessed letters:</div>

Javascript function to rotate some div elements on page

I have some javascript function - shows me a popup with some texts. I try to rotate two "section" elements, but if I add to HTML one more section with class custom, the page shows only first element. Please, help me to add 1-2 more elements and to rotate it. The idea is to have 2 or more elements with class custom and to show it in random order, after last to stop. Thanks.
setInterval(function () {
$(".custom").stop().slideToggle('slow');
}, 2000);
$(".custom-close").click(function () {
$(".custom-social-proof").stop().slideToggle('slow');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="custom">
<div class="custom-notification">
<div class="custom-notification-container">
<div class="custom-notification-image-wrapper">
<img src="checkbox.png">
</div>
<div class="custom-notification-content-wrapper">
<p class="custom-notification-content">
Some Text
</p>
</div>
</div>
<div class="custom-close"></div>
</div>
</section>
Set section display none of page load instead of first section. Check below code of second section:
<section class="custom" style=" display:none">
<div class="custom-notification">
<div class="custom-notification-container">
<div class="custom-notification-image-wrapper">
<img src="checkbox.png">
</div>
<div class="custom-notification-content-wrapper">
<p class="custom-notification-content">
Mario<br>si kupi <b>2</b> matraka
<small>predi 1 chas</small>
</p>
</div>
</div>
<div class="custom-close"></div>
</div>
</section>
And you need to make modification in your jQuery code as below:
setInterval(function () {
var sectionShown = 0;
var sectionNotShown = 0;
$(".custom").each(function(i){
if ($(this).css("display") == "block") {
sectionShown = 1;
$(this).slideToggle('slow');
} else {
if (sectionShown == 1) {
$(this).slideToggle('slow');
sectionShown = 0;
sectionNotShown = 1;
}
}
});
if (sectionNotShown == 0) {
$(".custom:first").slideToggle('slow');
}
}, 2000);
Hope it helps you.

Turning cards (memory)

I'm doing a memory and got a problem when turning cards. After turning two cards, when I turn a third one, I want the 2 previous cards to be turned again, but for some reason sometimes it works and sometimes only the first of the two cards turned turns. Any idea why this is happening?
JAVASCRIPT (where I think there's the problem):
var cartas = [];
var cartasGiradas = [];
var parejas= 0;
function cambiar_clase(){
cartasGiradas = document.getElementsByClassName("flip-container clicked");
if(cartasGiradas.length==2){
for (var i = 0; i <= cartasGiradas.length; i++) {
cartasGiradas[i].className="flip-container";
this.classList.add("clicked");}
}else if(cartasGiradas.length < 2){
this.classList.add("clicked");
}
}
function inicializar(){
cartas = document.getElementsByClassName("flip-container");
for(var i = 0; i < cartas.length; i++){
document.getElementById("a_girar"+i).addEventListener( 'click', cambiar_clase());
}
}
The HTML source:
<html>
<head>
<script type="text/javascript" src="javascript_memory.js"></script>
<link rel="stylesheet" type="text/css" href="css_memory.css">
<title>MEMORY GUERRA</title>
<meta charset="utf-8">
</head>
<body onload="inicializar()">
<table>
<tr>
<td class='color_fondo'>
<div class='flip-container' id='a_girar0'>
<div class='flipper'>
<div class='front'>
<img src='tras_carta.jpg' class='fotos'>
</div>
<div class='back'>
<img src='cartas/animal_15.jpg' class='fotos'>
</div>
</div>
</div>
</td>
<td class='color_fondo'>
<div class='flip-container' id='a_girar1'>
<div class='flipper'>
<div class='front'>
<img src='tras_carta.jpg' class='fotos'>
</div>
<div class='back'>
<img src='cartas/animal_14.jpg' class='fotos'>
</div>
</div>
</div>
</td>
</tr>
</table>
</body>
</html>
Hope somebody can help and sorry if my english is bad, goodbye!

Set CSS property with js click event handler

I am trying to create an onclick event which will show/hide elements of an array however I am struggling with the showSubTabGroup() function below.
Sidenote: Eventually I would like to make this onmouseenter and onmouseleave, rather than 'click' as you see below.
I would like to be able to click on a div and show subsequent div's below as you might expect from a navigation feature.
The console is not returning any errors, however the 'click' function seems alert("CLICKED") properly.
Any help would be greatly appreciated.
Problem:
Tab.prototype.showSubTabGroup = function (tabIndex, subTabIndex) {
this.tab[tabIndex].addEventListener('click', function () {
alert("CLICKED");//Testing 'click' call
for (var i = subTabIndex; i < this.subTabGroup; i++) {
this.subTab[i].style.display = "";
}
});
}
function Tab (subTabGroup) {
this.tab = document.getElementsByClassName("tab");
this.subTab = document.getElementsByClassName("sub-tab");
this.subTabGroup = subTabGroup;
}
Tab.prototype.hideSubTabs = function () {
for (var i = 0; i < this.subTab.length; i++) {
this.subTab[i].style.display = "none";
}
}
Tab.prototype.showSubTabGroup = function (tabIndex, subTabIndex) {
this.tab[tabIndex].addEventListener('click', function () {
for (var i = subTabIndex; i < this.subTabGroup; i++) {
this.subTab[i].style.display = "";
}
});
}
var tab = new Tab(3);
tab.hideSubTabs();
tab.showSubTabGroup(0,0);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Responsive Nav</title>
</head>
<body>
<!-- JQuery CDN -->
<script src="http://code.jquery.com/jquery-3.1.0.js"></script>
<div class="container">
<div class="tab">
<p>TAB</p>
</div>
<div class="sub-tab">
<p>SUBTAB</p>
</div>
<div class="sub-tab">
<p>SUBTAB</p>
</div>
<div class="sub-tab">
<p>SUBTAB</p>
</div>
</div>
<div class="container">
<div class="tab">
<p>TAB</p>
</div>
<div class="sub-tab">
<p>SUBTAB</p>
</div>
<div class="sub-tab">
<p>SUBTAB</p>
</div>
<div class="sub-tab">
<p>SUBTAB</p>
</div>
</div>
<div class="container">
<div class="tab">
<p>TAB</p>
</div>
<div>
<div class="sub-tab">
<p>SUBTAB</p>
</div>
<div class="sub-tab">
<p>SUBTAB</p>
</div>
<div class="sub-tab">
<p>SUBTAB</p>
</div>
</div>
</div>
<script type="text/javascript" src="tab.js"></script>
<script type="text/javascript" src="tabEvents.js"></script>
</body>
</html>
The problem of what is this inside the click. It is not your tab code, it is the reference to the element that you clicked on. You need to change that by using bind
Tab.prototype.showSubTabGroup = function (tabIndex, subTabIndex) {
this.tab[tabIndex].addEventListener('click', (function () {
for (var i = subTabIndex; i < this.subTabGroup; i++) {
this.subTab[i].style.display = "";
}
}).bind(this));
}
As I read your post, I assume hideSubTabs() is working properly. In that case I will suggest to use in showSubTabGroup():
this.subTab[i].style.display = "initial";
instead of
this.subTab[i].style.display = "";
"initial" will set to its default e.g. as "block" for div and "inline" for span
I see that you also have included jQuery. If I may ask why don't jQuery functions which will do the same with less code:
$('.tab').on('click',function() {
$(this).closest('.container').find('.sub-tab').toggle()
})
$('.tab').click();
$('.tab')[0].click();

Categories