Javascript Hangman Game Running Functions += Times After Reset - javascript

Ok this is my very first ever coding project. I'm self taught and thought creating a hangman game my 2 young kiddos could enjoy would be fun.
I have the game coded and it runs perfectly fine the first time through however, once I click on my reset button to restart the game, my lives start going down by 2 instead of one. After a third reset it goes down by three, and so on. Any console.log()s I put in to check run as many times as well.
I've spend hours racking my brain trying to figure it out and I've looked all over and I'm just not getting what's wrong. I thought about just saying screw it and resort to refreshing the page after every play through to avoid the problem but my brain wont let me do that.
Does anyone have any idea how to fix the problem? I know my code is probably not the most efficient and can definitely use to touch ups, but like I said, this is my first ever project and I know efficiency and skill will come with time and practice (like this). Below is the main chunk of my code.
function gameStart() {
// user writes in a word
pickedWord = prompt('pick a word').toUpperCase();
pickLetterHeading.innerText = 'Pick a Letter'
numLivesSec.hidden = false;
startBtn.disabled = true;
gameIsGoing = true;
resetBtn.disabled = false;
// enables buttons to press
for (let j = 0; j < alphabet.length; j++) {
document.getElementsByClassName('letterBtnClass')[j].disabled = false;
}
numLives = 7;
numLivesRemaining.innerText = numLives;
// calls the main game function
gameInProgress();
}
function reset() {
startBtn.disabled = false;
pickedWord = '';
gameIsGoing = false;
resetBtn.disabled = true;
pickLetterHeading.hidden = true;
numLivesSec.hidden = true;
while (guessedSec.firstChild) {
guessedSec.removeChild(guessedSec.firstChild);
}
for (let l = 0; l < alphabet.length; l++) {
document.getElementsByClassName('letterBtnClass')[l].disabled = true;
letters[l].classList.replace('correct', 'letterBtnClass');
letters[l].classList.replace('incorrect', 'letterBtnClass');
}
numLives = 7;
}
function gameInProgress() {
pickLetterHeading.hidden = false;
wordInProgress = '';
for (let i = 0; i < pickedWord.length; i++) {
let lettersToGuess = document.createElement('span');
lettersToGuess.innerText = '_';
guessedSec.appendChild(lettersToGuess);
wordInProgress += '_';
}
for (let btn = 0; btn < letters.length; btn++) {
letters[btn].addEventListener('click', function (e) {
letters[btn].disabled = true;
if (pickedWord.indexOf(letters[btn].innerText) == -1) {
console.log('letter not there!');
numLives -= 1;
numLivesRemaining.innerText = numLives;
console.log('TESTING!')
// checks if you have any tries left. If you have 0, then runs game over
if (numLives == 0) {
console.log('GAME OVER!')
gameIsGoing = false;
pickLetterHeading.innerText = 'You lose. Try again.'
for (let j = 0; j < alphabet.length; j++) {
document.getElementsByClassName('letterBtnClass')[j].disabled = true;
}
}
}
// checks if the letter picked matches anything in the chosen word
for (let x = 0; x < pickedWord.length; x++) {
if (letters[btn].innerText == pickedWord[x]) {
// adds class to chosen letter to show you guess correctly
letters[btn].classList.add('correct');
document.getElementsByTagName('span')[x].innerText = pickedWord[x];
wordInProgress[x] = pickedWord[x];
// if picked letter matches something in the chosen word, changes the "_" to the correct letter to show user progress
String.prototype.replaceAt = function (index, char) {
let a = this.split("");
a[index] = char;
return a.join("");
}
wordInProgress = wordInProgress.replaceAt(x, pickedWord[x]);
// when the word is complete. will show you win
if (wordInProgress == pickedWord) {
console.log("WINNER WINNER CHICKEN DINNER!");
pickLetterHeading.innerText = 'YOU WIN!'
for (let j = 0; j < alphabet.length; j++) {
document.getElementsByClassName('letterBtnClass')[j].disabled = true;
}
}
} else {
// since you chose incorrectly it adds a class to the button to show a bad choice
letters[btn].classList.add('incorrect');
}
}
})
}
}

Related

Add "No results found" message on div search

I'm making an audio player, and I have a list of divs acting as my playlist... I'm using JS to make the list, and I'm using this script to search through them:
/*Search Songs*/
function searchSongs(){
let input = _('#songSearch').value.toLowerCase();
let items = _all('.item');
let dividers = _all(".divider");
for (let i = 0; i < items.length; i++) {
if (!items[i].innerHTML.toLowerCase().includes(input)) {
items[i].style.display = "none";
}
else {
items[i].style.display = "";
}
}
// add noresults message at end if all list divs are hidden
if (!items.innerHTML.toLowerCase().includes(input)) {
_('.noResults').innerHTML = `<p>No results found for "${input}"`
}
}
I have a paragraph element at the end of my list (with nothing in it) and I want to show the message (<p>No results found for "${input}") is there some js I can use to accomplish this? The script above is working for the searching, but not working for the message.
Finished result:
/*Search Songs*/
function searchSongs(){
let input = _('#songSearch').value.toLowerCase();
let items = _all('.item');
let dividers = _all(".divider");
let counter = 0;
for (let i = 0; i < items.length; i++) {
if (!items[i].innerHTML.toLowerCase().includes(input)) {
items[i].style.display = "none";
counter++;
}
else {
items[i].style.display = "";
}
}
// keeping the result 22 letters long (for asthetic purposes)
let maxLen = 22;
if (input.length > maxLen) {
input = input.substring(0, maxLen - 3) + "...";
}
if (counter >= items.length) {
_('#noResults').innerHTML = `No results found for "${input}"` //add no results message if all items are hidden...
} else {
_('#noResults').innerHTML = `` //else hide the message by removing text.
}
}
Thanks, #Asif !!
I've made a little modification in your function. I hope this will solve your issue.
/*Search Songs*/
function searchSongs(){
let input = _('#songSearch').value.toLowerCase();
let items = _all('.item');
let dividers = _all(".divider");
let counter = 0;
for (let i = 0; i < items.length; i++) {
if (!items[i].innerHTML.toLowerCase().includes(input)) {
items[i].style.display = "none";
}
else {
items[i].style.display = "";
counter++;
}
}
// add noresults message at end if all list divs are hidden
if (counter > 0) {
_('.noResults').innerHTML = `<p>No results found for "${input}"`
}
}

Making blocks visible/invisible using javascript

Here is my code.
var i = 0;
var submenues = document.getElementsByClassName("submenu");
var click = 1;
function submenuvisible() {
if (click == 1) {
for (i; i < submenues.length; i++) {
submenues[i].style.display = "block";
}
click = 2;
return;
}
if (click == 2) {
for (i; i < submenues.length; i++) {
submenues[i].style.display = "none";
}
click = 1;
return;
}
}
Though when i onclick=submenuvisible() it works only 1 time. What am I doing wrong?
Your mistake is in your for loops.
Where you have: for (i; i < submenues.length; i++) {
You need to reset the variable i to 0 at the beginning of the for loops.
for (i = 0; i < submenues.length; i++) {
If you don't reset it, then i will remain at the same value it was after the first time you run your function. You could improve your code further by not making i a global variable, but overall, I hope this explains your issue.

I have repeated JS code, how do I turns these into functions

Here is the JSFiddle link for my project, you can see the full app.js here... https://jsfiddle.net/be4pLh7s/1/
Basically I am trying to build a contact diary where you can create new contacts and then be able to edit or delete them at a later stage...
on line 85 onwards is the click event for the Edit and Delete buttons, I THINK that the way I have written this part of the code maybe incorrect because I have repeated loops and if statements.
I have tried putting these repeated parts of the code into functions but then the app crashes and I receive different errors. I have tried a couple of things to overcome these errors but still cant get it to work and or get my head around this.
Please could you advise if the code is correct... have I repeated myself? If I have then please can you show/tell me how to make this code better in terms of DRY, and readability. Thank you.
How do I code separate functions for the "Edit", "Save" and "Delete" buttons.
This line of code is repeated 3 times -
for (var i = 0; i < contactsBook.length; i++) {
if (contactsBook[i].firstName === ul.getAttribute('data-person')) {}
This line of code is repeated 2 times -
const ulChild = ul.childNodes;
for (var j = 0; j < ulChild.length; j++) {
if (ulChild[j].tagName === "LI") {}
Here is the part of the code in question -
//Click event for Edit and Delete buttons.
contacts.addEventListener("click", (e) => {
if (e.target.tagName === "BUTTON") {
const button = e.target;
const ul = button.parentNode;
if (button.textContent === "Edit") {
for (var i = 0; i < contactsBook.length; i++) {
if (contactsBook[i].firstName === ul.getAttribute('data-person')) {
const ulChild = ul.childNodes;
for (var j = 0; j < ulChild.length; j++) {
if (ulChild[j].tagName === "LI") {
const items = ulChild[j];
const input = document.createElement('input');
input.type = 'text';
input.value = items.textContent;
items.textContent = "";
items.insertBefore(input, ulChild.childNodes);
button.textContent = 'Save';
};
};
};
};
} else if (button.textContent === "Save") {
for (var i = 0; i < contactsBook.length; i++) {
if (contactsBook[i].firstName === ul.getAttribute('data-person')) {
const ulChild = ul.childNodes;
for (var j = 0; j < ulChild.length; j++) {
if (ulChild[j].tagName === "LI") {
console.log(ulChild[j]);
};
};
};
};
} else if (button.textContent === "Delete") {
contacts.removeChild(ul);
for (var i = 0; i < contactsBook.length; i++) {
if (contactsBook[i].firstName === ul.getAttribute('data-person')) {
contactsBook.splice(i,1);
localStorage.setItem('addbook', JSON.stringify(contactsBook));
};
};
};
};
});
Thank you for your help!
Not much of repeated code. Only hint I could give is if you want to use more javascrpt flare, e.g. .forEach instead of standard for loop and .filter since you have a for and if statement.
For an example
for (var i = 0; i < contactsBook.length; i++) {
if (contactsBook[i].firstName === ul.getAttribute('data-person'))
....
Can be done with:
contactsBook.filter(book => book.firstName === ul.getAttribute('data-person'))
Not that it makes any difference.

Show modal and wait for action for each element while looping

I'm looking for simple solution for a loop where I have to confirm each element. I was looking at pausing and resuming, but it's quite a hassle. Is there any other way to make it easy? I'm sure that this problem is not so rare and many people have stuck upon it.
What I want to achieve is this - I'm looping through the list and if I don't find item by its EAN code then it opens search function to find this and after the item is found (or not) user clicks Next and resume with looping until same situation occurs.
Some code that I have for now:
for(var f = 0; f < biLen; f++){
var ean_parsed = parsedList[i][1];
if(beerList[f].get("ean") === ean_parsed){
console.log("Beer found: " + beerList[f].get("beer_name"));
break;
} else {
if(f === biLen - 1){
//open modal, search for item and then continue looping
console.log("B'r not found: " + parsedList[i][0]);
}
}
}
edit (whole function code instead of piece of it):
function parserCompareList(){
var parsedList = parseResult;
var piLen = parsedList.length;
var beerList = listaPiwArr;
var biLen = beerList.length;
var new_offer = [];
var counter = document.getElementById('imHeader');
for(var i = 0; i < piLen; i++){
counter.innerHTML = i + "/" + piLen; //plain text that's keeping where we actually are with this
for(var f = 0; f < biLen; f++){
var ean_parsed = parsedList[i][1];
if(beerList[f].get("ean") === ean_parsed){
console.log("Beer found: " + beerList[f].get("beer_name"));
break;
} else {
if(f === biLen - 1){
console.log("B'r not found: " + parsedList[i][0]);
}
}
}
}
}
ANSWER:
var indexCache;
function loop() {
var index = indexCache || 0;
for (var f = index; f < biLen; f++) {
var ean_parsed = parsedList[i][1];
if (beerList[f].get("ean") === ean_parsed) {
console.log("Beer found: " + beerList[f].get("beer_name"));
break;
} else {
if (f === biLen - 1) {
// Assuming $modal is the bootstrap modal
indexCache = f;
//$modal.modal().one('hide.bs.modal', loop);
$('#importModal').modal('show').one('hidden.bs.modal', loop) // <-- this one works like a charm
return;
}
}
}
}
loop();
var indexCache;
function loop() {
var index = indexCache || 0;
for (var f = index; f < biLen; f++) {
var ean_parsed = parsedList[i][1];
if (beerList[f].get("ean") === ean_parsed) {
console.log("Beer found: " + beerList[f].get("beer_name"));
break;
} else {
if (f === biLen - 1) {
// Assuming $modal is the bootstrap modal
indexCache = f;
$modal.modal().one('hide.bs.modal', loop);
return;
}
}
}
}
loop();
We have an indexCache variable that holds the index of the beer that is being handled.
If EAN is found, great! we handle it and move on to the next beer.
If not, we store the current beer index in the cache and show the modal and quit the loop immediately. When the modal is hidden, the loop resumes from the cached index.
PS. I am assuming you are using Twitter Bootstrap modal. And therefore, adding a handler to the event 'hide.bs.modal'. But similar thing could be done with your own modal implementation if that is the case.

Cannot set the printBlack printPreference in InDesign

I've learned an enormous amount of scripting for InDesign CS6 thanks to all of the helpful folks here! Now, it's a problem with setting the printPreferences for a document. Here is the code I have:
with(document.printPreferences) {
activePrinterPreset = outputPreset;
pageRange = outputRange;
for (var j = 0; j < allInks.length; j++) {
document.inks.item(allInks[j]).printInk = false;
}
for (var k = 0; k < printInks.length; k++) {
if (printInks[k].toString() === "Black") {
$.writeln("Found Black!");
printBlack = true;
$.writeln("Set Black!");
} else {
document.inks.item(printInks[k]).printInk = true;
}
}
if (offsetJob) {
// If it's an offset job, we might need to change page sizes.
if (productType === "HI-N13W") {
paperSize = PaperSizes.custom;
paperHeight = 12.5;
paperWidth = 8.5;
} else if (productType.subString(3,5) === "PC") {
paperSize = PaperSizes.custom;
paperHeight = 8;
paperWidth = 12.5;
} else if (couldBeBothJobs.toString().indexOf(productType.subString(3,5)) > -1) {
paperSize = "US Letter";
} else {
paperSize = PaperSizes.custom;
paperHeight = 8;
paperWidth = 25;
}
}
}
In the second for loop, you'll see that I have first turned off ALL of the inks in the document for printing. I then only turn on the ones in the printInks array. If, however, the word "Black" exists in the array, there isn't an Ink for it, so instead, I want to set the built-in printPreference "printBlack". (This complements the other three—printCyan, printMagenta, and printYellow.)
It's just supposed to be a boolean value, according to the InDesign CS6 Object Model Reference. However, whenever the script gets to that point, it halts. Pasting just that small bit of code to a fresh document, just so I can see the error message, gets me this:
The property is not applicable in the current state.
What does this mean? And more importantly, how can I fix it? I know that trapping has to be off before this property can be set, but I've definitely made sure that it is off.
After asking this question on the Adobe Community Forums, I was told that some of the Print Preferences cannot be set directly on the document, but must instead be set on a Print Preset, which, in turn, is then set as the Active Print Preset. Thus, I just re-built the preset I needed and assigned it to a new, temporary one. The following is the result:
try {
tempPreset.name;
}
catch(eNoSuchPreset) {
tempPreset = app.printerPresets.add({name:"tempPreset"});
}
// Let's turn off all of the spot colors before everything else.
for (var j = 0; j < allInks.length; j++) {
document.inks.item(allInks[j]).printInk = false;
}
with(document.printPreferences) {
tempPreset.printer = Printer.POSTSCRIPT_FILE;
tempPreset.ppd = "OYO Imagesetter";
tempPreset.pagePosition = PagePositions.CENTERED;
tempPreset.paperSize = PaperSizes.CUSTOM;
tempPreset.paperHeight = "12.5 in";
tempPreset.paperWidth = "6.5 in";
tempPreset.colorOutput = ColorOutputModes.SEPARATIONS;
tempPreset.trapping = Trapping.OFF;
tempPreset.screening = "60 lpi / 600 dpi";
tempPreset.sendImageData = ImageDataTypes.OPTIMIZED_SUBSAMPLING;
pageRange = outputRange;
// Now let's turn off all of the CMYK colors.
tempPreset.printCyan = false;
tempPreset.printMagenta = false;
tempPreset.printYellow = false;
tempPreset.printBlack = false;
// Then we turn back on BLACK if it exists.
for (var k = 0; k < printInks.length; k++) {
if (printInks[k] === "Black") {
tempPreset.printBlack = true;
} else {
document.inks.item(printInks[k]).printInk = true;
}
}
// If this is a four-color process job, then turn back on all of the process colors.
if (processJob) {
tempPreset.printCyan = true;
tempPreset.printMagenta = true;
tempPreset.printYellow = true;
tempPreset.printBlack = true;
}
// That concludes OYO seps.
}
var mydpp = document.printPreferences;
mydpp.activePrinterPreset = "tempPreset";
That gets my 99% of the way to completing this script. YAY!!

Categories