How to execute string containing function name AND parameters? - javascript

In javascript, I have a string like this:
"doSomething('param1','param2')"
And I want to execute it. I am aware that I could normally use
window['doSomething']('param1', 'param2');
But that only works if my function name is separate from the arguments. In my case they are already combined. I think I can use eval() but the consensus seems to be that it should be avoided. Is there another way?
EDIT:
To answer the request for more info: I am using this string like this:
<a id="YesButton" onclick="closeModalView_Yes("doSomething('param1','param2')")">
Where closeModalView_Yes will close the modal yes/no window and then execute the given function, although at times I may pass it doSomethingElse(param1) which only takes one parameter.

Use eval, just like:
eval( "console.log( 'hey', 'here I am' )" );
However eval is pretty dangerous and it's not recommended.
If you can (still we don't have much info about your case), render your JavaScript between <script> tags in your template, making it a "regular code", much easier to debug.
Also a good practice is to pass data (i.e. with JSON) rather than code. Try rethinking your logic or provide additional information.

<a id="YesButton" onclick="closeModalView_Yes("doSomething('param1','param2')")">
You really shouldn't pass that as a string, but as a function:
closeModalView_Yes(function(){ doSomething('param1','param2'); });
together with
function closeModalView_Yes(callback) {
// do whatever needs to be done to close the window
// and after that
callback();
}
Btw, with your current approach the HTML is not even valid, it would need to be
<a id="YesButton" onclick="closeModalView_Yes("doSomething('param1','param2')")">
<!-- ^^^^^^ ^^^^^^ -->
You could've avoided that by registering the event via javascript instead of inline attributes.

Related

Is there a name for the programming paradigm jQuery suggests? [duplicate]

I have heard many times that using JavaScript events, such as onClick(), in HTML is a bad practice, because it's not good for semantics. I would like to know what the downsides are and how to fix the following code?
link
You're probably talking about unobtrusive Javascript, which would look like this:
link
with the logic in a central javascript file looking something like this:
$('#someLink').click(function(){
popup('/map/', 300, 300, 'map');
return false;
});
The advantages are
behaviour (Javascript) is separated from presentation (HTML)
no mixing of languages
you're using a javascript framework like jQuery that can handle most cross-browser issues for you
You can add behaviour to a lot of HTML elements at once without code duplication
If you are using jQuery then:
HTML:
<a id="openMap" href="/map/">link</a>
JS:
$(document).ready(function() {
$("#openMap").click(function(){
popup('/map/', 300, 300, 'map');
return false;
});
});
This has the benefit of still working without JS, or if the user middle clicks the link.
It also means that I could handle generic popups by rewriting again to:
HTML:
<a class="popup" href="/map/">link</a>
JS:
$(document).ready(function() {
$(".popup").click(function(){
popup($(this).attr("href"), 300, 300, 'map');
return false;
});
});
This would let you add a popup to any link by just giving it the popup class.
This idea could be extended even further like so:
HTML:
<a class="popup" data-width="300" data-height="300" href="/map/">link</a>
JS:
$(document).ready(function() {
$(".popup").click(function(){
popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
return false;
});
});
I can now use the same bit of code for lots of popups on my whole site without having to write loads of onclick stuff! Yay for reusability!
It also means that if later on I decide that popups are bad practice, (which they are!) and that I want to replace them with a lightbox style modal window, I can change:
popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
to
myAmazingModalWindow($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
and all my popups on my whole site are now working totally differently. I could even do feature detection to decide what to do on a popup, or store a users preference to allow them or not. With the inline onclick, this requires a huge copy and pasting effort.
It's not good for several reasons:
it mixes code and markup
code written this way goes through eval
and runs in the global scope
The simplest thing would be to add a name attribute to your <a> element, then you could do:
document.myelement.onclick = function() {
window.popup('/map/', 300, 300, 'map');
return false;
};
although modern best practise would be to use an id instead of a name, and use addEventListener() instead of using onclick since that allows you to bind multiple functions to a single event.
With very large JavaScript applications, programmers are using more encapsulation of code to avoid polluting the global scope. And to make a function available to the onClick action in an HTML element, it has to be in the global scope.
You may have seen JS files that look like this...
(function(){
...[some code]
}());
These are Immediately Invoked Function Expressions (IIFEs) and any function declared within them will only exist within their internal scope.
If you declare function doSomething(){} within an IIFE, then make doSomething() an element's onClick action in your HTML page, you'll get an error.
If, on the other hand, you create an eventListener for that element within that IIFE and call doSomething() when the listener detects a click event, you're good because the listener and doSomething() share the IIFE's scope.
For little web apps with a minimal amount of code, it doesn't matter. But if you aspire to write large, maintainable codebases, onclick="" is a habit that you should work to avoid.
Revision
Unobtrusive JavaScript approach was good in the PAST - especially events handler bind in HTML was considered as bad practice (mainly because onclick events run in the global scope and may cause unexpected error what was mention by YiddishNinja)
However...
Currently it seems that this approach is a little outdated and needs some update. If someone want to be professional frontend developper and write large and complicated apps then he need to use frameworks like Angular, Vue.js, etc... However that frameworks usually use (or allow to use) HTML-templates where event handlers are bind in html-template code directly and this is very handy, clear and effective - e.g. in angular template usually people write:
<button (click)="someAction()">Click Me</button>
In raw js/html the equivalent of this will be
<button onclick="someAction()">Click Me</button>
The difference is that in raw js onclick event is run in the global scope - but the frameworks provide encapsulation.
So where is the problem?
The problem is when novice programmer who always heard that html-onclick is bad and who always use btn.addEventListener("onclick", ... ) wants to use some framework with templates (addEventListener also have drawbacks - if we update DOM in dynamic way using innerHTML= (which is pretty fast) - then we loose events handlers bind in that way). Then he will face something like bad-habits or wrong-approach to framework usage - and he will use framework in very bad way - because he will focus mainly on js-part and no on template-part (and produce unclear and hard to maintain code). To change this habits he will loose a lot of time (and probably he will need some luck and teacher).
So in my opinion, based on experience with my students, better would be for them if they use html-handlers-bind at the beginning. As I say it is true that handlers are call in global scope but a this stage students usually create small applications which are easy to control. To write bigger applications they choose some frameworks.
So what to do?
We can UPDATE the Unobtrusive JavaScript approach and allow bind event handlers (eventually with simple parameters) in html (but only bind handler - not put logic into onclick like in OP quesiton). So in my opinion in raw js/html this should be allowed
<button onclick="someAction(3)">Click Me</button>
or
function popup(num,str,event) {
let re=new RegExp(str);
// ...
event.preventDefault();
console.log("link was clicked");
}
link
But below examples should NOT be allowed
<button onclick="console.log('xx'); someAction(); return true">Click Me</button>
link
The reality changes, our point of view should too
There are a few reasons:
I find it aids maintenence to separate markup, i.e. the HTML and client-side scripts. For example, jQuery makes it easy to add event handlers programatically.
The example you give would be broken in any user agent that doesn't support javascript, or has javascript turned off. The concept of progressive enhancement would encourage a simple hyperlink to /map/ for user agents without javascript, then adding a click handler prgramatically for user agents that support javascript.
For example:
Markup:
<a id="example" href="/map/">link</a>
Javascript:
$(document).ready(function(){
$("#example").click(function(){
popup('/map/', 300, 300, 'map');
return false;
});
})
It's a new paradigm called "Unobtrusive JavaScript". The current "web standard" says to separate functionality and presentation.
It's not really a "bad practice", it's just that most new standards want you to use event listeners instead of in-lining JavaScript.
Also, this may just be a personal thing, but I think it's much easier to read when you use event listeners, especially if you have more than 1 JavaScript statement you want to run.
Your question will trigger discussion I suppose. The general idea is that it's good to separate behavior and structure. Furthermore, afaik, an inline click handler has to be evalled to 'become' a real javascript function. And it's pretty old fashioned, allbeit that that's a pretty shaky argument. Ah, well, read some about it #quirksmode.org
onclick events run in the global scope and may cause unexpected
error.
Adding onclick events to many DOM elements will slow down the
performance and efficiency.
Two more reasons not to use inline handlers:
They can require tedious quote escaping issues
Given an arbitrary string, if you want to be able to construct an inline handler that calls a function with that string, for the general solution, you'll have to escape the attribute delimiters (with the associated HTML entity), and you'll have to escape the delimiter used for the string inside the attribute, like the following:
const str = prompt('What string to display on click?', 'foo\'"bar');
const escapedStr = str
// since the attribute value is going to be using " delimiters,
// replace "s with their corresponding HTML entity:
.replace(/"/g, '"')
// since the string literal inside the attribute is going to delimited with 's,
// escape 's:
.replace(/'/g, "\\'");
document.body.insertAdjacentHTML(
'beforeend',
'<button onclick="alert(\'' + escapedStr + '\')">click</button>'
);
That's incredibly ugly. From the above example, if you didn't replace the 's, a SyntaxError would result, because alert('foo'"bar') is not valid syntax. If you didn't replace the "s, then the browser would interpret it as an end to the onclick attribute (delimited with "s above), which would also be incorrect.
If one habitually uses inline handlers, one would have to make sure to remember do something similar to the above (and do it right) every time, which is tedious and hard to understand at a glance. Better to avoid inline handlers entirely so that the arbitrary string can be used in a simple closure:
const str = prompt('What string to display on click?', 'foo\'"bar');
const button = document.body.appendChild(document.createElement('button'));
button.textContent = 'click';
button.onclick = () => alert(str);
Isn't that so much nicer?
The scope chain of an inline handler is extremely peculiar
What do you think the following code will log?
let disabled = true;
<form>
<button onclick="console.log(disabled);">click</button>
</form>
Try it, run the snippet. It's probably not what you were expecting. Why does it produce what it does? Because inline handlers run inside with blocks. The above code is inside three with blocks: one for the document, one for the <form>, and one for the <button>:
let disabled = true;
<form>
<button onclick="console.log(disabled);">click</button>
</form>
Since disabled is a property of the button, referencing disabled inside the inline handler refers to the button's property, not the outer disabled variable. This is quite counter-intuitive. with has many problems: it can be the source of confusing bugs and significantly slows down code. It isn't even permitted at all in strict mode. But with inline handlers, you're forced to run the code through withs - and not just through one with, but through multiple nested withs. It's crazy.
with should never be used in code. Because inline handlers implicitly require with along with all its confusing behavior, inline handlers should be avoided as well.

Eval() function alternative

I have a json file where i am rendering the template and have an object like
"templateLabelEval":"return $row->['document_code'];"
I am rendering the label by using
eval(templateLabelEval);
Is there any other alternative way where i could avoid using eval as it is considered to be a bad practice
Given that the code string has a return statement, eval alone wouldn't work anyway, but you could use Function().
var result = Function(data.templateLabelEval)();
This has nearly all the same security concerns, but not so much the performance issues that eval has (or had). So of course, you should only execute code that is secure.
To explain the code, passing the string to the Function constructor creates a new function object with that strings as its body. (I assume the function needs no parameters defined for now.) So the trailing () invokes the function immediately and the result is stored in result.
You could store the function itself if you want, and then invoke it later as many times as you'd like.

How do I avoid using eval on scripts from JSON?

My web app (running jQuery) gets the following JSON response from the server:
{
"script_0": "$('#objview_0_306').on('click', function(e){ getObjectResponse(96089, '306', 'display'); e.preventDefault();});",
"verb_0": "display",
"obid_0": "0_306",
"body_0": "<div id=\"objview_0_306\"></div>",
"icon_0": "icons/caseman16.png",
"close_0": "previousTab();",
"title_0": "Cases",
"cmdCount": "1"
}
The script_0 element contains scripts that must be run, in this case adding an onClick event to a DOM element.
At the moment, I use eval() to run the scripts, since they are returned as strings. Is there a way to avoid using eval()?
Noop. The eval (and alias Function constructor etc.) is designed to do exactly this, parse strings into runnable javascript.
If you don't want to use eval, instead of return the complete javascript from the server, you can return values in json response that instruct your client app to do predefined tasks.
I'm going to plagiarize the comment made by #Esailija:
Why on earth would you have code in json? it's a data-interchange format...
I do not think that you can achieve what you want without eval or one of its derivates.
You could give a look to jQuery.globalEval() method, as described here: http://api.jquery.com/jQuery.globalEval/
And you could also give a look to the jQuery.getScript() method: http://api.jquery.com/jQuery.getScript/
I'll be the first to spout the usual eval == evil mantra, but im also a realist, and there are times when rules can be broken.
I'd say you might have one of them here.
You can try to append that string with code to DOM and then trigger the click event on needed element.

Javascript security question / Using eval()

I'm seeing code in the following form - is such use of eval() safe?
function genericTakeAction(frm_name,id,pagenum,action)
{
var rset=eval("document."+frm_name);
var x=eval("document."+frm_name+".edit_key");
var y=eval("document."+frm_name+".cAction")
if(x)
x.value=id;
if(y)
y.value=action;
page_list(pagenum);
}
Its used as:
<a href="javaScript:;" onClick="genericTakeAction('frmSearch',
'<?php echo $rec_id;?>','<?php echo $pagenum?>','makeOpen')"
class='link6'>Make Open</a>
Whether it's right or wrong, it's needlessly complicated.
function genericTakeAction(frm_name,id,pagenum,action)
{
var rset = document[frm_name];
var x = rset.edit_key;
var y = rset.cAction;
if(x)
x.value=id;
if(y)
y.value=action;
page_list(pagenum);
}
This works because in JavaScript, you can access an object's properties in one of two ways: Either using dotted syntax and a literal identifier, e.g. x = obj.foo;, or using bracket syntax and a string identifier, e.g. x = obj["foo"];. (Note how foo was not in quotes in the first one, but was in quotes for the second; but both do exactly the same thing. Also note that since the property name is a string in the second case, you can use any expression that results in a string, so y = "f"; x = obj[y + "oo"]; also works.)
P.S. It's wrong
eval() is generally frowned upon because, as you are already aware, it is considered unsafe.
In the browser environment, however, it is less of an issue, because in fact, any user could eval() any code they wanted to, using tools like Firebug, etc.
There is still an issue, in that the eval() embedded in the code can be run without the user knowing that he was triggering an eval(), but it's still much less of an issue than in a server-side environment like PHP.
eval() is actually typically used as you've shown to run JSON code being returned from a server-side request. Newer browsers can import JSON more safely using a dedicated JSON parse() function, but older browsers do not have this function and are forced to use eval() for this. Most JSON libraries have eval() in their code somewhere for this reason, but will generally do some sanitisation of the input before running it through eval().
Even if it might look a little bit convoluted, as others have already mentioned, from a pure security perspective, you have to make sure that the 'frm_name' parameter of the genericTakeAction() function can never contain user-supplied data.
In your example, the 'frm_name' parameter contains the hard-coded literal 'frmSearch'. So it is ok as long as this genericTakeAction() function does not get called somewhere else with user-supplied data for the 'frm_name' parameter.
See http://en.wikipedia.org/wiki/Cross-site_scripting#Traditional_versus_DOM-based_vulnerabilities

Why is dynamically modifying a JavaScript function's code mid-execution a bad thing?

A few days ago, I asked a question regarding dynamically modifying a function's code midway through the outerlying script's execution and I was told to completely forget ever coming upon the notion. I'm not sure I understand why that is. Let me give an example:
<script>
var display = function(msg)
{
alert(msg);
}
// Now, at the moment, the display() function
// is receiving a single parameter and alerting
// it to the user. I'm now going to use eval()
// to modify the display() function.
eval('display = ' + display.toString().replace('alert(', 'document.write('));
// Now, the display() function writes its parameter
// to the document as opposed to alerting it.
</script>
I realize this is a rather trivial example, but there must surely be some use that can be derived from being able to dynamically modify a function, something so useful by itself.
Although this may do what you need it to do, 6 months from now you (or the person maintaining your code) will be going "WTF?!"
If your use case is to alert or write based on some condition, why don't you write two different functions? Or have your function take another parameter that decides the output mode. Or pass in a function as a parameter that performs the actual output. Something, you know, a little more on the sane side. ;-)
There are cases where it could be useful to change a function's behavior, but there are better ways to do it. In your example, you could create new instances of the function that handle the output differently, by passing a function as an argument (similar to the strategy pattern):
function makeDisplay(displayStrategy) {
return function(msg) {
// I'm assuming you would do some additional processing here...
displayStrategy(msg);
}
}
var display = makeDisplay(alert);
// now modify display to use document.write
display = makeDisplay(function(msg) { document.write(msg); });
Well, using eval might be a security concern but modifying a function in real-time is ok. How else you can make memoization anyway?
Although, come to think of it, changing method signature isn't a great idea, other people won't know how to call this function after this, since it would depend on execution order and it's not easy to track usually.
I have found myself needing to do this in situations where I don't have the source code for a particular piece of vendor javascript; so that could be a legitimate use case. I agree that if you have another option, it's better to do it in a more organised way, editing the original function to be more flexible.

Categories