I need to write a function where I can pass it a string and it will replace the word "prefix" in it with the contents of that string. So every time I call the function and pass it a new string i.e. alpha, beta, etc it will completely rebuild all the layers and events inside, using that key as the prefix. I suspect I need to use eval, but I'm not really sure how in this case.
layoutViews = (prefix) ->
prefix_layer1 = new Layer
width: 100
height: 100
prefix_layer1.on Events.Click ->
buttonActions()
layoutViews(alpha)
layoutViews(beta)
I'm using CoffeeScript, but any ideas in real JS are also welcome. I realize this specific question hints at my doing something else wrong in the project, but it's mostly for my own curiosity whether this is even possible.
So you want
function build(prefix) {
window[prefix + "_hi"] = function() {
alert("hi");
};
}
Use like this:
build("test");
test_hi();
Window is just the global (! Bad) element in browsers, so you might consider to assign to this instead of window depends on your usage.
Related
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 ?
This is an acceptable use for eval, because at worst a student will lock up their own browser with an infinite loop.
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,
Use eval,
...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.
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)();
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
You could do
var fn = document.getElementById("demoScript").value;
window[fn]();
This question is in reference to this jQuery limit plugin. Here's the source:
(function($){
$.fn.extend({
limit: function(limit,element) {
var interval, f;
var self = $(this);
$(this).focus(function(){
interval = window.setInterval(substring,100);
});
$(this).blur(function(){
clearInterval(interval);
substring();
});
substringFunction = "function substring(){ var val = $(self).val();var length = val.length;if(length > limit){$(self).val($(self).val().substring(0,limit));}";
if(typeof element != 'undefined')
substringFunction += "if($(element).html() != limit-length){$(element).html((limit-length<=0)?'0':limit-length);}"
substringFunction += "}";
eval(substringFunction);
substring();
}
});
})(jQuery);
Now I may just be nitpicking here... but maybe I'm missing something. Here are my questions/concerns:
1) What is the purpose of creating the substring function in a string and then eval'ing it? Looking through, it seems like the extension would work perfectly fine if the function was initialized normally.
2) I don't like that it uses a setInterval to execute the substring function. Wouldn't a keypress or similar event be the better and more logical way to do this? Also, I believe this to be the cause of (or at least enabling) the 'flickering text' bug that is referenced in the v1.2 change log (No, it isn't fixed).
3) Why is the variable f initialized? It is never used or referenced.
4) Also, this isn't a chainable method, and as a jQuery extension, it should be. I'm not too familiar with writing jQuery extensions, but this can be accomplished by return this; at the end of the method, correct?
It seems like this is just a case of poor programming, but I'd like to get an outside opinion.
1) I agree. Looks like he doesn't understand closures to me.
2) It's hard to predict exactly which events might change the contents of the textbox. Keypresses are obvious, but maybe mouse events could also do it. It could also be updated by other Javascript functions.
3) I have a couple of guesses: a) He was using it to hold the callback function when he was trying to get the closure to work, and didn't remove the declaration when he switched to the eval kludge; b) it was supposed to hold the substring function string, but he made a mistake and called it substringFunction when he assigned it (notice that he forgot the var declaration there).
4) True.
Just because someone posts their code to a web site doesn't mean they're an expert.
jQuery currently uses window as its default element so any call like $('div') will look for div tags inside window.
Is there any way to change defaults on jQuery like:
$.defaultRoot = $('.anyOtherRootElement');
$('div').text("Hello");
this will select any div inside the elements containing .anyOtherRootElement class.
Thanks in advance
Upate
just an update refining the question a bit more here:
I would like to perform the actions above based on external queries coming from external script which won't know what defaultRoot is so they can still be calling what is supposed to be the current base, so in this instance, I'm afraid adding the a second parameter wouldn't be an option, unfortunately.
And at the same time creating a function which returns defaultRoot.find(el) would prevent me of using first-level methods such $.trim, $.each, etc… so unfortunately that would not be possible as well.
Ideally (for performance reasons) you'd want to use find()
$.defaultRoot.find("div");
Otherwise you can use the 2 argument form that sets a context
$("div", $.defaultRoot);
In general you don't want to do these types of things implicitly since someone else could easily end up thoroughly confused when having to work with your code later. If you want to do it consistently and make it shorter you should create your own function to do so like:
var $s = function(selector) {
return $.defaultRoot.find(selector);
}
and then you'd just be able to use
$s("div")
or you could also do a scoped higher order function with something like
var withScope = function(scope$) {
return function(selector) {
return scope$.find(selector);
}
}
var $s = withScope($.defaultRoot);
$s("div")
If for some reason you really want to screw around with the default state for client code (begging for chaos IMO), you should look at the functional practice: currying.
$('SELECTOR', 'CONTEXT')
You can use context. As in your case $('div', '.anyOtherRootElement')
For more details, visit http://api.jquery.com/jQuery/
Given that you can pass the context as a second argument, you can easily overwrite the $() operator in Javascript with a version which internally calls JQuery using jQuery.noConflict(); and always passes your new root as the second argument.
I don't think jQuery provide such method or variable. But you can pass second parameter in jQuery method to set context.
$.defaultRoot = $('.anyOtherRootElement');
$('div', $.defaultRoot ).text("Hello"); // all div inside $('.anyOtherRootElement')
$('div' ).text("Hello"); //all div inside body tag
I have a problem with old website. All JavaScript code on it use getElemenById function. But tags of site markup doen't have id property, instead they have only name property. Although it still works for IE, browser returns elements even by name property. For all other browsers it's a mistake in JS.
I wonder if there any way to overload this function in other browser to make web site compatible to other browsers?
There's no way to "overload" a function in JavaScript in the sense that you would do so in a strongly-typed language like Java or C. In fact, every function in JavaScript is already overloaded in this sense in that you can call any function with any number and type of arguments.
What you can do, however, is insert your own proxy in front of the existing version, and implement the proxy with whatever behavior you prefer. For instance:
document._oldGetElementById = document.getElementById;
document.getElementById = function(elemIdOrName) {
var result = document._oldGetElementById(elemIdOrName);
if (! result) {
var elems = document.getElementsByName(elemIdOrName);
if (elems && elems.length > 0) {
result = elems[0];
}
}
return result;
};
I wouldn't count on overriding getElementById working properly. Sounds easy enough to do a search and replace that does something like this:
// Replace
document.getElementById("foo");
// With
myGetElementById("foo", document);
// Replace
myElement.getElementById("foo");
// With
myGetElementById("foo", myElement);
Then you can myGetElementById as you want, without worrying about what might happen in old IEs and what not if you override getElementById.
Try getElementsByName. This is used to get a collection of elements with respect to their name
I got a simple question about jQuery but rather javascript approaches in general...
Is it ok, to do this? :
this._checkedTexts.length = 0; // <- array belonging to "me"
var contextCheckedTexts = this._checkedTexts;
$('#valueContainer input:checked').each(function() {
contextCheckedTexts.push($(this).text());
});
Since 'this' points to the element, I am using a closure here to keep additional context for the 'each'-handler around.
Is this the way 'how it's done' or could there be any risk involved using a closure in this way (memoryleak-wise....)?
What other options are there?
I find it very practical - but also I'm always a little worried about introducing reference-count-problems when using closures - which will be hard to find later on.
I don't know what you are trying to achieve but your codes may also be done like this,
this._checkedTexts.length = 0; // <- array belonging to "me"
var arr = $('#valueContainer input:checked').map(function() {
return $(this).text();
}).get();
this._checkedTexts = this._checkedTexts.concat(arr);
It seems to be fine as jQuery methods like each, map, grep does not support context by default (there're bound to the current jQuery object). However you can create you own bind function that will apply context to functions (but in this case you dontn't have to as you use default context to get the elements text).