Firefox Javascript Events Anonymous Function - javascript

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.

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?

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);
}

Use closures like Tom suggested.
Heres a good explanation by John Resig: How Closures Work (pdf)

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.

Related

running code in another function's scope (JavaScript)

So I'm working on a sort of JavaScript framework, just some utility things for myself to use in future projects, and I want to make a data binding system.
The first method I used was objects, and the code would just loop through the specified html element and look for occurences of {{key}} in the markup and then look for that key in the object and replace it that way in the HTML.
For example, if you had <div>{{name}} is a cool guy</div> in the HTML and had {name:"joseph"} in the JS then the final product would be displayed on screen as 'joseph is a cool guy'.
However, I decided later to change my method and instead the framework would except a function. So instead of {name:"joseph"} you would give it function(){ var name = "joseph" }.
This obviously looks better and gives a lot better functionality.
I changed the processing function so instead of looking for the key/value pair to replace the {{key}}, it just uses eval on the variable to gets its value.
My problem lies here: How do I run my search/replace code INSIDE the scope of the function the user passes.
If the user defines variables within that function, their values will not be available anywhere else due to scope issues.
I've tried using Function.toString() to actually modify the source code of the function, but nothing's working and it's all very complicated.
(The issues are not due to the actual solution, I think that Function.toString() might work, but due to my implementation. I keep getting errors)
So... What is the best way to run arbitrary code in the scope of another function?
Critera:
Obviously, I can't modify the function because the user is passing it in. (you can't just tell me to add the search/replace code to the bottom of the function)
The variables must stay in the local scope of the function. (no cheating by using window.name = "joseph" or anything)
I am also aware of how terrible eval is so any suggestions as to get it to work are greatly appreciated. Thanks!
Code:
function process(html) {
var vars = html.match( /({{)[^{}]*(}})/g )
// vars = ['{{variable}}', '{{anotherVariable}}']
var names = vars.map( function(x){ return x.replace("{{", "").replace("}}", "") } )
// names = ['variable', 'anotherVariable]
obj = {}
for (var i = 0; i < names.length; i++) {
obj[names[i]] = eval(names[i])
}
for (var p in obj) {
html = html.replace(new RegExp('{{'+p+'}}','g'), obj[p]);
}
return html
}
You should go back to your first method with the object, it's much better. You can still pass a function, but the function should return an object:
function () {
return { name: 'joseph' }
}

Javascript, passing 'e' to new Function, scope

I have a function block which is dynamic and I need to call with either eval or new Function (preferably the latter). I want to pass in the event it was raised from
function MyFunc(e)
{
new Function("OtherFunc(e, 'abcde')");
}
I can't see how to do this, I have tried a few things such as bind(this), and with(this) but no joy. It's an unusual thing to want to do hence my confusion.
NB I can see it works with eval but new Function would be better if possible and I get the impression it should be, e.g.
How to use scope in JavaScript for Function constructor? (second answer)
Any suggestions? Thanks
(Added: Why I want to do this)
I'm using Kendo mobile buttons. I'm moving from this:
<button onclick="MyFunc(e)"/>
to this
<button data-click="Call" data-func="MyFunc(e)"/>
this is because onclick is not recommended with Kendo UI on iPhones
Don't use the function constructor. Really, really don't. It is eval by another name. Use a function declaration instead. That won't break scope or expect to be built up out of strings.
function MyTest(e) {
function callOtherFunc() {
OtherFunc(e, "abcde");
}
return callOtherFunc;
}
So basically, you have buttons right now with
onclick="MyFunc(e); OtherFunc('a')"
...and you want to change those to
data-click="Call" data-func="MyFunc(e); OtherFunc('a')"
...and you're trying to figure out how to write your Call function without any significant refactoring, continuing to use the strings as you have them now in the onclick.
I'm a bit confused by your use of e within onclick rather than event. As far as I'm aware, there's no e in-scope for onXyz handlers; the event is available as event, though. In the answer below, I've assumed event in onclick but e everywhere else; adjust as necessary.
Within those constraints, eval and new Function are indeed pretty much your only option. It's not more evil than onclick (which is also eval in disguise); eval used with strings you control isn't necessarily evil, it's just usually a last resort (kind of like with).
Based on the documentation, looks like your Call would look something like this:
function Call(e) {
var code = this.element.prop("data-func");
var f = new Function("e", code);
f.call(this, e);
}
That ends up running the code with this being the element that was clicked, and with e in scope to the code in the generated function.
I do not recommend this except perhaps as a temporary measure during proper refactoring, but within the constraints you've given, that's how I see it working. One reason I don't recommend it is that, as with onclick, all of your functions have to be globals (because other than the args you pass it, new Function only has access to globals), and globals are best avoided like the plague.
Live example (with some workaround for the fact I didn't include Kendo):
// Kendo calls the data-click function with this being something
// other than the element; but the element is available as `this.element`
function fakeKendo(e) {
Call.call({element: this}, e);
}
function Call(e) {
// (Using getAttribute instead of Kendo's prop here)
var code = this.element.getAttribute("data-func");
var f = new Function("e", code);
f.call(this, e);
}
function MyFunc(e) {
snippet.log("MyFunc: e.type = " + e.type);
}
function OtherFunc(arg) {
snippet.log("OtherFunc: arg is " + arg);
}
<p onclick="fakeKendo.call(this, event)" data-func="MyFunc(e); OtherFunc('a')">
Click me
</p>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

jQuery Event handlers for Javascript functions

The question title is a bit obscure so let me explain.
A requirement that was only explained to me recently is the use of jQuery in my project. But it has been noted that my functions are fine as is and can be reused as long as they contain some jQuery.
So I'm exploring the world of event listeners for the first time (js side not in the HTML)
A standard Jquery onclick event:
referenceToElement.onclick = function () { alert('here'); };
One thing I notice is that the function doesn't actually have a name. Is there any clean way of doing something like:
referenceToElement.onclick = myOldJavascriptFunction();
function myOldJavascriptFunction()
{
//blahblahblah
}
Is this good practice or is there a better way to do it. Will this even work now that I think of it?
Even if the question is actually worth a downvote, since you could easily answer all those questions by searching, I'll give you a headsup.
That
referenceToElement.onclick = function () { alert('here'); };
is for sure no jQuery standard thing. It's pure Javascript, adding a property to a DOM reference, in this case an anonymous function. However, you basically asked two questions now.
can we give that anonymous function a name ? => YES
can we reference a function which is defined somewhere else ? => YES
To give it a name, we can just create a named function expression like this
referenceToElement.onclick = function myFunctionName() { alert('here'); };
To reference a function, we just pass in it's name
referenceToElement.onclick = myOldJavascriptFunction;
Finally, jQuery's syntax to add the same event listener would look like this:
$( referenceToElement ).click( myOldJavascriptFunction );
Yes, you were very nearly right:
referenceToElement.onclick = myOldJavascriptFunction;
Note the lack of parentheses. This passes a reference to the function to the onclick event. Also note that this is plain old JavaScript, not jQuery. The jQuery way of doing this is more like:
$(referenceToElement).click(myOldJavascriptFunction);
Your first example there is plain and normal javascript, nothing to do with jQuery at all. If you were using jQuery, the line would look like this:
$(referenceToElement).click(function () { ... });
But in any case, it seems like you question is about anonymous functions. You can assign the function to a variable name, or use a function declaration and still reference that function by name:
function myFunction () { ... }
$(referenceToElement).click(myFunction);

MOOTOOLS variable scope

I'm using mootools:
I can't figure out how to use a variable when using an addEvent.
I want to use a for next loop to set values in a loop:
for (x=0;x<num;x++){
var onclickText = 'function (){onclick="addPageMoveEvent('+x+'"); }';
$('pageNum'+x).addEvent('click', onclickText);
}
>
I've search forums but not found any help.
Any help would be great.
Thanks
The addEvent method in MooTools accepts two arguments:
myElement.addEvent(type, fn);
Arguments:
type - (string) The event name to monitor ('click', 'load', etc) without the prefix 'on'.
fn - (function) The function to execute.
It does not take a string and passing a string such as "myFunction()" or "function() { myFunction(); }" will not work.
Since you are inside a loop, and the variable x will share the environment, you need to wrap its value inside another closure. One way is to use an additional closure:
$("pagenum" + x).addEvent("click", (function(value) {
return function() { addPageMoveEvent(value); }
})(x));
See all questions on StackOverflow regarding this particular problem of creating closures within loops.
Also worth checking out is this MDC article - Creating closures in loops: A common mistake
Warning: this first example will not work! Read on for an explanation.
You are confusing onclick HTML syntax with the MooTools addEvent. Try
for (var x=0;x<num;x++){
$('pageNum'+x).addEvent('click', 'addPageMoveEvent('+x+');');
}
This is simpler and cleaner, but might still not do what you want. This code will call the function addPageMoveEvent every time the link is clicked... is that what you want?
Since MooTools doesn't allow the above method, you must use the following:
A programmatically more interesting and less hazardous way to do the same would be:
factory = function (x) { return function() { addPageMoveEvent(x); }; };
for (var x=0;x<num;x++){
$('pageNum'+x).addEvent('click', factory(x));
}
This uses a factory for creating closures that hold your values of x... rather complex code, but it's the purist way. It also avoids using the scary eval that occurs because you feed addEvent a string. (It seems that MooTools doesn't like the other option anyway.)
That a use case for mootools pass method.
for (x=0;x<num;x++){
$('pageNum'+x).addEvent('click', addPageMoveEvent.pass(x));
}
Pass internally creates a closure that holds x in the his scope, so when the click event is fired it has the right value cause its not the same from the for loop.

Regarding JavaScript function unorthodoxy

I've been programming for the Web for quite some time now, but have only just recently discovered a few new intricacies regarding the use of functions and the weird (or so I view them as) things you can do with them. However, they seem at this point only to be syntactically pretty things. I was hoping somebody might enlighten me as to how some of these newly discovered aspects could prove to be useful.
For example, the first time I ran this, I thought for sure it would not work:
<script>
function x(q)
{
q(x);
}
x(function(a)
{
alert(a);
}
);
</script>
But it did! Somehow, creating a named function which receives a different, anonymous function as its only parameter and then runs the function passed to it with itself passed as the parameter to it works just fine. This positively blew my mind and I'm almost certain there's a vast amount of practicality to it, but I just can't quite place it yet.
Ah, and another thing I was elated to discover: using a globally scoped variable to store a function, one can later in the execution use JavaScript's eval() function to modify that variable, thus changing the function's inner workings dynamically. An example:
<script>
var f = function()
{
alert('old text');
}
eval('f = ' + f.toString().replace('old text', 'new text'));
f();
</script>
Sure enough, that code alerts the "new text" string; when I saw that, my mind was once again blown, but also immediately intrigued as to the potential to create something incredible.
So... my burning question for Stack Overflow: how can such seemingly abstract coding principles be used in any positive way?
What you're basically asking is How can I use functions as first-class objects?
The biggest and most common usage is closures (or anonymous functions) for event handling. However, just because you can be clever, it doesn't mean you should. Write clear, readable code just as you would in any other language.
Oh, and flog yourself for typing eval and never think about doing it again
The first one, closures, are very common in javascript. If you want some more advanced examples, here's a nice interactive playground you can mess with: http://ejohn.org/apps/learn/.
Here's my window.onload function I use when whatever I'm working on doesn't require a full blown library.
//add events to occur on page load
window.addOnload = function(fn) {
if (window.onload) {
var old = window.onload;
window.onload = function() {
old();
fn();
}
} else {
window.onload = fn;
}
}
Then whenever I need something to happen onload, I can just use an anonymous function. Here's an example from a recent maintenance project of mine.
//make all menu items have a hover property
window.addOnload(function(){
var cells = document.getElementsByTagName('td');
for (var i=0; i < cells.length; i++) {
if (cells[i].className != 'NavMenuItem') continue;
(function(cell){
cell.onmouseover = function() {
cell.className = 'NavMenuItemHighlight';
}
cell.onmouseout = function() {
cell.className = 'NavMenuItem';
}
})(cells[i])
}
});
As for your second 'discovery', just pretend you never found out about it.
Well, the first one is typically how you prove that the Halting Problem is undecidable...
Whether or not you consider that "useful" is entirely up to you, I guess B-)

Categories