views:

1213

answers:

6

I'm attempting to register an anonymous function when a user clicks a cell in an HTML table. Here's some of the raw, unadulterated code:

document.getElementById(
    "course"+displayed_year_index+occurrences_indices[displayed_year_index]).onclick =
        eval("function() {PrintReceipt("+result.years[result_year_index].rul_code+");};");

Note the use of eval, since this sits in a loop and the anonymous function is different each time round.

Suffice to say, this works absolutely fine in Firefox 2. But, Firefox 3 throws a 'Syntax Error', pointing inside the brackets after the word 'function'.

Does anybody have any smart ideas on how I can fix this?


Just to make it crystal clear what I'm attempting to do, here's a much simplified example:

for (index=0; index<4; index++) {
    document.getElementById("div"+index).onclick = 
        eval("function () {Foo(index);};");
}

In other words, I wish to trigger the same function with a different parameter value for each div.

+5  A: 

Have you tried something like this?

document.getElementById('course' + displayed_year_index + occurences_indices[displayed_year_index]) =
    function (nr)
    {
        return function () { PrintReceipt(nr) }
    } (result.years[result_year_index].rul_code);

Can you please post the loop to help us find the problem instead of making us guess what you're trying to do?

Tom
Good answer, but a link to a good explanation of closures would help so much. I understand your code but it still scares me ;) Someone who doesn't know what a closure is wouldn't have a chance. Unfortunately I haven't yet seen a clear, concise explanation of closures anywhere online :(
Gareth
Unfortunately, I need to assign to the onclick event, and I can't pass a parameter to the anonymous function because of this.
Jonathan Swift
A: 

It seems like this is the direction that you would want to go:

document.getElementById("course"+displayed_year_index+occurrences_indices[displayed_year_index]).addeventlistener("click",  function() {
    var current_rul_code = result.years[result_year_index].rul_code;
    PrintReceipt(current_rul_code);
}, true);

This should cause each to onclick event to be created within a different scope (each iteration of the loop). Closures will take care of the rest.

ng.mangine
This doesn't work, because the parameter to PrintReceipt needs to be a constant when called by the onclick.
Jonathan Swift
It should not need to be a constant, because the function will be called within it's original scope when the event fires.
ng.mangine
Thanks, I've tried your example as stated, but it doesn't work. This is because result.years[result_year_index].rul_code is not in scope when the function is called from the onClick. What I actually need is the value of the variable (13208 in this case) to be passed to the function.
Jonathan Swift
+1  A: 

Use closures like Tom suggested.

Heres a good explanation by John Resig: How Closures Work (pdf)

orip
But it works in Firefox 2 as expected.
Jonathan Swift
A: 

I found Javascript Closures article quite clear. Maybe it can help.

PhiLho
A: 

Thanks Tom, your code works a treat. All I have to do now is work out why it works, as the syntax is somewhat unfamiliar.

Jonathan Swift
+3  A: 

IMHO closures should not be used in this case and there is no need to create a new function for each onlick (uses much more memory than necessary) and eval is the wrong answer.

You know that the element you are getting with getElementById is an object and that you can assign values to it?

for ( /* your definition */ ) {
  var e = document.getElementById(
    "course"+displayed_year_index+occurrences_indices[displayed_year_index]
  );
  e.rul_code = result.years[result_year_index].rul_code;
  e.onclick = PrintReceipt;
}

But you should define the PrintReceipt first:

function PrintReceipt() {
  //This function is called as an onclick handler, and "this" is a reference to the element that was clicked.
  if (this.rul_code === undefined) { return; }
  //Do what you want with this.rul_code
  alert (this.rul_code);
}
some
You are of course completely right. Never use a sledgehammer to crack a nut eh?
Jonathan Swift
Thanks for this. I've been pulling my hair out trying to figure the answer!
Matt Ellen