Is it possible to call a method from an object using a string?
var elem = $('#test'); //<div id="test"></div>
var str = "attr('id')";
//This is what I'm trying to achieve
elem.attr('id'); //test
//What I've tried so far
elem.str; //undefined
elem.str(); //Object [object Object] has no method 'str'
var fn = eval(str); //attr is not defined
eval(elem.toString()+'.'+str); //Unexpected identifier
//Only solution I've found so far,
//but is not an option for me
//because this code is in a function
//so the element and method call
//get passed in and I wouldn't know
//what they are
eval($('#test').attr('id')); //test
UPDATE
This is my final, working answer:
After running this code in the console
theMethod = 'attr("id","foo")'.match(/^([^(]+)\(([^)]*)\)/);
jQuery('#post-form')[theMethod[1]].apply(jQuery('#post-form'),JSON.parse('['+theMethod[2]+']'));
The post-form element now has a new ID, no problems at all. This works for methods that take multiple arguments, a single argument or no arguments at all. Recap:
theMethod = theInString.match(/^\.?([^(]+)\(([^)]*)\)/);
//added \.? to trim leading dot
//made match in between brackets non-greedy
//dropped the $ flag at the end, to avoid issues with trailing white-space after )
elem[theMethod[1]].apply(elem,JSON.parse('['+theMethod+']'));
That's the safest, most reliable approach I can think of, really
What ever you do DON'T USE EVAL:
var theMethod = 'attr(\'id\')';
//break it down:
theMethod = theMethod.match(/^([^(]+)\(.*?([^)'"]+).*\)$/);
//returns ["attr('id')", "attr", "id"]
elem[theMethod[1]](theMethod[2]);//calls the method
It's the same basic principle as you'd use with any objects (remember that functions are objects all on their own in JS - and jQuery objects are, well, objects, too). This means that methods can be accessed in the exact same way as properties can:
$('#foo').attr('id') === $('#foo')['attr']('id');
So just break the string apart, and use the method name like you would an object property and you're all set to go.
Just remember: When all you have is the eval hammer, everything looks like your thumb.
Brendan Eich
If there is a chance of multiple arguments being passed to whatever method, you can sort of work your way around that, too (I think - well: logic dictates, but it's rather late and logic is getting beat up by Gin pretty bad now):
theMethod = theMethod.match(/^([^(]+)\(([^)]+)\)$/);
//["attr('id','foo')", "attr", "'id','foo'"] --> regex must now match quotes, too
elem.theMethod[1].apply(elem,JSON.parse('['+theMethod[2]+']'));
This applies the method of whatever element/object you're dealing with to itself, thus not changing the caller context (this
will still point to the object within the method) and it passes an array of arguments that will be passed to the called method.
You should use one of these methods:
apply
var result = function.apply(thisArg[, argsArray]);
call
var result = fun.call(thisArg[, arg1[, arg2[, ...]]]);
Here is the sample:
var Sample = function() {
var that = this;
this.sampleMethod = function() {
return alert("Hello!");
};
this.sampleMethod2 = function(){
that["sampleMethod"].apply(that);
};
};
var objImpl = new Sample();
objImpl.sampleMethod2(); //you will get a message from 'sampleMethod()'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With