I'd like to be able to say something like this in javascript :
"a".distance("b")
How can I add my own distance function to the string class?
You can extend the String prototype;
String.prototype.distance = function (char) {
var index = this.indexOf(char);
if (index === -1) {
alert(char + " does not appear in " + this);
} else {
alert(char + " is " + (this.length - index) + " characters from the end of the string!");
}
};
... and use it like this;
"Hello".distance("H");
See a JSFiddle here.
String.prototype.distance = function( arg ) {
// code
};
Minimal example:
No ones mentioned valueOf.
==================================================
String.prototype.
OPERATES_ON_COPY_OF_STRING = function (
ARGUMENT
){
//:Get primitive copy of string:
var str = this.valueOf();
//:Append Characters To End:
str = str + ARGUMENT;
//:Return modified copy:
return( str );
};
var a = "[Whatever]";
var b = a.OPERATES_ON_COPY_OF_STRING("[Hi]");
console.log( a ); //: [Whatever]
console.log( b ); //: [Whatever][Hi]
==================================================
From my research into it, there is no way to edit the string in place.
Even if you use a string object instead of a string primitive.
Below does NOT work and get's really weird results in the debugger.
==================================================
String.prototype.
EDIT_IN_PLACE_DOES_NOT_WORK = function (
ARGUMENT
){
//:Get string object:
var str = this;
//:Append Characters To End:
var LN = str.length;
for( var i = 0; i < ARGUMENT.length; i++){
str[LN+i] = ARGUMENT[ i ];
};
};
var c = new String( "[Hello]" );
console.log( c );
c.EDIT_IN_PLACE_DOES_NOT_WORK("[World]");
console.log( c );
==================================================
after years (and ES6) … we have a new option how to do this:
Object.defineProperty( String.prototype, 'distance', {
value: function ( param )
{
// your code …
return 'counting distance between ' + this + ' and ' + param;
}
} );
// ... and use it like this:
const result = "a".distance( "b" );
console.log(result);
You could do this:
String.prototype.distance = function (){
//your code
}
Using prototype to add you own function to string is called a prototype I created small JavaScript code that can select elements and change its innerHTML
var dom; //you can replce this to be $ just like jQuery
dom = function(elm) {
if(typeof elm === "object") {
// already done example
//typeof document.getElementById('id') will be object
return [elm];
} else {
return document.querySelectorAll(elm);
}
} // Returns elements by all css selector eg
// .class #id id p id > p id ~ p in short any css selectors
Object.prototype.text = function(txt) { //object prototype as NodeList returned would be object or maybe displayed as [Object NodeList]
var i = 0; //loop through the elements
for(i; i < this.length; i++) {
this[i].innerHTML = txt;
}
// in this function this refers to object that this function is passed on
};
dom('.classh').text('Changed for elements with classh');
dom('#heading').text('Changed for elements with id heading'); //examples
Related
I have built this function to replace a group of characters in a string by a random value from another list within a function:
function replaceExpr(a) {
var expToReplace = 0
var newSent = a
while (expToReplace == 0) {
if (a.search("zx") == -1) {
expToReplace = 1
} else {
var startPos = a.search("zx");
startPos += 2;
var endPos = a.search("xz");
var b = a.substring(startPos, endPos);
var fn = window[b];
if (typeof fn === "function") var newWord = fn();
final = newSent.replace("zx" + b + "xz", newWord);
newSent = final
a = a.replace("zx" + b + "xz", "")
}
}
return final
}
function appearance() {
var list = [
"attractive",
"fit",
"handsome",
"plain",
"short",
"tall",
"skinny",
"well-built",
"unkempt",
"unattractive"
]
return list[Math.floor(Math.random() * list.length)];
}
function personality() {
var list = [
"aggresive",
"absent-minded",
"cautious",
"detached from the real world",
"easygoing",
"focused",
"honest",
"dishonest",
"polite",
"uncivilized"
]
return list[Math.floor(Math.random() * list.length)];
}
An example :
var a = replaceExpr("Theodor is a zxappearancexz man. He seems rather zxpersonalityxz.")
alert(a)
// Theodor is a unattractive man. He seems rather cautious.
Everything works perfectly with the function but I have an issue related to it. As you can see, there's one grammar mistake : it's written "a unattractive" where it should be "an unattractive".
There's a function I usually use to to fix the a\an issue which is :
var AvsAnSimple = (function (root) {
//by Eamon Nerbonne (from http://home.nerbonne.org/A-vs-An), Apache 2.0 license
// finds if a word needs a "a" or "an" before it
var dict = "2h.#2.a;i;&1.N;*4.a;e;i;o;/9.a;e;h1.o.i;l1./;n1.o.o;r1.e.s1./;01.8;12.1a;01.0;12.8;9;2.31.7;4.5.6.7.8.9.8a;0a.0;1;2;3;4;5;6;7;8;9;11; .22; .–.31; .42; .–.55; .,.h.k.m.62; .k.72; .–.82; .,.92; .–.8;<2.m1.d;o;=1.=1.E;#;A6;A1;A1.S;i1;r1;o.m1;a1;r1; .n1;d1;a1;l1;u1;c1.i1.a1.n;s1;t1;u1;r1;i1;a1;s.t1;h1;l1;e1;t1;e1.s;B2.h2.a1.i1;r1;a.á;o1.r1.d1. ;C3.a1.i1.s1.s.h4.a2.i1.s1;e.o1.i;l1.á;r1.o1.í;u2.i;r1.r1.a;o1.n1.g1.j;D7.a1.o1.q;i2.n1.a1.s;o1.t;u1.a1.l1.c;á1. ;ò;ù;ư;E7;U1;R.b1;o1;l1;i.m1;p1;e1;z.n1;a1;m.s1;p5.a1.c;e;h;o;r;u1.l1;o.w1;i.F11. ;,;.;/;0;1;2;3;4;5;6;71.0.8;9;Ae;B.C.D.F.I2.L.R.K.L.M.N.P.Q.R.S.T.B;C1;M.D;E2.C;I;F1;r.H;I3.A1;T.R1. ;U;J;L3.C;N;P;M;O1. ;P1;..R2.A1. ;S;S;T1;S.U2.,;.;X;Y1;V.c;f1.o.h;σ;G7.e1.r1.n1.e;h1.a3.e;i;o;i1.a1.n1.g;o2.f1. ;t1.t1. ;r1.i1.a;w1.a1.r1.r;ú;Hs. ;&;,;.2;A.I.1;2;3;5;7;B1;P.C;D;F;G;H1;I.I6;C.G.N.P.S1.D;T.K1.9;L;M1;..N;O2. ;V;P;R1;T.S1.F.T;V;e2.i1.r;r1.r1.n;o2.n6;d.e1.s;g.k.o2;l.r1;i1.f;v.u1.r;I3;I2;*.I.n1;d1;e1;p1;e1;n1;d2;e1;n1;c1;i.ê.s1;l1;a1;n1;d1;s.J1.i1.a1.o;Ly. ;,;.;1;2;3;4;8;A3. ;P;X;B;C;D;E2. ;D;F1;T.G;H1.D.I1.R;L;M;N;P;R;S1;m.T;U1. ;V1;C.W1.T;Z;^;a1.o1.i1.g;o1.c1.h1.a1;b.p;u1.s1.h1;o.ộ;M15. ;&;,;.1;A1;.1;S./;1;2;3;4;5;6;7;8;Ai;B.C.D.F.G.J.L.M.N.P.R.S.T.V.W.X.Y.Z.B1;S1;T.C;D;E3.P1;S.W;n;F;G;H;I4. ;5;6;T1;M.K;L;M;N;O1.U;P;Q;R;S;T1;R.U2. ;V;V;X;b1.u1.m;f;h;o2.D1.e.U1;..p1.3;s1.c;Ny. ;+;.1.E.4;7;8;:;A3.A1;F.I;S1.L;B;C;D;E3.A;H;S1. ;F1;U.G;H;I7.C.D1. ;K.L.N.O.S.K;L;M1;M.N2.R;T;P1.O1.V1./1.B;R2;J.T.S1;W.T1;L1.D.U1.S;V;W2.A;O1.H;X;Y3.C1.L;P;U;a1.s1.a1.n;t1.h;v;²;×;O5;N1;E.l1;v.n2;c1.e.e1.i;o1;p.u1;i.P1.h2.i1.a;o2.b2;i.o.i;Q1.i1.n1.g1.x;Rz. ;&;,;.1;J./;1;4;6;A3. ;.;F1;T.B1;R.C;D;E3. ;S1.P;U;F;G;H1.S;I2.A;C1. ;J;K;L1;P.M5;1.2.3.5.6.N;O2.H;T2;A.O.P;Q;R1;F.S4;,...?.T.T;U4;B.M.N.S.V;X;c;f1;M1...h2.A;B;ò;S11. ;&;,;.4.E;M;O;T1..3.B;D;M;1;3;4;5;6;8;9;A3. ;8;S2;E.I.B;C3.A1. ;R2.A.U.T;D;E6. ;5;C3;A.O.R.I1.F.O;U;F3;&.H.O1.S.G1;D.H3.2;3;L;I2. ;S1.O.K2.I.Y.L3;A2. ;.;I1. ;O.M3;A1. ;I.U1.R.N5.A.C3.A.B.C.E.F.O.O5. ;A1.I;E;S1;U.V;P7;A7;A.C.D.M.N.R.S.E1. ;I4;C.D.N.R.L1;O.O.U.Y.Q1. ;R;S1;W.T9.A1. ;C;D;F;I;L;M;S;V;U7.B.L.M.N.P.R.S.V;W1.R;X1.M;h1.i1.g1.a1.o;p1.i1.o1;n.t2.B;i1.c1.i;T4.a2.i2.g1.a.s1.c;v1.e1.s;e1.a1.m1.p;u1.i2.l;r;à;Um..1.N1..1.C;/1.1;11. .21.1;L1.T;M1.N;N4.C1.L;D2. .P.K;R1. .a;b2;a.i.d;g1.l;i1.g.l2;i.y.m;no. ;a1.n.b;c;d;e1;s.f;g;h;i2.d;n;j;k;l;m;n;o;p;q;r;s;t;u;v;w;p;r3;a.e.u1.k;s3. ;h;t1;r.t4.h;n;r;t;x;z;í;W2.P1.:4.A1.F;I2.B;N1.H.O1.V;R1.F1.C2.N.U.i1.k1.i1.E1.l1.i;X7;a.e.h.i.o.u.y.Y3.e1.t1.h;p;s;[5.A;E;I;a;e;_2._1.i;e;`3.a;e;i;a7; .m1;a1;r1. .n1;d2; .ě.p1;r1;t.r1;t1;í.u1;s1;s1;i1. .v1;u1;t.d3.a1.s1. ;e2.m1. ;r1. ;i2.c1.h1. ;e1.s1.e2.m;r;e8;c1;o1;n1;o1;m1;i1;a.e1;w.l1;i1;t1;e1;i.m1;p1;e1;z.n1;t1;e1;n1;d.s2;a1. .t4;a1; .e1; .i1;m1;a1;r.r1;u1.t.u1.p1. ;w.f3. ;M;y1.i;h9. ;,;.;C;a1.u1.t1;b.e2.i1.r1;a.r1.m1.a1.n;o4.m2.a1; .m;n8; .b.d.e3; .d.y.g.i.k.v.r1.s1. ;u1.r;r1. ;t1;t1;p1;:.i6;b1;n.e1;r.n2;f2;l1;u1;ê.o1;a.s1;t1;a1;l1;a.r1; .s1; .u.k1.u1. ;l3.c1.d;s1. ;v1.a;ma. ;,;R;b1.a.e1.i1.n;f;p;t1.a.u1.l1.t1.i1.c1.a1.m1.p1.i;×;n6. ;V;W;d1; .t;×;o8;c2;h1;o.u1;p.d1;d1;y.f1; .g1;g1;i.no. ;';,;/;a;b;c1.o;d;e2.i;r;f;g;i;l;m;n;o;r;s;t;u;w;y;z;–;r1;i1;g1;e.t1;r1.s;u1;i.r3. ;&;f;s9.,;?;R;f2.e.o.i1.c1.h;l1. ;p2.3;i1. ;r1.g;v3.a.e.i.t2.A;S;uc; ...b2.e;l;f.k2.a;i;m1;a1. .n3;a3; .n5.a;c;n;s;t;r1;y.e2; .i.i8.c2.o1.r1.p;u1.m;d1;i1.o;g1.n;l1.l;m1;o.n;s1.s;v1.o1;c.r5;a.e.i.l.o.s3. ;h;u1.r2;e.p3;a.e.i.t2.m;t;v.w1.a;xb. ;';,;.;8;b;k;l;m1;a.t;y1. ;y1.l;{1.a;|1.a;£1.8;À;Á;Ä;Å;Æ;É;Ò;Ó;Ö;Ü;à;á;æ;è;é1;t3.a;o;u;í;ö;ü1; .Ā;ā;ī;İ;Ō;ō;œ;Ω;α;ε;ω;ϵ;е;–2.e;i;ℓ;";
function fill(node) {
var kidCount = parseInt(dict, 36) || 0,
offset = kidCount && kidCount.toString(36).length;
node.article = dict[offset] == "." ? "a" : "an";
dict = dict.substr(1 + offset);
for (var i = 0; i < kidCount; i++) {
var kid = node[dict[0]] = {}
dict = dict.substr(1);
fill(kid);
}
}
fill(root);
return {
raw: root,
//Usage example: AvsAnSimple.query("example")
//example returns: "an"
query: function (word) {
var node = root, sI = 0, result, c;
do {
c = word[sI++];
} while ('"‘’“”$\''.indexOf(c) >= 0);//also terminates on end-of-string "undefined".
while (1) {
result = node.article || result;
node = node[c];
if (!node) return result;
c = word[sI++] || " ";
}
}
};
})({})
Now, the problem is that I can't find a way to use this function in conjunction with the replaceExpr. The following obviously wouldn't work because of order precedence :
var a = replaceExpr("Theodor is " + AvsAnSimple(zxappearancexz) + "man. He seems rather " + AvsAnSimple(zxpersonalityxz).")
I just recently started learning javascript so my knowledge is rather limited. Any ideas how I could overcome this?
Thank you!
You could use a regular expression to optionally match the " a " or "an" before your word in the input string and store that matched portion in a variable using the String.match() function, then check if that " a " or " an " exists in your matched string, do the manipulations you need to do and store that manipulated string in a separate variable, then use String.replace() to find that previously matched string again, and replace it wit
your manipulated string. The regular expression you could use for this is /(\san?\s)?(zx\w*zx)/gm
See the regular expression here for more context.
Thank you Joseph! With your help I managed to find something that works by using your regular expression. Here's my function :
function replaceExpr(a) {
var nbExprToReplace = 1;
while (nbExprToReplace == 1) {
if (a.search("zx") == -1) {
nbExprToReplace = 0;
} else {
var currentGroup = a.match(/(\san?\s)?(zx\w*xz)/);
var exprToChange = currentGroup[2];
exprToChange = exprToChange.slice(2,-2);
var exprToChange = window[exprToChange];
if (typeof exprToChange !== "function") {
alert("the keyword is not a recognized function!");
break;
} else {
exprToChange = exprToChange();
var final = exprToChange
};
if (currentGroup[1] === undefined) {
} else {
var newArticle = AvsAnSimple.query(exprToChange);
final = newArticle.concat(" " + final)
};
a = a.replace(currentGroup[0], " " + final);
};
};
return a;
};
Currently I'm writing a librarySystem
And there's an stringArray ['greet','name'] as dependencies in the code example below, however I need to use this stringArray as an array of functions passing into
greetingToName() by using apply(), is there any way to convert the stringArray into An array of functions ?
function greet(){
return 'hi!';
}
function name(){
return 'name';
}
function greetingToName (greet, name) {
console.log( greet() + ' ' + name() );
}
var stringArray = ['greet','name'];
greetingToName.apply(null, stringArray); // this is not working properly , because it passes 'greet' and 'name' as string into function, instead of function pointers.
You can create an Object with those 2 functions, and pass the strings as you wanted, and then look inside the object by name.
const posiableFuncs = {
greet: () => {
return 'hi!';
},
name: () => {
return 'name';
}
};
function greetingToName(greet, name) {
console.log(posiableFuncs[greet]() + ' ' + posiableFuncs[name]());
}
const stringArray = ['greet', 'name'];
greetingToName.apply(null, stringArray);
Creating function variables and assigning them in array would do the trick.
something as below.
var g = function(){
return 'hi!';
}
var n = function(){
return 'name';
}
function greetingToName (greet, name) {
console.log( greet() + ' ' + name() );
}
var stringArray = [g,n];
Fiddle
https://jsfiddle.net/dk_dragonknight/yhL188se/
After i've read everybody's solutions here, I got huge inspiration, therefor I came up with this solution without changing the function context , what do you guys think ?
var posiableFuncs = {
greet : function(){
return 'hi! ';
},
name : function(){
return 'Mr. Awesome!';
}
}
function greetingToName (greet, name) {
console.log( greet() + ' ' + name() );
}
var stringArray = ['greet','name'];
// copy the functions and assign into arrayOfFunctions variable based on stringArray
var arrayOfFunctions = stringArray.map(function(functionName){
return posiableFuncs[functionName];
});
greetingToName.apply(window, arrayOfFunctions);
I'm writing a piece of code to easily save error logs in an object for debugging.
What I'm trying to achieve is to get the Object name from the function it was called from like so:
var MainObject = {
test : function() {
return MainObject.test.caller;
// When called from MainObject.testcaller,
// it should return MainObject.testcaller.
},
testcaller : function() {
return MainObject.test(); // Should return MainObject.testcaller, Returns own function code.
},
anothercaller : function() {
return MainObject.test(); // Should return MainObject.anothercaller, Returns own function code.
}
}
However when I run this code it returns the function code from MainObject.testcaller.
JSFiddle example
Is there any way this is possible?
Update
After looking at Rhumborl's answer, I discovered that assigning the value through another function would lead it to point back at the function name without the object itself.
Code:
(function (name, func) {
MainObject[name] = func;
})('invalid', function() {
return MainObject.test("blah");
});
// This now points at invalid() rather than MainObject.invalid()
Updated fiddle
There is a non–standard caller property of functions that returns the caller function, however that is a pointer to a function object and doesn't tell you the object it was called as a method of, or the object's name. You can get a reference to the function through arguments.callee.
There is also the obsolete arguments.caller, but don't use that. It also provides a reference to the calling function (where supported).
Once you have a reference to the calling function (if there is one), you then have the issue of resolving its name. Given that Functions are Objects, and objects can be referenced by multiple properties and variables, the concept of a function having a particular name is alluvial.
However, if you know that the function is a property of some object, you can iterate over the object's own enumerable properties to find out which one it is.
But that seems to be a rather odd thing to do. What are you actually trying to do? You may be trying to solve a problem that can be worked around in a much more robust and simpler way.
Edit
You can do what you want in a very limited way using the method described above for the case in the OP, however it is not robust or a general solution:
var mainObject = {
test : function() {
var obj = this;
var caller = arguments.callee.caller;
var global = (function(){return this}());
var fnName, objName;
for (var p in global) {
if (global[p] === obj) {
objName = p;
}
}
for (var f in obj) {
if (obj[f] === caller) {
fnName = f;
}
}
return objName + '.' + fnName;
},
testcaller : function() {
return mainObject.test();
},
anothercaller : function() {
return mainObject.test();
}
}
console.log(mainObject.testcaller()); // mainObject.testcaller
console.log(mainObject.anothercaller()); // mainObject.anothercaller
but it's brittle:
var a = mainObject.anothercaller;
console.log(a()); // mainObject.anothercaller
var b = {
foo : mainObject.anothercaller
}
console.log(b.foo()); // mainObject.anothercaller
Oops.
You can use this trick at http://www.eriwen.com/javascript/js-stack-trace/ which throws an error, then parses the stack trace.
I have updated it for the latest versions of Firefox, Chrome and IE. Unfortunately it doesn't work well on my IE9 (and I haven't tested it on Opera).
function getStackTrace() {
var callstack = [];
var isCallstackPopulated = false;
try {
i.dont.exist += 0; //doesn't exist- that's the point
} catch (e) {
if (e.stack) { //Firefox/Chrome/IE11
var lines = e.stack.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
var line = lines[i].trim();
if (line.match(/^at [A-Za-z0-9\.\-_\$]+\s*\(/)) {
// Chrome/IE: " at Object.MainObject.testcaller (url:line:char)"
var entry = line.substring(3, line.indexOf('(') - 1);
// Chrome appends "Object." to the front of the object functions, so strip it off
if (entry.indexOf("Object.") == 0) {
entry = entry.substr(7, entry.length);
}
callstack.push(entry);
} else if (line.match(/^[A-Za-z0-9\.\-_\$]+\s*#/)) {
// Firefox: "MainObject.testcaller#url:line:char"
callstack.push(line.substring(0, lines[i].indexOf('#')));
}
}
//Remove call to getStackTrace()
callstack.shift();
isCallstackPopulated = true;
} else if (window.opera && e.message) { //Opera
var lines = e.message.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
var entry = lines[i];
//Append next line also since it has the file info
if (lines[i + 1]) {
entry += lines[i + 1];
i++;
}
callstack.push(entry);
}
}
//Remove call to getStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
}
if (!isCallstackPopulated) { //IE9 and Safari
var currentFunction = arguments.callee.caller;
while (currentFunction) {
var fn = currentFunction.toString();
var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf('')) || 'anonymous';
callstack.push(fname);
currentFunction = currentFunction.caller;
}
}
return callstack;
}
var MainObject = {
test: function (x) {
// first entry is the current function (test), second entry is the caller
var stackTrace = getStackTrace();
var caller = stackTrace[1];
return caller + "()";
},
testcaller: function () {
return MainObject.test(1, null);
}
}
function SomeFunction() {
return MainObject.test("blah");
}
document.body.innerHTML += '<b style="color: red">' + MainObject.testcaller() + '</b>';
document.body.innerHTML += '<div>Calling SomeFunction() returns: <b style="color: red">' + SomeFunction() + '</b></div>';
MainObject.test() should return: <b style="color: blue">MainObject.testcaller()</b>
<hr />
MainObject.test() returns:
Updated fiddle here
I have a string like this "namespace.fun1.fun2.fun3" passed from the client. It's telling the server which function to use.
How do I safely run the function?
right now i'm doing:
var runthis = string.split('.')
namespace[runthis[1]][runthis[2]][runthis[3]]
How do I handle arbitrary depth of nesting safely?
A little function I wrote. I use it in most of my applications:
Object.lookup = (function _lookup() {
var cache = { };
return function _lookupClosure( lookup, failGracefully ) {
var check = null,
chain = [ ],
lastkey = '';
if( typeof lookup === 'string' ) {
if( cache[ lookup ] ) {
chain = cache[ lookup ].chain;
check = cache[ lookup ].check;
}
else {
lookup.split( /\./ ).forEach(function _forEach( key, index, arr ) {
if( check ) {
if( typeof check === 'object' ) {
if( key in check ) {
chain.push( check = check[ key ] );
lastkey = key;
}
else {
if( !failGracefully ) {
throw new TypeError( 'cannot resolve "' + key + '" in ' + lastkey );
}
}
}
else {
if( !failGracefully ) {
throw new TypeError( '"' + check + '" ' + ' does not seem to be an object' );
}
}
}
else {
lastkey = key;
chain.push( check = window[ key ] );
}
});
if( check ) {
cache[ lookup ] = {
chain: chain,
check: check
};
}
}
}
return {
execute: function _execute() {
return typeof check === 'function' ? check.apply( chain[chain.length - 2], arguments ) : null;
},
get: function _get() {
return check;
}
};
}
}());
usage:
Object.lookup( 'namespace.fun1.fun2.fun3' ).execute();
The first parameter is the object/method/property to resolve. The second (optional) parameter indicates whether or not the lookup() method shall fail silently or throw an exception if some property or object could not get resolved. default is 'throw'. To avoid that call
Object.lookup( 'namespace.fun1.fun2.fun3', true ).execute( 'with', 'paras' );
If .fun3 is a function, you can pass in any parameters into .execute() instead.
if you just want to receive the property value, you can also call .get() instead of .execute()
var val = Object.lookup( 'namespace.fun1.fun2.fun3' ).get();
(I may be misinterpreting the question, but this is what came to mind)
var s = "space.f.g.h.i.j.k.l.m",
a = s.split( "." ),
fn = eval( a[0] );
for ( var i = 1; i < a.length; i++ ) {
fn = fn[ a[i] ];
}
fn();
Note: this won't guard against the namespace being specified incorrectly or maliciously.
This should do:
var get = function(obj, key) {
var s = key.split('.')
, i = 1
, l = s.length;
for (; i < l; i++) {
obj = obj[s[i]];
if (!obj) return;
}
return obj;
};
get({hello:{world:{}}}, 'ns.hello.world');
edit: changed code a bit
Here's a simple for loop that should do find each object specified strating from the global scope, and then run the function it finds.
window.namespace = { fun1: { fun2: { fun3: function() { alert('hi!') }}}};
runFunc = function(address) {
var addressArray = address.split('.'),
current = window,
i = 0;
for (i = 0; i < addressArray.length; i++) {
current = current[addressArray[i]];
}
current();
};
runFunc('namespace.fun1.fun2.fun3');
http://jsfiddle.net/jfWra/1/
And here's some eror protection that will throw something meaningful if the value referenced doesnt exist or is not a function: http://jsfiddle.net/jfWra/2/
Here's another simple solution using a recursive function:
function run(str, context) {
var path = str.split(".")
if path.length == 1 {
context[path[0]].call()
return;
}
if(typeof context == 'undefined') {
context = window[path[0]]
} else {
context = context[path[0]]
}
run(path.splice(1).join('.'), context)
}
Two years later there is this module: https://github.com/daaku/nodejs-dotaccess.
var get = require("dotaccess").get;
var o = {a:{b:{c:'deep'}}};
console.log(get(o, 'a.b.c'));
Since element.classList is not supported in IE 9 and Safari-5, what's an alternative cross-browser solution?
No-frameworks please.
Solution must work in at least IE 9, Safari 5, FireFox 4, Opera 11.5, and Chrome.
Related posts (but does not contain solution):
how to add and remove css class
Add and remove a class with animation
Add remove class?
Here is solution for addClass, removeClass, hasClass in pure javascript solution.
Actually it's from http://jaketrent.com/post/addremove-classes-raw-javascript/
function hasClass(ele,cls) {
return !!ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}
function addClass(ele,cls) {
if (!hasClass(ele,cls)) ele.className += " "+cls;
}
function removeClass(ele,cls) {
if (hasClass(ele,cls)) {
var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
ele.className=ele.className.replace(reg,' ');
}
}
Look at these oneliners:
Remove class:
element.classList.remove('hidden');
Add class (will not add it twice if already present):
element.classList.add('hidden');
Toggle class (adds the class if it's not already present and removes it if it is)
element.classList.toggle('hidden');
That's all! I made a test - 10000 iterations. 0.8s.
I just wrote these up:
function addClass(el, classNameToAdd){
el.className += ' ' + classNameToAdd;
}
function removeClass(el, classNameToRemove){
var elClass = ' ' + el.className + ' ';
while(elClass.indexOf(' ' + classNameToRemove + ' ') !== -1){
elClass = elClass.replace(' ' + classNameToRemove + ' ', '');
}
el.className = elClass;
}
I think they'll work in all browsers.
The simplest is element.classList which has remove(name), add(name), toggle(name), and contains(name) methods and is now supported by all major browsers.
For older browsers you change element.className. Here are two helper:
function addClass(element, className){
element.className += ' ' + className;
}
function removeClass(element, className) {
element.className = element.className.replace(
new RegExp('( |^)' + className + '( |$)', 'g'), ' ').trim();
}
One way to play around with classes without frameworks/libraries would be using the property Element.className, which "gets and sets the value of the class attribute of the specified element." (from the MDN documentation).
As #matías-fidemraizer already mentioned in his answer, once you get the string of classes for your element you can use any methods associated with strings to modify it.
Here's an example:
Assuming you have a div with the ID "myDiv" and that you want to add to it the class "main__section" when the user clicks on it,
window.onload = init;
function init() {
document.getElementById("myDiv").onclick = addMyClass;
}
function addMyClass() {
var classString = this.className; // returns the string of all the classes for myDiv
var newClass = classString.concat(" main__section"); // Adds the class "main__section" to the string (notice the leading space)
this.className = newClass; // sets className to the new string
}
Read this Mozilla Developer Network article:
https://developer.mozilla.org/en/DOM/element.className
Since element.className property is of type string, you can use regular String object functions found in any JavaScript implementation:
If you want to add a class, first use String.indexOf in order to check if class is present in className. If it's not present, just concatenate a blank character and the new class name to this property. If it's present, do nothing.
If you want to remove a class, just use String.replace, replacing "[className]" with an empty string. Finally use String.trim to remove blank characters at the start and end of element.className.
Fixed the solution from #Paulpro
Do not use "class", as it is a reserved word
removeClass function
was broken, as it bugged out after repeated use.
`
function addClass(el, newClassName){
el.className += ' ' + newClassName;
}
function removeClass(el, removeClassName){
var elClass = el.className;
while(elClass.indexOf(removeClassName) != -1) {
elClass = elClass.replace(removeClassName, '');
elClass = elClass.trim();
}
el.className = elClass;
}
The solution is to
Shim .classList:
Either use the DOM-shim or use Eli Grey's shim below
Disclaimer: I believe the support is FF3.6+, Opera10+, FF5, Chrome, IE8+
/*
* classList.js: Cross-browser full element.classList implementation.
* 2011-06-15
*
* By Eli Grey, http://eligrey.com
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
/*global self, document, DOMException */
/*! #source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
(function (view) {
"use strict";
var
classListProp = "classList"
, protoProp = "prototype"
, elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
, objCtr = Object
, strTrim = String[protoProp].trim || function () {
return this.replace(/^\s+|\s+$/g, "");
}
, arrIndexOf = Array[protoProp].indexOf || function (item) {
var
i = 0
, len = this.length
;
for (; i < len; i++) {
if (i in this && this[i] === item) {
return i;
}
}
return -1;
}
// Vendors: please allow content code to instantiate DOMExceptions
, DOMEx = function (type, message) {
this.name = type;
this.code = DOMException[type];
this.message = message;
}
, checkTokenAndGetIndex = function (classList, token) {
if (token === "") {
throw new DOMEx(
"SYNTAX_ERR"
, "An invalid or illegal string was specified"
);
}
if (/\s/.test(token)) {
throw new DOMEx(
"INVALID_CHARACTER_ERR"
, "String contains an invalid character"
);
}
return arrIndexOf.call(classList, token);
}
, ClassList = function (elem) {
var
trimmedClasses = strTrim.call(elem.className)
, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
, i = 0
, len = classes.length
;
for (; i < len; i++) {
this.push(classes[i]);
}
this._updateClassName = function () {
elem.className = this.toString();
};
}
, classListProto = ClassList[protoProp] = []
, classListGetter = function () {
return new ClassList(this);
}
;
// Most DOMException implementations don't allow calling DOMException's toString()
// on non-DOMExceptions. Error's toString() is sufficient here.
DOMEx[protoProp] = Error[protoProp];
classListProto.item = function (i) {
return this[i] || null;
};
classListProto.contains = function (token) {
token += "";
return checkTokenAndGetIndex(this, token) !== -1;
};
classListProto.add = function (token) {
token += "";
if (checkTokenAndGetIndex(this, token) === -1) {
this.push(token);
this._updateClassName();
}
};
classListProto.remove = function (token) {
token += "";
var index = checkTokenAndGetIndex(this, token);
if (index !== -1) {
this.splice(index, 1);
this._updateClassName();
}
};
classListProto.toggle = function (token) {
token += "";
if (checkTokenAndGetIndex(this, token) === -1) {
this.add(token);
} else {
this.remove(token);
}
};
classListProto.toString = function () {
return this.join(" ");
};
if (objCtr.defineProperty) {
var classListPropDesc = {
get: classListGetter
, enumerable: true
, configurable: true
};
try {
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
} catch (ex) { // IE 8 doesn't support enumerable:true
if (ex.number === -0x7FF5EC54) {
classListPropDesc.enumerable = false;
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
}
}
} else if (objCtr[protoProp].__defineGetter__) {
elemCtrProto.__defineGetter__(classListProp, classListGetter);
}
}(self));
}
Improved version of emil's code (with trim())
function hasClass(ele,cls) {
return !!ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}
function addClass(ele,cls) {
if (!hasClass(ele,cls)) ele.className = ele.className.trim() + " " + cls;
}
function removeClass(ele,cls) {
if (hasClass(ele,cls)) {
var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
ele.className = ele.className.replace(reg,' ');
ele.className = ele.className.trim();
}
}
function addClass(element, classString) {
element.className = element
.className
.split(' ')
.filter(function (name) { return name !== classString; })
.concat(classString)
.join(' ');
}
function removeClass(element, classString) {
element.className = element
.className
.split(' ')
.filter(function (name) { return name !== classString; })
.join(' ');
}
Just in case if anyone would like to have prototype functions built for elements, this is what I use when I need to manipulate classes of different objects:
Element.prototype.addClass = function (classToAdd) {
var classes = this.className.split(' ')
if (classes.indexOf(classToAdd) === -1) classes.push(classToAdd)
this.className = classes.join(' ')
}
Element.prototype.removeClass = function (classToRemove) {
var classes = this.className.split(' ')
var idx =classes.indexOf(classToRemove)
if (idx !== -1) classes.splice(idx,1)
this.className = classes.join(' ')
}
Use them like:
document.body.addClass('whatever') or document.body.removeClass('whatever')
Instead of body you can also use any other element (div, span, you name it)
add css classes: cssClassesStr += cssClassName;
remove css classes: cssClassStr = cssClassStr.replace(cssClassName,"");
add attribute 'Classes': object.setAttribute("class", ""); //pure addition of this attribute
remove attribute: object.removeAttribute("class");
A easy to understand way:
// Add class
DOMElement.className += " one";
// Example:
// var el = document.body;
// el.className += " two"
// Remove class
function removeDOMClass(element, className) {
var oldClasses = element.className,
oldClassesArray = oldClasses.split(" "),
newClassesArray = [],
newClasses;
// Sort
var currentClassChecked,
i;
for ( i = 0; i < oldClassesArray.length; i++ ) {
// Specified class will not be added in the new array
currentClassChecked = oldClassesArray[i];
if( currentClassChecked !== className ) {
newClassesArray.push(currentClassChecked);
}
}
// Order
newClasses = newClassesArray.join(" ");
// Apply
element.className = newClasses;
return element;
}
// Example:
// var el = document.body;
// removeDOMClass(el, "two")
https://gist.github.com/sorcamarian/ff8db48c4dbf4f5000982072611955a2