Rawan Mansour
Rawan Mansour

Reputation: 111

Pass object to function

I have a group of <span> elements that is created dynamically in a JavaScript function. Each <span> that is created I want to be clickable and call a function that takes the parameter as object on click.

My problem is when I pass the object I get the error undefined for objAddQuestion. How can I pass the object to the function?

 for (var j = 0; j < lstQuestions.length; j++) {
    var objAddQuestion = new Object();
    objAddQuestion = lstQuestions[j];
    html += ' <span onclick="addQuestion(objAddQuestion)" class="badge addQ">' +
        ' Add</span>';
}

and the function:

function addQuestion(obj) {
    //I got error when click
}

lstQuestions is a list of objects.

Upvotes: 0

Views: 185

Answers (4)

Aleksandar Stajic
Aleksandar Stajic

Reputation: 82

If object are global try with:

function addQuestion(obj) {
    window[obj];
}

for local objects try e.g.:

function addQuestion(obj) {
    lstQuestions[obj];
}

and:

for (var j = 0; j < lstQuestions.length; j++) {
    var objAddQuestion = new Object();
    objAddQuestion = lstQuestions[j];
    html += ' <span onclick="addQuestion('+j+')" class="badge addQ">' +
        ' Add</span>';
}

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074295

You've said that lstQuestions is a list of objects. That means you probably don't want to embed the question in an onclick attribute handler.

Instead, I'd suggest using modern event handling, not onclick attributes. For instance, I'd probably do this by including a question identifier on the span and then handling the event with event delegation.

Note: You've tagged your question , but there's no jQuery apparent in your question, so I haven't used it in this first solution. You certainly could, though, and the delegation would be simpler; see under the horizontal line below.

var lstQuestions = [
    {
        id: 1,
        text: "Question 1",
        details: "Details for question 1"
    },
    {
        id: 2,
        text: "Question 2",
        details: "Details for question 2"
    },
    {
        id: 3,
        text: "Question 3",
        details: "Details for question 3"
    }
];
var html = "";
for (var j = 0; j < lstQuestions.length; j++)
{
    // Include the question ID (or if the list never changes, just use `j`) as a data-* attribute
    var question = lstQuestions[j];
    html += ' <span data-question="' + lstQuestions[j].id + '" class="badge addQ">' +
        question.text +
        ' Add</span>';
}
document.getElementById("questions").innerHTML = html;

// Handle clicks on the container
document.getElementById("questions").addEventListener("click", function(e) {
    // Did the click pass through a span.addQ?
    var span = e.target.closest("span.addQ");
    if (span && this.contains(span)) {
        // Yes, get its ID
        var id = +span.getAttribute("data-question");
        // Find the question
        var question = lstQuestions.find(function(q) { return q.id === id; });
        if (question) {
            console.log("Question details: " + question.details);
        }
    }
});
<div id="questions"></div>


Using jQuery, you might go with Rory's approach using jQuery's element data cache. But here's the above using jQuery rather than using the DOM directly:

var lstQuestions = [
    {
        id: 1,
        text: "Question 1",
        details: "Details for question 1"
    },
    {
        id: 2,
        text: "Question 2",
        details: "Details for question 2"
    },
    {
        id: 3,
        text: "Question 3",
        details: "Details for question 3"
    }
];
var html = "";
for (var j = 0; j < lstQuestions.length; j++)
{
    // Include the question ID (or if the list never changes, just use `j`) as a data-* attribute
    var question = lstQuestions[j];
    html += ' <span data-question="' + lstQuestions[j].id + '" class="badge addQ">' +
        question.text +
        ' Add</span>';
}
$("#questions")
    .html(html)
    .on("click", "span.addQ", function() {
        // Yes, get its ID
        var id = +this.getAttribute("data-question");
        // Find the question
        var question = lstQuestions.find(function(q) { return q.id === id; });
        if (question) {
            console.log("Question details: " + question.details);
        }
    });
<div id="questions"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Upvotes: 4

Rory McCrossan
Rory McCrossan

Reputation: 337560

To achieve this you can store the object in jQuery's data cache on each span that you create. Then you can use an unobtrusive delegated event handler to read them back out again when the element is clicked. Try this:

var lstQuestions = [
  { label: 'foo' }, 
  { label: 'bar' }, 
  { label: 'fizz' }, 
  { label: 'buzz' }
];

var spans = lstQuestions.map(function(o) {
  return $('<span />', {
    'class': 'badge',
    'text': o.label
  }).data('question', o);  
})

$('#questions').on('click', '.badge', function() {
  var obj = $(this).data('question');
  console.log(obj);
}).append(spans);
span {
  padding: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="questions"></div>

Another alternative would be to store the index of the related object of the array in the data attribute and then use that to retrieve the item from the array when the element is clicked. Either has advantages, it depends on your use case as to which is more appropriate.

Upvotes: 4

Thum Choon Tat
Thum Choon Tat

Reputation: 3090

You may want to generate the span elements one by one, then append it to your container

var container    = document.getElementById( 'container' );
var lstQuestions = [
    'Question A'
  , 'Question B'
  , 'Question C'
];

for (var j = 0; j < lstQuestions.length; j++)
{
    var objAddQuestion = lstQuestions[j];
    var span           = document.createElement( 'span' );

    span.onclick = generateClickMethod( objAddQuestion );

    span.classList.add( 'badge', 'addQ' );

    span.innerText = ' Add ';
    
    container.appendChild( span );
}

function generateClickMethod( obj ){
  return function(){
    addQuestion( obj );
  };
}

function addQuestion( obj ){
  alert( obj );
}
<div id="container"></div>

Upvotes: 3

Related Questions