We are hoping on trimming some fat from our custom library we use across our products.
One commonly used action is changing of object styles.
Normally, we do this via:
document.getElementById('object').style.property='value';
I just tested the following in chromes console, and it worked:
function objStyle(o,p,v){
document.getElementById(o).style[p]=v;
}
objStyle('object','property','value');
Is this a valid way of doing things?
Any pitfalls one can think of when using this way of doing things? Crossbrowser compatability?
Yes, that is perfectly valid. A property that you access by .name can also be access by ['name'].
That works for any property in any object, for example:
window['alert']('Hello world.');
document['getElementById']('object')['style']['color'] = '#fff';
Your code is fine.
One thing I would consider though is whether you want to keep calling document.getElementById() (inside the function) if there is a situation where you need to perform multiple changes to the same element. What I'm about to suggest is overkill for the sake of showing you more options, but consider that you can pass the Id to your function, or pass a reference to the element directly, or have a function that accepts a string or an element reference and figures it out from the type of the parameter:
function objStyleById(oId,p,v){
document.getElementById(oId).style[p]=v;
}
function objStyle(o,p,v) {
o.style[p] = v;
}
function objStyleAuto(o,p,v) {
if (typeof o === "string")
o = document.getElementById("o");
// else not a string so assume o is element reference
o.style[p] = v;
}
objStyleById('object','property','value');
var myEl = document.getElementById("someElement");
objStyle(myEl,"prop","val");
objStyle(myEl,"prop2","val");
// some other non-style operation on myEl, e.g.,
myEl.className = "something";
myEl.innerHTML = "something";
objStyle(myEl.parentNode,"prop","value");
objStyleAuto('object','property','value');
objStyleAuto(myEl,'property','value');
Related
I'm passing a property options_r into a function but i need to reference to its properties dynamically...
to illustrate in a simple way, here's a function that takes in a parameter.
options_r = {}
function blah(myData,options_r) {
output_data = myData[options_r.target]; //
alert(output_data)
}
THIS WORKS
myData.joe = 'male';
myData.anne = 'female';
let options_r.target = 'joe';
blah(myData, options_r);
THIS DOESNT
myData.dataset.joe = 'male';
myData.dataset.anne = 'female';
let options_r.target = 'dataset.joe';
blah(myData, options_r);
...and yes sometimes in need to reference an object within an object this way.
Any ideas?
Thanks!
What you are trying to acomplish is not a native feature of JavaScript.
Some common extension libraries do that, for example _.get function of lodash. Where in your case if you use this you will be able to do
_.get(myData, options_r.target);
Otherwise, you will have to code something that parse your path and does smart object traversal yourself.
The last option would be to use a solution based around eval but I really wouldn't advise going that route.
Is it possible to find the name of an anonymous function?
e.g. trying to find a way to alert either anonyFu or findMe in this code http://jsfiddle.net/L5F5N/1/
function namedFu(){
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var anonyFu = function() {
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var findMe= function(){
namedFu();
anonyFu();
}
findMe();
This is for some internal testing, so it doesn't need to be cross-browser. In fact, I'd be happy even if I had to install a plugin.
You can identify any property of a function from inside it, programmatically, even an unnamed anonymous function, by using arguments.callee. So you can identify the function with this simple trick:
Whenever you're making a function, assign it some property that you can use to identify it later.
For example, always make a property called id:
var fubar = function() {
this.id = "fubar";
//the stuff the function normally does, here
console.log(arguments.callee.id);
}
arguments.callee is the function, itself, so any property of that function can be accessed like id above, even one you assign yourself.
Callee is officially deprecated, but still works in almost all browsers, and there are certain circumstances in which there is still no substitute. You just can't use it in "strict mode".
You can alternatively, of course, name the anonymous function, like:
var fubar = function foobar() {
//the stuff the function normally does, here
console.log(arguments.callee.name);
}
But that's less elegant, obviously, since you can't (in this case) name it fubar in both spots; I had to make the actual name foobar.
If all of your functions have comments describing them, you can even grab that, like this:
var fubar = function() {
/*
fubar is effed up beyond all recognition
this returns some value or other that is described here
*/
//the stuff the function normally does, here
console.log(arguments.callee.toString().substr(0, 128);
}
Note that you can also use argument.callee.caller to access the function that called the current function. This lets you access the name (or properties, like id or the comment in the text) of the function from outside of it.
The reason you would do this is that you want to find out what called the function in question. This is a likely reason for you to be wanting to find this info programmatically, in the first place.
So if one of the fubar() examples above called this following function:
var kludge = function() {
console.log(arguments.callee.caller.id); // return "fubar" with the first version above
console.log(arguments.callee.caller.name); // return "foobar" in the second version above
console.log(arguments.callee.caller.toString().substr(0, 128);
/* that last one would return the first 128 characters in the third example,
which would happen to include the name in the comment.
Obviously, this is to be used only in a desperate case,
as it doesn't give you a concise value you can count on using)
*/
}
Doubt it's possible the way you've got it. For starters, if you added a line
var referenceFu = anonyFu;
which of those names would you expect to be able to log? They're both just references.
However – assuming you have the ability to change the code – this is valid javascript:
var anonyFu = function notActuallyAnonymous() {
console.log(arguments.callee.name);
}
which would log "notActuallyAnonymous". So you could just add names to all the anonymous functions you're interested in checking, without breaking your code.
Not sure that's helpful, but it's all I got.
I will add that if you know in which object that function is then you can add code - to that object or generally to objects prototype - that will get a key name basing on value.
Object.prototype.getKeyByValue = function( value ) {
for( var prop in this ) {
if( this.hasOwnProperty( prop ) ) {
if( this[ prop ] === value )
return prop;
}
}
}
And then you can use
THAT.getKeyByValue(arguments.callee.caller);
Used this approach once for debugging with performance testing involved in project where most of functions are in one object.
Didn't want to name all functions nor double names in code by any other mean, needed to calculate time of each function running - so did this plus pushing times on stack on function start and popping on end.
Why? To add very little code to each function and same for each of them to make measurements and calls list on console. It's temporary ofc.
THAT._TT = [];
THAT._TS = function () {
THAT._TT.push(performance.now());
}
THAT._TE = function () {
var tt = performance.now() - THAT._TT.pop();
var txt = THAT.getKeyByValue(arguments.callee.caller);
console.log('['+tt+'] -> '+txt);
};
THAT.some_function = function (x,y,z) {
THAT._TS();
// ... normal function job
THAT._TE();
}
THAT.some_other_function = function (a,b,c) {
THAT._TS();
// ... normal function job
THAT._TE();
}
Not very useful but maybe it will help someone with similar problem in similar circumstances.
arguments.callee it's deprecated, as MDN states:
You should avoid using arguments.callee() and just give every function
(expression) a name.
In other words:
[1,2,3].forEach(function foo() {
// you can call `foo` here for recursion
})
If what you want is to have a name for an anonymous function assigned to a variable, let's say you're debugging your code and you want to track the name of this function, then you can just name it twice, this is a common pattern:
var foo = function foo() { ... }
Except the evaling case specified in the MDN docs, I can't think of any other case where you'd want to use arguments.callee.
No. By definition, an anonymous function has no name. Yet, if you wanted to ask for function expressions: Yes, you can name them.
And no, it is not possible to get the name of a variable (which references the function) during runtime.
I'd like to make a function to the effect of:
function supportsElem(tagName) {
// returns boolean
}
where:
supportsElem("div") // true
supportsElem("randomtext") // false
What the easiest way to do that?
Just a guess, cause i've never had to care about this. But it'd seem to me you could create the element and then check to make sure it acts like it should. For example, that it has certain properties, or that its constructor is right (or at least, isn't the same as a generic unsupported element would have).
An example of the constructor check (untested):
// pick a name that'll never be an element
var generic_element = document.createElement('randomtext');
var tagName_to_check = document.createElement('div');
if (tagName_to_check.constructor === generic_element.constructor) {
// the browser treats the node as a generic element, rather than
// (eg) a DivElement
// so it's probably unsupported
}
Try this:
function testTag(tagname) {
return document.createElement(tagname) instanceof HTMLUnknownElement;
}
I don't know what kind of browser support this (HTMLUnknownElement) will have though.
This is more effort than it's worth.
Just keep a dictionary of all the valid tags (easy to find online). Basically an array of strings
then its just
var dictionary = ["div", "a", "input", "span", ..., "td"];
var myTag = "div";
dictionary.indexOf(myTag); // if this doesn't return -1, then the tag is valid
You could simply create the element, then test it against a method specific to this element.
okay im working with a friend and he sent me js file which included a variable that included the ternary operator. I cant figure out how to change it to if..else. can you help please?
also i noticed ".length" didnt have the normal "()" after it, is there a reason why?
var nextRadioTab = activeRadioTab.next().length ? activeRadioTab.next() : $('#contentslider div:eq(0)');
Does this work?
if (activeRadioTab.next().length) {
var nextRadioTab = activeRadioTab.next();
} else {
var nextRadioTab = $('#contentslider div:eq(0)');
}
In JavaScript, objects are more-or-less just a list of names pointing to values. Each name-value pair is called a "property".
These values themselves can be any type of value, including a function. If the value of a property is a function, we call that a "method".
Say you want an object to track the x and y coordinates of a point.
var point = { x: 10, y: 20 };
In this case we can just use simple values, because we don't need any behaviour more advanced than getting a value (alert(point.x)) or setting one (point.x = 10).
jQuery is designed to let your code work on different browsers; different browsers behave differently in lots of situations, so jQuery can't just let you set
element.text = "hello world"
because depending on the type of object element is, it will need to modify different properties on different browsers. For this reason, jQuery makes you use methods for things like this:
element.text("hello world")
The .length attribute of a jQuery object is simple; it's controlled by jQuery itself and doesn't need to do any special things in different browsers. For this reason, you just use it directly. If they needed more complicated behaviour, they would use a function/method instead:
var myObject = { length: 2 }; // myObject.length
var myObject = { length: function() { return 2; } }; // myObject.length()
var nextRadioTab;
if (activeRadioTab.next().length)
nextRadioTab = activeRadioTab.next();
else
nextRadioTab = $('#contentslider div:eq(0)');
length is a property of whatever next() returns, which is most likely the same type of object as activeRadioTab.
Because jQuery is a widely used and mature collaborative effort, I can't help but to look at its source for guidance in writing better Javascript. I use the jQuery library all the time along with my PHP applications, but when I look under the hood of this rather sophisticated library I realize just how much I still don't understand about Javascript. Lo, I have a few questions for the SO community. First of all, consider the following code...
$('#element').attr('alt', 'Ivan is SUPER hungry! lolz');
vs
$('#element').attr({'alt': 'Ivan is an ugly monster! omfgz'});
Now, is this to say that the attr() method was designed to accept EITHER an attribute name, an attribute name and a value, or a pair-value map? Can someone give me a short explanation of what a map actually is and the important ways that it differs from an array in Javascript?
Moving on, the whole library is wrapped in this business...
(function(window, undefined) { /* jQuery */ })(window);
I get that the wrapped parentheses cause a behavior similar to body onLoad="function();", but what is this practice called and is it any different than using the onLoad event handler? Also, I can't make heads or tails of the (window) bit there at the end. What exactly is happening with the window object here?
Am I wrong in the assessment that objects are no different than functions in Javascript? Please correct me if I'm wrong on this but $() is the all encompassing jQuery object, but it looks just like a method. Here's another quick question with a code example...
$('#element').attr('alt', 'Adopt a Phantom Cougar from Your Local ASPCA');
... Should look something like this on the inside (maybe I'm wrong about this)...
function $(var element = null) {
if (element != null) {
function attr(var attribute = null, var value = null) {
/* stuff that does things */
}
}
}
Is this the standing procedure for defining objects and their child methods and properties in Javascript? Comparing Javascript to PHP, do you use a period . the same way you would use -> to retrieve a method from an object?
I apologize for this being a bit lengthy, but answers to these questions will reveal a great deal to me about jQuery and Javascript in general. Thanks!
1. Method overloading
$('#element').attr('alt', 'Ivan is SUPER hungry! lolz');
vs
$('#element').attr({'alt': 'Ivan is an ugly monster! omfgz'});
var attr = function (key, value) {
// is first argument an object / map ?
if (typeof key === "object") {
// for each key value pair
for (var k in key) {
// recursively call it.
attr(k, key[k]);
}
} else {
// do magic with key and value
}
}
2. Closures
(function(window, undefined) { /* jQuery */ })(window);
Is not used as an onload handler. It's simply creating new scope inside a function.
This means that var foo is a local variable rather then a global one. It's also creating a real undefined variable to use since Parameters that are not specified passes in undefined
This gaurds againts window.undefined = true which is valid / allowed.
the (window) bit there at the end. What exactly is happening with the window object here?
It's micro optimising window access by making it local. Local variable access is about 0.01% faster then global variable access
Am I wrong in the assessment that objects are no different than functions in Javascript?
Yes and no. All functions are objects. $() just returns a new jQuery object because internally it calls return new jQuery.fn.init();
3. Your snippet
function $(var element = null) {
Javascript does not support default parameter values or optional parameters. Standard practice to emulate this is as follows
function f(o) {
o != null || (o = "default");
}
Comparing Javascript to PHP, do you use a period . the same way you would use -> to retrieve a method from an object?
You can access properties on an object using foo.property or foo["property"] a property can be any type including functions / methods.
4. Miscellanous Questions hidden in your question
Can someone give me a short explanation of what a map actually is and the important ways that it differs from an array in Javascript?
An array is created using var a = [] it simply contains a list of key value pairs where all the keys are positive numbers. It also has all the Array methods. Arrays are also objects.
A map is just an object. An object is simply a bag of key value pairs. You assign some data under a key on the object. This data can be of any type.
For attr, if you give an object instead of a key value pair it will loop on each property.
Look for attr: in jQuery's code, then you'll see it use access. Then look for access: and you will see there is a check on the type of key if it is an object, start a loop.
The wrapping in a function, is to prevent all the code inside to be accessed from outside, and cause unwanted problems. The only parameters that are passed are window that allow to set globals and access the DOM. The undefined I guess it is to make the check on this special value quicker.
I read sometimes jQuery but I didn't start with it, may be you should get some good books to make you an idea first of what some advanced features Javascript has, and then apply your knowledge to the specifics of jQuery.
1 - Yes attr can accept a attribute name for getting a value, a name and a value for setting one value or a map of attribute names and values for settings more than one attribute
2 - A map is basically a JavaScript object e.g:
var map = {
'key1' : 'value1',
'key2' : 'value2'
};
3 - (function(window, undefined) { /* jQuery */ })(window); is something called an anonymous function as it doesn't have a name. In this case it also executes straight away.
A simple example would be:
function test(){
...
}
test();
//As an anonymous function it would be:
(function(){
...
}();
//And it you wanted to pass variables:
function test(abc){
...
}
test(abc);
//As an anonymous function it would be:
(function(abc){
...
}(abc);
this would make it different to the load event, as it is a function not an event.
4 - window is passed as a variable, as it is used internally within jQuery
5 - Objects and functions the same, as everything in JavaScript is an object. jQuery does something like this:
var obj = {
"init" : function(){
}
}
6 - Yes you can use . to retrieve a value on an object but you can also use [] e.g:
var map = {
"test" : 1
}
map.test //1
map["test"] //1
I hope this answers your many questions, let me know if I've missed anything out.
jQuery 1.6.1
The test is typeof key === "object"
if that is true, then you passed a { .... }
jQuery.fn.extend({
attr: function( name, value ) {
return jQuery.access( this, name, value, true, jQuery.attr );
},
// Mutifunctional method to get and set values to a collection
// The value/s can be optionally by executed if its a function
access: function( elems, key, value, exec, fn, pass ) {
var length = elems.length;
// Setting many attributes
if ( typeof key === "object" ) {
for ( var k in key ) {
jQuery.access( elems, k, key[k], exec, fn, value );
}
return elems;
}
// Setting one attribute
if ( value !== undefined ) {
// Optionally, function values get executed if exec is true
exec = !pass && exec && jQuery.isFunction(value);
for ( var i = 0; i < length; i++ ) {
fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
}
return elems;
}
// Getting an attribute
return length ? fn( elems[0], key ) : undefined;
},