Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dealing with `return false` when using `eval`in JavaScript

I'm dealing with a situation where I need to bind jQuery events to a page to handle UI updates that is being generated via JSF. Alas, JSF sucks and it sticks onclick events on everything, which pre-empt any of my jQuery work.

Thanks to the fine folks here on SO, I found a solution for that:

mixing my jQuery click events with existing object's onclick attribute

The logic is:

  • on page load, grab all of the onclick attributes and store them in a variable.
  • bind my jQuery events
  • after my own jQuery events, I can then eval the original onclick: eval(onclickValueVariable)

This worked find in all my dummy onclick event testing.

But then it failed on that live JSF page. The issue is that all of the onclick's end with a 'return false' which leads to this error:

return not in function

For example:

<div class="uglyJSFcreatedTag" onclick="console.log('hey');return false">

And the jQuery that would fire it:

var $jsfTag = $('.uglyJSFcreatedTag');
var cacheOnclick = $jsfTag.attr(onclick);
$jsfTag.removeAttr('onclick');

...bunch of logic here to decide if the inline onclick should fire and, if so...

eval(cacheOnclick)

The problem is the return false. It looks like nothing can be returned when firing a eval.

Is there a solution for this? I imagine I could take the value as a string and do string parsing to remove all return false, then call that from the script itself. But that sounds a bit messy. Is there a more elegant solution?

If not, what particular JS statements should I be looking for to filter out before calling eval?

like image 600
DA. Avatar asked Oct 04 '11 04:10

DA.


People also ask

What is a safe alternative to using eval ()?

An alternative to eval is Function() . Just like eval() , Function() takes some expression as a string for execution, except, rather than outputting the result directly, it returns an anonymous function to you that you can call. `Function() is a faster and more secure alternative to eval().

Why we should not use eval in JavaScript?

eval() is a dangerous function, which executes the code it's passed with the privileges of the caller. If you run eval() with a string that could be affected by a malicious party, you may end up running malicious code on the user's machine with the permissions of your webpage / extension.

Why eval is not recommended?

Malicious code : invoking eval can crash a computer. For example: if you use eval server-side and a mischievous user decides to use an infinite loop as their username. Terribly slow : the JavaScript language is designed to use the full gamut of JavaScript types (numbers, functions, objects, etc)… Not just strings!

Does === return false?

Note that === never causes type coercion, but checks for correct types first and yields false if they are not equal!


1 Answers

Two options:

1. Using your current approach, just wrap the onclick string with a function before you eval it. Store the result, and call it as a function. Make sure you call it with the appropriate this context:

var f = eval("(function(){ "+cacheOnclick+"})");
f.call($jsfTag[0]);

Note: the parenthesis () around the function declaration, are required within the eval. This makes the declaration into an expression (syntactically speaking), thus making it legal in in the eval.

2. instead of grabbing the onclick attribute, grab the actual function from the dom element itself. Also, unless you need to do something special with the jsf handler function from your code, I would suggest that you just add the JSF function as a jquery click handler directly, rather than calling it explicitly from your code:

var $jsfTag = $('.uglyJSFcreatedTag');
$jsfTag.bind('click', $jsfTag[0].onclick);
$jsfTag.removeAttr('onclick');

Personally, I would go for approach #2, but either one should work.


Update: Here's a litte additional explanation for Option #2:

var $jsfTag = $('.uglyJSFcreatedTag');

That's from your example -- we're using jquery to retrieve a set containing all elements with the classname 'uglyJSFcreatedTag'.

$jsfTag.bind('click', $jsfTag[0].onclick);

This fetches the first element from the set ($jsfTag[0]), and gets the onclick property of that element object. .onclick is a javascript property that contains a reference to the compiled function that the browser generated from the "onclick" attribute's string content. Now, since we have the handler function, we can bind it directly to the jquery click event, using the jquery bind() function.

$jsfTag.removeAttr('onclick');

Finally, we just remove the attribute. This is ok, because we've already bound the function via jquery. If we don't remove it from the "old-style" onclick, then it'll get called twice.

Note: You may have noticed that the above code only works on the first element in the selected set. For the very likely case that your set contains multiple elements, you'll want to loop through the set and handle each element separately. Here's how you would do that:

var $jsfTag = $('.uglyJSFcreatedTag');
$jsfTag.each( function(idx, element) {
    $(element).bind('click', element.onclick).removeAttr("onclick");
});

If you want to skip the jsf handler for certain elements, then insert logic within the "each loop" to test for this, and skip the call to bind accordingly.


If you must continue to call the jsf handler from within your click handler, then at least consider using element.onclick instead of $(element).attr('onclick'). The latter requires eval, while the former does not.

like image 109
Lee Avatar answered Oct 13 '22 14:10

Lee