0

I am currently trying to create an onclick event for a button that calls a separate function and passes uses an object as the parameter for this function.

My code can be seen below:

async function getJourneyAwait(){
  const routes = await getJourney();
  var innerHTML = " "; 
  if(!(routes === null)) {
    for (var i = 0; i < routes.length; i++){
        console.log(routes[i])
        console.log(typeof(routes[i]))
        var route = routes[i]
        innerHTML += '<p> Route ' + i+1 + ': <button onClick=startJourney(' + route + ')>Start Trip</button></p>'
    }
    document.getElementById('tripmessage').innerHTML = innerHTML;
  }
}


function startJourney(route){

    console.log(route);
}

When I try to click the Start Trip button I get an error stating: Uncaught SyntaxError: Unexpected end of input at .(index):1

When I inspect the button element it there seems to be some kind of error with the parameter as the element is as follows:

   
<button onclick="startJourney([object" object])="">Start Trip</button>

I have tried multiple different ways and in some cases I have been able to get the function to run but when I do all that is logged to the console is undefined. For example, if I remove the plus signs and quotes on either side of 'route' the function runs but undefined is logged to the console.

Ronan Byrne
  • 107
  • 1
  • 7
  • Without seeing the content of `routes` it's hard to say how to make the exact fix. But as the created HTML shows, `route[i]` is an object (or an array), and you've to fetch the string deeper from the data structure. Notice also, that the quoting is off in the attribute, unless the data string (`route`) is meant to be a variable name. – Teemu Jul 30 '20 at 10:24
  • Sorry, my bad. routes looks like: `[{Line: "39A", Departure Stop: 404, Arrival Stop: 767, Route ID: "39A_43"}]` while routes[ i ] looks like: `{Line: "39A", Departure Stop: 404, Arrival Stop: 767, Route ID: "39A_43"}` – Ronan Byrne Jul 30 '20 at 10:27
  • 1
    And you want to pass an object from an inline listener ... You've to copy a refrence to `routes` array to the global scope (`window.routes = routes`), and then create the listener like this: `... onClick="startJourney(routes[' + i + ']);" ...`. Or rather do as nip has answered, [inline listeners are problematic](https://stackoverflow.com/a/63119431/1169519). – Teemu Jul 30 '20 at 10:35

2 Answers2

1

The [object object] that you are seeing is the result of the conversion of your route object to a string format.

I suggest one of two approaches:

  1. Create the button element using createElement, assign the startJourney function to the onclick property using button.onclick = function() { createJourney(route) ]} and append it to the parent element.
  2. Add an id to the button element and add a click event listener using addEventListener("click", createJourney(route))

Edit: As pointed out by @Teemu, in case you use option #1, you'll have to replace var i = 0 by let i = 0 since in a loop with a let-based index, each iteration through the loop will have a new variable i with loop scope.

nip
  • 1,065
  • 8
  • 19
  • These will pass the last route to all the buttons. – Teemu Jul 30 '20 at 10:41
  • @Teemu How so ? The code is to be placed inside the for loop where he's iterating over all routes `var route = routes[i]` – nip Jul 30 '20 at 10:52
  • Exactly, and because `route = routes[i]`, at the end of the loop `route` is referring to the latest defined `route` only. See http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – Teemu Jul 30 '20 at 10:53
  • @Teemu im not following. i've implemented the 1st solution here https://jsfiddle.net/3fL640qu/ – nip Jul 30 '20 at 11:00
  • Also, I'd rather go with option #2 – nip Jul 30 '20 at 11:00
  • 1
    You're not using `var` in your fiddle snippet, the difference between `var` and `let`/`const` is crucial. – Teemu Jul 30 '20 at 11:02
  • @Teemu you're right! not used to see var anymore! well spotted, will add a note – nip Jul 30 '20 at 11:03
0

You should I would recommend you use Data attributes and also bind your buttons this way:

function getJourneyAwait(){
  const routes = getJourney();
  var innerHTML = " "; 
  if(routes) {
    for (var i = 0; i < routes.length; i++){
        var route = routes[i]
        innerHTML += '<p> Route ' + route + ': <button onclick="startJourney(this)" data-route="' + route + '">Start Trip</button></p>'
    }
    document.getElementById('tripmessage').innerHTML = innerHTML;
  }
}

function getJourney(){
  return [1, 2, 4, 6]
}


function startJourney(element){
    console.log(element.dataset.route);
}
<div id="tripmessage"></div>
<button onclick="getJourneyAwait()">GetJourney</button>
Njuguna Mureithi
  • 2,283
  • 1
  • 13
  • 32
  • This suffers from the same flaw as OP's code (when run with OP's data), it doesn't matter, in which attribute you're including the object, the value of all `data-route` attributes will be `[object Object]`. – Teemu Jul 30 '20 at 11:10
  • Data attributes would solve that. And I mean by setting the data required to be passed. OP should have state management for data. – Njuguna Mureithi Jul 30 '20 at 11:38
  • When the content is `[object Object]`, I can't see `data-*` solving anything here ... – Teemu Jul 30 '20 at 11:39
  • ` data-* attributes allow us to store extra information on standard, semantic HTML elements. ` I wasn't trying to manage the state for OP. I agree my answer was a little abstract. – Njuguna Mureithi Jul 30 '20 at 11:43