I am creating a few DOM elements dynamically like,
var anchorElement = jQuery('<a />',{text:property.text});
var liElement = jQuery('<li />',{"class":"navlink_"+i,id:"navlink_"+i});
anchorElement.on('click',property.fnctn);
liElement.append(anchorElement);
parentID.append(liElement);
Where property is a JSON object.
property.text is the text that I want to put into anchor element. (Works fine)
I want to attach a click event handler to that anchor element.
The function that needs to be bound to that element is specified in JSON and we can access it like
property.fnctn
The following line should bind the event handler to the anchor element.
anchorElement.on('click',property.fnctn);
This was not working so I tried converting it into string like,
anchorElement.on('click',property.fnctn.toString());
No Success...
When I click on this link, the error is logged in the console
The object has no method 'apply'.
What is the reason...???
I am able to get it working with a slight work around like
anchorElement.attr('onclick',property.fnctn+"()");
Above statement works, but I want to know why .on() API is not working.
Thanks :)
AÐitya.
Update:
Youve said that property.actfn is a string, "paySomeoneClick". It's best not to use strings for event handlers, use functions instead. If you want the function paySomeoneClick, defined in the string, to be called, and if that function is global, you can do this:
anchorElement.on('click',function(event) {
return window[property.fnctn](event);
});
That works because global functions are properties of the global object, which is available via window on browsers, and because of the bracketed notation described below.
If the function is on an object you have a reference to, then:
anchorElement.on('click',function(event) {
return theObject[property.fnctn](event);
});
That works because in JavaScript, you can access properties of objects in two ways: Dotted notation with a literal property name (foo.bar accesses the bar propety on foo) and bracketed notation with a string property name (foo["bar"]). They're equivalent, except of course in the bracketed notation, the string can be the result of an expression, including coming from a property value like property.fnctn.
But I would recommend stepping back and refactoring a bit so you're not passing function names around in strings. Sometimes it's the right answer, but in my experience, not often. :-)
Original answer:
(This assumed that property.fnctn was a function, not a string. But may be of some use to someone...)
The code
anchorElement.on('click',property.fnctn);
will attach the function to the event, but during the call to the function, this will refer to the DOM element, not to your property object.
To get around that, use jQuery's $.proxy:
anchorElement.on('click',$.proxy(property.fnctn, property));
...or ES5's Function#bind:
anchorElement.on('click',property.fnctn.bind(property));
...or a closure:
anchorElement.on('click',function(event) {
return property.fnctn(event);
});
More reading (on my blog):
Mythical methods
You must remember this
Closures are not complicated
Related
This doesn't work:
var s = '^foo';
console.log(['boot', 'foot'].some(s.match));
Uncaught TypeError: String.prototype.match called on null or undefined
But this does:
var s = '^foo';
console.log(['boot', 'foot'].some(function(i) { return i.match(s) }));
Why is this? I imagine somehow the String.prototype.match function is too "primitive" or something, but why exactly? Since I'm not using ES2015, the second version seems quite verbose. Is there an alternative?
EDIT
When I wrote the above, I actually got it backwards compared to my actual need, which was matching one string against a number of regexes. But thanks to the great answers and comments below, I get it: [/^foo/, /^boo/].some(''.match, 'boot').
Note: The value of this is determined by how the function is called! (exception: bound and arrow functions)
If you pass s.match to .some, then the function will be called with this set to the global object (e.g. window) not the string it "belongs" to.
I.e. it would be equivalent to this:
String.prototype.match.call(window, 'foo')
This cannot work because this has to refer to a string object.
You could solve this by binding the function to a specific this value:
['boot', 'foot'].some(s.match.bind(s));
Learn more about this:
MDN - this
You Don't Know JS: this or That?
How to access the correct `this` context inside a callback?
A function value in Javascript does not bring its object along with it. The value of s.match is a plain function value, with no knowledge that you happened to find it attached to s. In fact, no matter what String you access it through, it's always the same function value:
"foo".match === "bar".match
//= true
When you call a function through an object, Javascript sets this to that object for the duration of the function call. But as soon as anything comes between retrieving the function value and calling it, any object association is lost.
You can create a function that does remember a specific this value using bind, as in #Felix King's answer. someFunction.bind(someObject) has approximately the same meaning as function(arg1, arg2,...) { return someObject.someFunction(arg1, arg2,...); }, but it automatically handles the number of parameters properly.
I have a site with both jQuery 1.8 and Prototype 2, using $ (dollar sign) seems to call jQuery but I want to invoke Prototype (from console). Specifically to do something like this: How to find event listeners on a DOM node when debugging or from the JavaScript code?
Ise there an Equivalent in Prototype like the jQuery() function?
Prototype is not a single object like jQuery, so you cannot invoke it like that.
Jquery
var jq = $('#id')
At this point you have a jQuery object that is referencing a DOM. If you try to use it like an actual DOM object, however, it won't work.
// This won't work
jq.className = "";
// This works because it's referencing the function inside jQuery
jq.removeClass();
Please note that jQuery can give you the actual DOM element if you need it, but this is not the default behavior.
Prototype
You need to think of Prototype as making base Javascript better as opposed to an object. A great example is Object.isUndefined. The Object object already exists in Javascript, Prototype is simply extending it with another function. When you see Prototype behaving like jQuery, it's almost always because Prototype extended what was already there
// This is a DOM reference
var pro = $('id'); // equivalent to document.getElementById('id');
pro.className = "";
// But there's no base Prototype object so this fails
pro.isUndefined();
// This is correct
Object.isUndefined(pro);
I am having some trouble searching google and stackoverflow for the answer to what this piece of JavaScript code is doing:
obj['e'+type+fn]( window.event );
To me this looks like an array element with an argument/parameter:
array[index](argument);
However, I can only guess at what this is doing. Is this equivalent to:
array[index]=argument
Which is assigning an argument to the array element?
If anyone could provide a simple/generic example of what this is doing that would be great.
I am attempting to decipher John Resig's addEvent() implementation. I'm not really looking for an explanation of this implementation or example related to it, but more like a dumbed-down example like MDC has done for call which uses some imagined products.
obj['e'+type+fn]( window.event );
This is just a way of accessing a property of an object. For instance, if you have an object
a = {
name: 'someName'
age: 20
};
You can access name by using a.name or, as above a['name'].
The reason he is using the [] notation is so that he can build the key from multiple strings.
Thus if type=click and fn=foo he's accessing obj.eclickfoo. Or obj['eclickfoo']
This property of the object must be a method as he's invoking it using (); so again, he's saying:
obj.eclickfoo( window.event );
or equivalent
obj['eclickfoo']( window.event );
This is what it is doing:
From the array obj, it takes the function with index 'e'+type+fn. It then executes it passing window.event as a parameter.
Remember that () invokes a function, and [] extract a value from an array.
obj['e'+type+fn] returns a function type. This is then executed with window.event as a parameter.
obj['e'+type+fn]( window.event );
Arrays can indeed use the "obj[...]" notation, but so can any object in JavaScript. And in this case, Resig is adding the property to any object, specifically for DOM objects.
obj['aVar'] is equivalent to obj.aVar. The advantage of the former is that it can also work with keywords which are reserved in JavaScript to have special meaning (e.g., obj['var'] if you defined a property called "var" on an object) and allows property names to be accessed dynamically, as in your example. Since type is a variable, you could not do obj.type since that would be finding a property exactly named "type", not finding a property with the name equal to the value of the type variable.
Since objects (or arrays) can hold functions as data, you can also use the invocation operator (the matching parentheses) on a function found inside of an object or array, as is done here--the property is accessed (which is a previously stored "method" or function on an object) and then invoked with the window.event object as a single argument.
Functions also have a built-in toString method on their prototype (which will get called in cases like this where you are are concatenating to a string, and therefore must want a string, as long as you don't set your own toString method on your function, since functions are also objects in JavaScript!). Resig's code is taking advantage of this, to define a new property, somewhat haphazardly which is normally a bad idea, but in a way which is pretty unlikely to clash with other applications also adding such a property.
So if document.body is the obj and if the type variable is set to "click" and "fn" is set to function () {alert('boo!');}", it will actually name a property on the document.body object as "eloadfunction () {alert('boo!');}". As he explains, creating this property (and then invoking it inside his own anonymous function), allows the function to be called with the normal behavior of any "this" keyword used inside--this will refer to the parent object, in this case obj, not to a global (unless obj is the global--i.e., the window object).
I'm trying to understand the format of the Javascript functions that jQuery, among other people, use.
For instance jQuery(arg).hide() or $("#obj").hide
I'd like to write similar format functions but I don't understand how.
I know how to write
function myFunc(args) {
}
but I don't understand the second part ie the .hide()
is that a function within a function?
thanks for any help
It's called method chaining. The way to achieve this is for your first function to return an object, so the second function can be called as a method on that object.
The standard way to do this style of programming is to always return the same type of object, so for example, jQuery always returns a jQuery object representing a collection of HTML nodes. If one of the calls modifies the collection then the next call will be on that collection. That's how you can do something like $('#myid').parent().hide();. $('#myid') returns a jQuery object representing the #myid element and .parent() returns a jQuery object representing the parent element of #myid. .hide() returns the same object, so you could then call another method on the same object if you wanted.
This is called method chaining. I highly recommend picking up Crockford's "JavaScript: The Good Parts". This is a very quick read but wonderfully explains OOP in JavaScript and identifies good versus bad language features. Highly recommend it.
As Skilldrick pointed out, this is called method chaining.
The most straightforward example for this is an object that returns itself when you call any of its methods:
var world = {
'hello': function() {
alert('Hello');
return this;
},
'goodbye': function() {
alert('Goodbye');
return this;
}
};
world.hello().goodbye();
This is identical to world.hello(); world.goodbye();.
jQuery does a little more than that. Calling the jQuery or $ function on a valid selector string will return a jQuery object representing the matched elements (it's not actually an array, though you could think of it as one). Most of its methods will return the object itself after modifying the object (e.g. $("a").css({...}) will apply changes to the styling of the matched elements and then return the set of matched elements again).
But some jQuery methods allow modifying the set you're working with (e.g. $("a").parent() will return a jQuery object representing the parents of the matched elements). That is, they don't return the same object, but an object that behaves identically.
You have to be careful if you decide to use this style, though, as the flow will break if you need a method that has a return value of its own (e.g. if you want calculations or getter methods). This can be avoided by passing a callback function to the method, but the resulting coding style may be terribly convoluted.
i am trying not to repeat the selector and get to its children via a the same objects parentElment declared variable.
I tried:
testimonialsBelt={
parentElment:$(".testimonialsCntnr ul"),
childrenElem:this.parentElment.children().length
}
I also tried:
testimonialsBelt={
parentElment:$(".testimonialsCntnr ul"),
childrenElem:$("testimonialsBelt.parentElment").children().length
}
but i keep on getting a undefined when calling alert(testimonialsBelt.childrenElem).
is there anyway to get the jquery object with object literals?
What is the rule? when can i use this and when must i have the full path? (in this case testimonialsBelt.parentElment).
i am trying to have all these variables in one object called testimonialsBelt. i know i can do this with loose javaScript.
Thanks
In object literals, you can only use this to refer to the object that you're declaring inside of a function. Try the following:
var testimonialsBelt = {
parentElment: $(".testimonialsCntnr ul"),
childrenElem: function() {
return this.parentElment.children().length;
}
};
The difference in calling childrenElem is that instead of using alert(testimonialsBelt.childrenElem), you would instead have alert(testimonialsBelt.childrenElem()).
Otherwise, this refers to the current scope that you are in (typically window if you are declaring the object literal as a global).
Addressing your edit: I'm not sure what you mean by "loose javascript," but I assume you mean as simple as possible. In which case, you can try the following, although I'm not a big fan of the method. It's more verbose, but is easy to understand.
var testimonialsBelt = {
parentElment: $(".testimonialsCntnr ul")
};
testimonialsBelt.childrenElem = parentElment.children().length;
This gives you an object where childrenElem is static (it doesn't change) and avoids calling $(".testimonialsCntnr ul") twice. However, if you expect $(".testimonialsCntnr ul").children() to change, then you will need to use my first example.
In JavaScript (not ECMAScript) you can use this:
testimonialsBelt={
parentElment:#1=$(".testimonialsCntnr ul"),
childrenElem:#1#.children().length
}