Why does console.log returns correctly but my function returns undefined?

I dont get why this function's console.log returns the value, and the typeof correctly, but its return value is invalid to use in another function

I'm attempting to use bubble event to collect the value of the clicked button.

<DIV class = "keys">
        <button class = "key" id = "rock" value = "rock">Rock</button>
        <button class = "key" id = "paper" value = "paper">Paper</button>
        <button class = "key" id = "scissors" value = "scissors">Scissors</button>
</DIV>

Here is the JS:

const bubbleBar = document.querySelector(".keys");
bubbleBar.addEventListener("click", playerPlay);
bubbleBar.addEventListener("click", display);

function playerPlay(e){
            let playerChoice = ""
            console.log(e.target.value); //debugs correctly
            playerChoice = e.target.value;
            console.log(playerChoice, typeof playerChoice); //debugs correctly
            return playerChoice; // apparently returns nothing
        }

function display(){
            console.log(playerPlay) // displays function playerPlay(e), not the result of the first function.
        }

Upvotes: 2

Views: 2860

Answers (2)

Garrett Motzner
Garrett Motzner

Reputation: 3230

A note on scope and return statements

Nested scopes can often be confusing, and where things return to isn't always clear. Here's some examples fo reference:

function simple(){ return 'foo' }

Returns "foo" when simple is called like simple() (or simple("args", "aren't", "used", "but still are passed") or simple.call())


function nested(){
  return function inner(){
    return 'foo'
  }
}

nested, when called, returns a function, but that function does not run immediately. so to get "foo" you would need to call it like this:

let outerResult = nested() // creates and returns a function
outerResult() // returns "foo"

//or more concisely
nested()() // calls both functions in order.

Here we see that return is always going to return to the function it is "closest" too, the inner-most function. return can't skip out of the function it is in.


function loop(){
  let values = [1,2,3]

  for (let value of values){
    return value
  }
}

Returns 1, and the loop does not run another time. So in this case the block does not change the scope of return.


function callbackLoop(){
  let values = [1,2,3]

  values.forEach(value => { return value })
}

This code has a bug. You might expect that this code would work the same as the regular loop, but it doesn't, and in fact, it can't. There is no way for the return in the function value => { return value } to cause the callbackLoop function to return. It can only return itself, same as the nested example. Sometimes functions that take callbacks will pass along the callback return value, but they don't have to if they don't want to. Since forEach is designed for side effects (functions that don't have return values) it doesn't do this, and neither does addEventListener, because it also is designed for side effects.

An example of a function that takes a "pure function" callback, instead of side effects, is Array.prototype.map:

const result = [1, 4, 9, 16].map(x => {return x * 2});
// `map` returns: [2, 8, 18, 32] 
// (it gets the return value of the callback function for 
// each item in the array, and uses that)

A "pure function" is a function that operates on only the inputs (x) in this case, and makes no changes to any variables. Multiplication "creates" a new value, and that is what is returned. The original x is not "mutated" (it is left unchanged). In order for pure functions to be useful, they need to return a value, and that value needs to be used in some way.

Upvotes: 0

Stephen P
Stephen P

Reputation: 14800

As stated in comments, returning a value from an event handler doesn't do anything with that value. The value is used to determine further event processing.

If you want the value you will have to store it somewhere from within the handler.
This code saves the value in a global variable chosenValue and also sets the value as the text within a <span> — what you actually do to store the value will depend on how you plan to make use of the value later.

let chosenValue;
const bubbleBar = document.querySelector(".keys");
bubbleBar.addEventListener("click", playerPlay);
bubbleBar.addEventListener("click", display);

function playerPlay(e) {
  e.preventDefault();
  let playerChoice = ""
  console.log(e.target.value); //debugs correctly
  playerChoice = e.target.value;
  console.log(playerChoice, typeof playerChoice); //debugs correctly
  document.getElementById('playerChoice').innerText = playerChoice;
  chosenValue = playerChoice;
  console.log(`chosenValue variable value is now ${chosenValue}`);
}

function display(e) {
  e.preventDefault();
  console.log(e.target.value);
}
<div class="keys">
  <button class="key" id="rock" value="rock">Rock</button>
  <button class="key" id="paper" value="paper">Paper</button>
  <button class="key" id="scissors" value="scissors">Scissors</button>
</div>
<div>
  Player's Choice: <span id="playerChoice">none</span>
</div>

Upvotes: 2

Related Questions