Luke
Luke

Reputation: 27

I'm trying to call a JavaScript function but I cannot access it as apparently it is 'undefined'

I'm trying to call a JavaScript function (apiCall) using an event listener binded to a drop-down list listening for a change.

I want the function to re-run when a different menu item is selected from the drop-down list, but when I run this code, the error occurs on the event listener saying that 'apiCall' is not defined, even though it clearly is right above.

Any suggestions?

Thanks in advance.

// API CALL 
var request = new XMLHttpRequest()
request.open('GET', base + forecast + key + city, true)

request.onload = function apiCall() {
  var data = JSON.parse(this.response)
  console.log(data)
  string = JSON.stringify(data, null, 4);
  app.innerHTML = string;
}


document.getElementById("locationList").addEventListener("change", apiCall)


//send request
request.send()

Upvotes: 0

Views: 56

Answers (2)

Nick Parsons
Nick Parsons

Reputation: 50684

The function which you're assigning to window.onload is a function expression, not a function declaration. A named function expression's name isn't added to the surrounding scope, and so, it is only available locally to the function expression itself. This means you won't be able to reference it later on outside the function:

function iAmAFunctionDeclaration() {
  console.log("abc");
}

+function iAmAFunctionExpression() {
  console.log("abc");
}

console.log(typeof iAmAFunctionDeclaration);
console.log(typeof iAmAFunctionExpression); // undefined (not added to the `window`).

Instead, if you can make apiCall a function declaration, you'll be able to access its name to execute it later on, as well as assign it as a callback:

function apiCall() {
  console.log("performing api call");
}

window.onload = apiCall;
document.addEventListener("click", apiCall);

In order to make your API call work correctly, you can either .bind() your request to your apiCall function as shown by @Stratubas or you can replace this keyword with request. This will allow you to handle what this refers to within your callback. For more ways of dealing with this inside of a callback, see this answer and this answer.

You can read more about function expressions here

Upvotes: 2

Stratubas
Stratubas

Reputation: 3067

Writing the function first, then assigning it to request.onload seems to be working:

var request = {};

function apiCall() {
  console.log('hello');
}

request.onload = apiCall;

request.onload();
apiCall();

So your code should be like

// API CALL 
var request = new XMLHttpRequest()
request.open('GET', base + forecast + key + city, true)

function apiCall() { // <- removed 'request.onload' from here...
  var data = JSON.parse(this.response)
  console.log(data)
  string = JSON.stringify(data, null, 4);
  app.innerHTML = string;
}

request.onload = apiCall; // <- and placed it here

const boundApiCall = apiCall.bind(request); // to keep your 'this' object

document.getElementById("locationList").addEventListener("change", boundApiCall)


//send request
request.send()

Edit: I see you're using this inside your function. I'm not 100% sure (still learning here), but I think the extra bind I added will keep your this working as previously.

Upvotes: 1

Related Questions