user3128057
user3128057

Reputation: 1

alternative to eval() in DOM manipulation

I am converting tutorials for students (2nd language speakers, 9 to 12 yrs old) to access in an offline / intranet context. Hence the websites I would like them to use are unavailable.

I am trying to mimic the 'alter the code' style of tutorials for helping with JavaScript / HTML5 Canvas.

This works :

<canvas id="myCanvas" height="400px" width="400px"></canvas>
<script>
function update(){
   eval(document.getElementById('demoScript').value);
}

var ctx = document.getElementById("myCanvas").getContext("2d");

</script>
<textarea id="demoScript">
ctx.fillRect(100,100,50,50);
</textarea>
<input type="button" value="update" onClick="update()">

... but everything I have read says eval() is a bad idea.

I can get the textarea content to pop-up in a div if I want, but I can't get it to pop-up in a script anywhere ... leaving me with just eval().

Options and recommendations for alternatives please ... or this is as good as it gets ?

Upvotes: 0

Views: 425

Answers (5)

adeneo
adeneo

Reputation: 318332

You can get the value string from the textarea, split, validate and run it manually.
For a demonstration like this, where ctx is given, something like this should work

var ctx = document.getElementById("myCanvas").getContext("2d");

function update(){
    var val = document.getElementById('demoScript').value,
        fn  = val.match(/\.(.*?)\(/),
        arg = val.match(/\((.*?)\)/);

    if (fn && fn.length > 0) {
        if (arg && arg.length > 0) {
            var args = arg[1].indexOf(',') != -1 ? arg[1].split(',') : [arg[1]];
            ctx[fn[1]].apply(ctx, args);
        }else{
            ctx[fn[1]]();
        }
    }
}

FIDDLE

Upvotes: 0

cookie monster
cookie monster

Reputation: 10982

Options and recommendations for alternatives please ... or this is as good as it gets ?

I'd suggest using the Function constructor instead of eval. While in your simple example it may not make much difference, in other cases it may.

This will make the code evaluate in the global scope, so none of your local variables can be touched. It also allows JS engines to more easily optimize the local code. Using eval() can disable optimizations.


So to use the Function constructor, just pass the code to eval as the last argument. Since you have no parameters to define for the new function, it'll be the only argument.

var f = new Function("return "  + document.getElementById("demoScript").value);

Then invoke the function.

f();

Notice that I concatenated a return statement into the provided code. This isn't required if you don't care about getting the returned value from the code your invoking, and should be removed if it might interfere with the provided code.


And of course, you can do this all in one line if you're only going to invoke it once.

new Function(document.getElementById("demoScript").value)();

Upvotes: 1

dkellner
dkellner

Reputation: 9966

First of all, it's not a "bad idea" to use eval. Second: anything that replaces eval will have the same "disadvantages" since it executes code. You'll have to execute code to do this. If you don't want to make your own interpreter (which is at least ten times worse and more vulnerable) you'll have to stick with eval or something similar.

Now what is the danger of it? Nothing else but the fact that it executes code. It's like telling someone that a hammer is dangerous because it hits hard - YES, and it's necessary when it gets to nailing something. Of course, a hammer can kill.

So,

  1. Use eval,
  2. ...but sanitize the code it gets (= watch out for dangerous expressions, etc).

You can limit a lot of things for the user, like only one instruction per line, only double quotes, etc, to make it more controllable. Anything that's off the limit will be deleted. If no dangerous thing can be pushed thru the input, eval is harmless.

Upvotes: 1

Niet the Dark Absol
Niet the Dark Absol

Reputation: 324790

This is an acceptable use for eval, because at worst a student will lock up their own browser with an infinite loop.

Upvotes: 1

maček
maček

Reputation: 77806

You could do

var fn = document.getElementById("demoScript").value;
window[fn]();

Upvotes: -1

Related Questions