Why is a Javascript function without declared parameter working? - javascript

I'm writing a hangman game. With the help of Google I have a function to check if a letter is in the word, but I don't understand how, although I never declare the parameter "chosenLetter" of my function, this is able to give "chosenLetter" exactly the value of the specific letter I click on. Nor how "getElementById(chosenLetter)" can pick exactly the letter I click on to disable the button.
Same thing with the "guessedWord" function, where "letter" is never declared, yet it selects a letter to check for in the "guessed" array
let answer = '';
let maxWrong = 5;
let mistakes = 0;
let guessed = [];
let wordStatus = null;
function guessedWord() {
wordStatus = answer.split('').map(letter => (guessed.indexOf(letter) >= 0 ? letter : '_')).join('');
document.getElementById('word').innerHTML = wordStatus;}
function generateButtons() {
let buttonsHTML = 'abcdefghijklmnopqrstuvwxyz'.split('').map(letter =>
`
<button
class = "btn"
id = '` + letter + `'
onClick = "handleGuess('` + letter + `')"
>
` + letter + `
</button>
`).join('');
document.getElementById('alphabet').innerHTML = buttonsHTML;}
function handleGuess(chosenLetter) {
if (guessed.indexOf(chosenLetter) === -1) {
guessed.push(chosenLetter)
} else {
null;
}
document.getElementById(chosenLetter).setAttribute('disabled', true);
if (answer.indexOf(chosenLetter) >= 0) {
guessedWord();
checkIfGameWon();
} else if (answer.indexOf(chosenLetter) === -1) {
mistakes++;
updateMistakes();
checkIfGameLost();
}}
As I'm new at javascript, I'm generally trying to understand how is it possible that parameters not declared can seem to declare themselves so specifically? I don't know if I'm asking the wrong question, but that's the only thing in the code which I can't understand nor explain

You need to read this documentation first.
It says
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
If we try to break the following statement in separate statements,
wordStatus = answer.split('').map(letter => (guessed.indexOf(letter) >= 0 ? letter : '_')).join('');
You will find,
let splittedAnswer = answer.split(''); // This will split the answer string and crate an array with all the characters in answer.
const callbackFunction = (letter) => {
return guessed.indexOf(letter) >= 0 ? letter : '_';
}
let newArray = splittedAnswer.map(callbackFunction);
let joinedString = newArray.join('');
As you can see you are declaring a callback function. You are passing that function to the array.map() method. Now the array.map() method will call the function with 3 params currentItemInTheArray, currentIndex, theSourceArray. As you have declared only the first param in your callbackFunction i.e. currentElementInTheArray you will get the first param. You could declare your callback array like this,
const callbackFunction = (item, index, arr) => {
console.log(item);
console.log(index);
console.log(arr);
}
And now if you call
[1,2,3].map(callbackFunction);
You will get the following output,
1 //currItem
0 //index
[1,2,3] //source array
2 //currItem
1 //index
[1,2,3] //source array
3 //currItem
2 //index
[1,2,3] //source array
But you are doing it inline, that is why you were not following. I hope now it is clear to you.

This is how functions work, you pass an argument as an input and to use that argument in your function you use parameters in the function definition which act as placeholders for your input and then you can use the parameters anywhere inside your function.
Example: In the function guessedWord(), you are using a map function which takes a callback function as an argument. So the work letter is actually the parameter of the callback function and parameters don't need declaration they are just placeholders.

generateButtons is going to insert into the HTML element "alphabet" something that looks like this:
<button
class = "btn"
id = 'a'
onClick = "handleGuess('a')"
>
a
</button>
<button
class = "btn"
id = 'b'
onClick = "handleGuess('b')"
>
b
</button>
...
Those onClick handlers will be function that pass the various letters as the first (and only) argument to handleGuess.
handleGuess is a function declared to take a single parameter, which is known internally to the function as chosenLetter. But when you call it, you do not have to pass it a variable with the name chosenLetter. Javascript doesn't care about that. You could pass it a variable named foobar and it would be fine, or you could just pass it the raw value.
For a simpler example, let's say we had a simple function:
function area (height, width) {
return height * width
}
we could call it in various ways, including:
const height = 5;
const width = 8;
area (height, width) //=> 40
// or
const h = 6
const w = 7
area (h, w) //=> 42
// or
const rectangle = {
height: 3,
width: 10
}
area (rectangle.height, rectangle.width) //=> 30
// or simply
area (4, 9) //=> 36
The same principle applies to handleGuess

in JavaScript
variables that don't get declared will get automatically assigned to the global scope
is simple words it will declare it self :)
in your case something different is happening
you are passing the letter as a parameter to the function
onClick = "handleGuess('` + letter + `')"
and this is why it makes all the magic come alive :)
the same goes to the id property
id = '` + letter + `'
passing the information about which specific letter you are pressing on make it so that the JavaScript Engine know which button is being pressed and that is how it know what letter you chose and what he need to disable

Related

call for loop through function with parameters

I have the below for loops, I have it manytime in my code, with different variables name, I would put it in a function with parameters and call it but it didn't work
for(let i = 0; i < inputs.length - 1; i++){
if(!nputs[i].value){
error += inputs[i].getAttribute('test') + " is blank";
isTrue = false;
}
}
Here what I did
let y = "";
let z = true;
function test(x,y,z){
for(let i = 0; i < x.length - 1; i++){
if(!x[i].value){
y += x[i].getAttribute('name') + " is blank !";
z = false;
}
}
}
let error = "";
let isTrue = true;
test(inputs,error,isTrue);
shall I do return in the function? if yes which return should I do?
Scope: when you define y and z outside the function (in the global scope presumably) they are different than the y and z defined in the parameter list of the function. The ones in the parameter list only exist within the body of the function. Changing the value of the parameter named y within the function does not change the value of the global variable y. So the simple answer to your question is, yes, you need to return something, since the value of the parameter y is lost when the function is done executing.
Give your variables descriptive names. Let the obfuscator do it's thing later.
function test(x,y,z) -> function valueTest(arr, err, success)
The boolean status and error string are redundant bits of information. If the error string is not empty, then the status is failure. So you don't need to return both a boolean and the string.
The status of the previous test is of no relevance to the next test. Therefore, z or success doesn't have to be passed in to the function each time, as it (or something like it) is really the desired output of the function, and each call of the function can be treated separately. If you want to combine the results from different tests then that should be the concern of whatever is calling this function - see separation of concerns and coupling
The only parameter the function actually needs is the value that is under test (the array).
When you write the function you define the return value, and thus you define how other code can decipher those results. The function itself doesn't have to do all the work of interpreting the results and building the error string. If your return value was just an array of name attribute values (of the elements of the test array that failed), the calling code could still process "success" or "failure". If the return value has one or more elements, or a length > 0 that would indicate failure.
Removing the redundant/unnecessary parameters and information, you'll have a function that looks something like this:
function valueTest(arr) {
let results = [];
for (let i = 0; i < arr.length - 1; i++){
if (!arr[i].value) {
results.push(arr[i].getAttribute('name'));
}
}
return results;
}
The caller can decipher and build an error message from that. It might make sense for the function to handle some of the additional work by returning <name> is blank! instead of just <name>, and then you just need to join the elements of the array.
...so within the function...
results.push(arr[i].getAttribute('name') + ' is blank!');
...and back in the global scope...
const error = valueTest(inputs).join(" ");
let success = error.length > 0;
5.If you want a running status indicator from different tests, evaluate an individual test's result, then logically AND that with the previous result.
const result1 = valueTest(inputs1).join(' ');
let success = results1.length > 0;
const result2 = valueTest(inputs2).join(' ');
success &= (results2.length > 0);
Seeing the issues with your code are handled in the comments, I present you a simpler method.
If you count the elements that have the attribute and are not empty and compare it to the length of all the inputs passed you will have a better test
const test = (inputs,attr) => [...inputs]
.filter(inp => inp.getAttribute(attr) && inp.value.trim() !== "").length === inputs.length;
const istrue = test(document.querySelectorAll("input"),"name")
isTrue will be true if all passed inputs has an attribute called name
You can also do
const elems = document.querySelectorAll("input[name]")
const isTrue = elems.filter(inp => inp.value.trim() !== "").length === elems.length

How to remove dynamically added text-content and offer new text?

In my HTML I want to place a word within the id=“button__anfang“
<p class="card-text" id="text__anfang"></p>
For this I have an array with words to chose from. A function: ausgabe_start choses randomly a word and place it there by click on a button.
This is the function which places the randomly choosen word in the node with the id "text-anfang".
let ausgabe_start = () => {
arrWords.push(textAnfang.textContent = start[number_start]);
}
For some reason it pushes the word into another array. [number_start] is the index which takes a word from the array start. It is randomly.
When I don't like the word, I want to click the button again, to get a new one. And I don't want a complete page reload
How can I make the function chose again from the array and place the word within id="text__anfang"?
I tried a query within the function like in pseudocode:
If text__anfang.length != "0" delete text__anfang.
Pls. take a look at this codepen
In the callback function for the click event listener you're actually pushing the assignment to textAnfang.textContent to the array arrWords.
The text itself is defined by the result of start[number_start]); where start is your array of words and number_start a random integer. If you want to have another random index, you need to give number_start another value inside the callback handler like:
let ausgabe_start = () => {
let number_start = Math.floor((Math.random() * 2));
textAnfang.textContent = start[number_start];
}
The drawback here is that it might return the self random word again because there is no check to make sure that it doesn't happen. You can workaround this by putting the assignment inside a do-while loop which repeates itself as long as the random word and the current value of textAnfang are equal.
let ausgabe_start = () => {
let tempStr;
do {
number_start = Math.floor((Math.random() * 2));
tempStr = start[number_start];
}
while (tempStr == textAnfang.textContent);
textAnfang.textContent = tempStr;
}

iterating through and changing nodes in singly linked list

The problem is I don't understand why this code works. It works, but I just can't wrap my mind around it. Here's a function that deletes a node from a singly linked list. I feel like it shouldn't work because it's not actually changing any of the elements in the list, I'm just changing the value of a variable I have set equal to something in the list. In other words, when I create a "runner" variable to iterate through the list, why are the changes I make to "runner" actually changing the list itself. Analogously, if I do
var x = 1
var y = x
y = 2
Obviously, x is still going to equal 1. Why isn't the same true for my Linked List "runner". In the deleteNode function below, why does changing the runner.next value actually change anything in the node or list that exists outside of the function?
function deleteNode(head, position) {
var runner = head
var counter = 0
while (runner) {
if (counter == position - 1) {
runner.next = runner.next.next
return head;
}
runner = runner.next
counter++
}
}
Its because runner is an object, and so the runner variable is a reference to that object.
for example
const x = {a:1}
const y = x;
x.a = 3
console.log(y.a) // this will print 3 also

I am trying to stop my function from displaying the same object twice when clicking a button

I have for quite some time now been trying to figure out how I can stop my code to print the same quote twice.
Also, when every single object in the array has been printed out, I'd like for it to reset somehow. So that you can browse through the quotes once you've gone through all of them.
This is the essential parts of my code:
document.getElementById('loadQuote').addEventListener("click", printQuote, false);
The printQuote function simply contains information that's accessing information from my array:
var randomObjectNumber = getRandomQuote();
var html = "<p class='quote'>"
+ quotes[randomObjectNumber].quote +
"</p>";
document.getElementById('quote-box').innerHTML = html;
One random object is displayed each time you click the eventListener:
function getRandomQuote () {
var randomObjectNumber = Math.floor(Math.random() * quotes.length );
return randomObjectNumber;
}
I have some ideas on how to do this and I have tried them but without success. I tried giving each object a boolean property but I can't really seem to assign each property a boolean value without messing the printQuote function up.
I also tried assigning the object displayed to a different array but the same problem occurred there.
I feel like there is some concepts around the eventListener that I don't fully understand, because every time I try to manipulate a displayed object I just end up changing every single object.
This is what a typical object in the array looks like by the way:
{quote : "Darkness is merely the absence of light"}
(I also have other properties assigned to the object but i feel like presenting them would be redundant)
If someone could explain, or give me a hint, on how to solve this problem I've been struggling with for some time.
Some hints would be greatly appreciated!
Have a nice day.
Sebastian.
EDIT: All code: https://jsfiddle.net/fusqb7hz/
Basically what you need:
Create a separate array that will store all quotes that you've already used.
Remove quote from initial array.
Check if you still have quotes in initial array, if not, get them back from backup array.
The problem is that you call addEventListener twice:
//Let's developers create multiple eventListeners without being redundant.
function onClicking (printFunction) {
document.getElementById('loadQuote').addEventListener("click", printFunction, false);
}
onClicking(printColor);
onClicking(printQuote);
by calling onClicking twice you make the click happen twice, so addEventListener is added twice, meaning one click counts as two.
Change the above code for this:
//Let's developers create multiple eventListeners without being redundant.
document.getElementById('loadQuote').addEventListener("click", function(){
printColor();
printQuote();
});
Here is the jsfiddle:
https://jsfiddle.net/fusqb7hz/3/
I think the easiest approach is to shuffle your quote array and then go through them one by one. This gives you the next "random" as yet unseen quote. The only part I'm not keen on is this shuffler (a derivation of Fisher Yates) modifies the original quote array. You might not care about that though.
// --------------------------------
// A bunch of quotes
// --------------------------------
var quotes = [];
quotes.push({quote : "Darkness is merely the absence of light"});
quotes.push({quote : "quote 2"});
quotes.push({quote : "quote 3"});
quotes.push({quote : "quote 4"});
quotes.push({quote : "quote 5"});
// --------------------------------
// --------------------------------
// Your favorite array shuffle utility
// --------------------------------
var shuffle = function(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
// --------------------------------
// --------------------------------
// construct a function to get a random unseen quote until
// all quotes have been seen. Then reset...
// --------------------------------
var getQuote = (function(quotes, shuffle){
var current = 0;
var get = function(){
if ( !quotes || !quotes.length ) { return ""; }
if ( current >= quotes.length ){ current = 0; }
if ( current === 0 ){
console.log("randomizing quotes...");
shuffle(quotes);
}
return quotes[current++].quote;
};
return get;
})(quotes, shuffle);
// --------------------------------
var printQuote = function(){
document.getElementById('quote').innerText = getQuote();
};
document.getElementById('loadQuote').addEventListener("click", printQuote, false);
<div id="quote"></div>
<button id="loadQuote">get quote</button>

Javascript: Parameter is undefined?

I recently began learning javascript, and this is the answer to a practice problem (Write a function that swaps the cases of each letter in a string):
var swapCase = function(letters){
var newLetters = "";
for(var i = 0; i<letters.length; i++){
if(letters[i] === letters[i].toLowerCase()){
newLetters += letters[i].toUpperCase();
}else {
newLetters += letters[i].toLowerCase();
}
}
console.log(newLetters);
return newLetters;
}
var text = "Life is 10% what happens to you, and 90% of how you REACT to it";
var swappedText = swapCase(text);
OUTPUT:
"lIFE IS 10% WHAT HAPPENS TO YOU, AND 90% OF HOW YOU react TO IT"
The code is perfectly functional and does exactly what it needs to do, but I am confused on the use of letters. This is not my code.
The parameter letters is not linked to anything anywhere, and this is what confuses me. I believe it represents each individual letter, but where is it defined to do so? I am familiar with Python, so I was expecting something like for i in list.
Thanks in advanced.
When you put text in the parameter, text becomes letters inside the function.
var swapCase = function(letters){ //anything you put as a parameter in this function will become 'letters'
var newLetters = "";
for(var i = 0; i<letters.length; i++){
if(letters[i] === letters[i].toLowerCase()){ //letters[i] represents the character in the 'i' position (which is assigned in the for loop) in the string you added as a parameter.
newLetters += letters[i].toUpperCase();
}else {
newLetters += letters[i].toLowerCase();
}
}
console.log(newLetters);
return newLetters;
}
var text = "Life is 10% what happens to you, and 90% of how you REACT to it";
var swappedText = swapCase(text); // You are adding the text string as a parameter in the function, thus it becoming the letter variable inside the function
letters is a function parameter, so basically when you call swapCase(text), the function takes text and assign it to letters. If you call the function without parameter like this swapCase() then you basically pass undefined to this function and that is assign to letter. You can do a quick check at the beginning of the function to check for that.
if(letters === undefined) return false;
The parameter letters is not linked to anything anywhere
It is defined here — function(letters){ — as an argument name on the function.
It is passed a value when the function is called here — swapCase(text); — where text is a variable defined as a string on the line above.
I believe it represents each individual letter, but where is it defined to do so?
It's a string. You can access characters in a string using square bracket notation.
When you write the line of code swapCase(text) you are passing the variable text into the function swapCase. The letters variable inside the function gets assigned the value of whatever text is.

Categories