I have the code (inside one object)
onclick: this._addX.bind(this)
and then inside another object
onclick: this._addY.bind(this)
Now, _addX() and _addY are nearly identical, except they both end up calling (on the click event) a function with different argument values, say _addX calls foo('x') and _addY calls foo('y'). So I tried:
onclick: this._add.bind(this,'x') and
onclick: this._add.bind(this,'y') in the two objects. And of course I changed _add to accept an argument.
At runtime, when _add is called, it does not see any incoming arguments! I have fumbled around with different syntaxes but nothing works. Any ideas? The original syntax works fine (no arguments) but forces me to duplicate a large function with only one line different, which pains me. Thanks in advance.
_add: function(which) {
var me = this;
var checkFull = function(abk) {
if (abk.isFull) {
alert("full");
} else {
alert(which); // which is always undefined here!
}
};
getAddressBook(checkFull); //checkFull is a fn called by getAddressBook
},
this works and it keeps the scope within an element click event with the scope set to the class and not the element--there is no point in passing scope to the add method, it already has that:
var foo = new Class({
Implements: [Options],
add: function(what) {
alert(what);
},
initialize: function(options) {
this.setOptions(options);
this.options.element.addEvents({
click: function() {
this.add(this.options.what);
}.bind(this)
});
}
});
window.addEvent("domready", function() {
new foo({
element: $("foo"),
what: "nothin'"
});
});
just make an element with id=foo and click it to test (alerts nothin'). if your onclick is a function / event handler within your class as opposed to a normal element click event, then things are going to differ slightly - post a working skeleton of your work on http://mootools.net/shell/
If you read my previous answer, disregard it. The MooTools .bind method supports passing parameters. So something else isn't working as you expect:
onclick: this._add.bind(this, 'y');
Here is a simple setup on JSBin to show how bind truly does pass parameters.
The only purpose of bind is to "tell" the JS what object you mean when you say this. i.e. you pass as a parameter to bind an instance of the object you wish the this key word will refer to inside the function you used the bind on.
Related
Say I have this structure:
File1.js:
var myObject = {
bindEvents: function() {
console.log('Root events binding');
}
}
keyboard-object.js:
myObject.bindEvents: function() {
console.log('Keyboard events binding');
}
mouse-object.js:
myObject.bindEvents: function() {
// extends original bindEvents and adds more functionality
// right now this behavior overrides the original bindEvents method
console.log('Mouse events binding');
}
How can I trigger myObject.bindEvents() and make sure it is fired in each file?
My purpose is to split one big object into separate files and make one method that fires all the corresponding method(s) in each file, as in bindEvents should trigger bindEvents (or keyboardEvents in my case) in the object
You are essentially after a custom events handler for objects here. I would take a look at the one that Backbone.js supplies.
Be ware that you are not assigning an anonymous function to (earlier declared) myObject properties with the described syntax (as in):
myObject.bindEvents: function() {
console.log('Keyboard events binding');
}
What you are doing here is actually labeling the anonymous function with the word "myObject.bindEvents" which doesn't do you any good.
I think that - aside from this error - you are trying to do something like this:
myObject.bindEvents = function() {
console.log('Keyboard events binding');
}
And only Now your myObject has a property method of bindEvents which than you can invoke by simply declaring:
myObject.bindEvents(); later on your script.
Ok eventually I decided to use two (or more) seperate objects and trigger methods by firing events which are set on the document.
So my current solution is this:
myObject.js
var myObject = {
bindEvents: function() {
// some code here
$(document).trigger('myObject:bindEvents');
}
}
secondObject.js
var secondObject = {
bindEvents: function() {
// separate code and logic here
}
}
$(document).on('myObject:bindEvents',function(){
secondObject.bindEvents();
});
This gives me the flexibility to add more separate objects and bind their methods to events which are fired by the myObject object.
I'm working with jQuery and trying to apply some basic Javascript OOP principles to a set of functions that control hover behavior. However, I can't figure out how to get the "this" keyword to refer to the instance of the object I'm creating. My sample code is:
var zoomin = new Object();
zoomin = function() {
// Constructor goes here
};
zoomin.prototype = {
hoverOn: function() {
this.hoverReset();
// More logic here using jQuery's $(this)...
},
hoverReset: function() {
// Some logic here.
}
};
// Create new instance of zoomin and apply event handler to matching classes.
var my_zoomin = new zoomin();
$(".some_class").hover(my_zoomin.hoverOn, function() { return null; });
The problematic line in the above code is the call to this.hoverReset() inside the hoverOn() function. Since this now refers to element that was hovered on, it does not work as intended. I would basically like to call the function hoverReset() for that instance of the object (my_zoomin).
Is there any way to do this?
Only assigning a function to a property of an object does not associated this inside the function with the object. It is the way how you call the function.
By calling
.hover(my_zoomin.hoverOn,...)
you are only passing the function. It will not "remember" to which object it belonged. What you can do is to pass an anonymous function and call hoverOn inside:
.hover(function(){ my_zoomin.hoverOn(); },...)
This will make the this inside hoverOn refer to my_zoomin. So the call to this.hoverReset() will work. However, inside hoverOn, you will not have a reference to the jQuery object created by the selector.
One solution would be to pass the selected elements as parameter:
var zoomin = function() {
// Constructor goes here
};
zoomin.prototype = {
hoverOn: function($ele) {
this.hoverReset($ele);
// More logic here using jQuery's $ele...
},
hoverReset: function($ele) {
// Some logic here.
}
};
var my_zoomin = new zoomin();
$(".some_class").hover(function() {
my_zoomin.hoverOn($(this)); // pass $(this) to the method
}, function() {
return null;
});
As a next step, you could consider making a jQuery plugin.
You can "bind" the event handler to the object (see Mootools bind code for example).
You can pass the object as a parameter in the anonymous function and use that instead of this in the event handler
As for 1, you add the bind method to function
bind: function(bind){
var self = this,
args = (arguments.length > 1) ? Array.slice(arguments, 1) : null;
return function(){
if (!args && !arguments.length) return self.call(bind);
if (args && arguments.length) return self.apply(bind, args.concat(Array.from(arguments)));
return self.apply(bind, args || arguments);
};
}
Not sure though how well it will interact with JQ stuff.
please see my answers to these questions:
where is my "this"?
why is "this" not this?
this confusion comes up all the time.
when you pass a function in as a callback, it's invoked as a standalone function, so its "this" becomes the global object.
"bind" is a native part of ecmascript 5, and is part of the function prototype. If you go to the end of my second answer up there, you get a link to the mozilla website, which has a "compatibility" version of the bind function. Use use myfunction.bind(myobject), and it'll use the native function if it's available, or the JS function if it is not.
I have a function which is a JQuery event handler. Because it is a JQuery event handler, it uses the this variable to refer to the object on which it is invoked (as is normal for that library).
Unfortunately, I need to manually call that method at this point. How do I make this inside the called function behave as if it were called from JQuery?
Example code:
function performAjaxRequest() {
//Function which builds AJAX request in terms of "this"
}
function buildForm(dialogOfForm) {
var inputItem;
dialogOfForm.html('...');
dialogOfForm.dialog('option', 'buttons', {
"Ok" : performAjaxRequest
});
inputItem = dialogOfForm.children(':not(label)');
//Redirect enter to submit the form
inputItem.keypress(function (e) {
if (e.which === 13) {
performAjaxRequest(); //Note that 'this' isn't the dialog box
//as performAjaxRequest expects here, it's
//the input element where the user pressed
//enter!
}
}
}
You can use the function's call method.
someFunction.call(objectToBeThis, argument1, argument2, andSoOnAndSoOn);
If dialog is the object that you need to be set to this then:
performAjaxRequest.apply(dialog, []);
// arguments (instead of []) might be even better
should do the trick.
Otherwise, in jQuery you can simply call the trigger method on the element that you want to have set to this
Say, for example, that you wanted to have a click event happen on a button and you need it to happen now. Simply call:
$("#my_button").trigger("click");
Your #my_button's click handler will be invoked, and this will be set to the #my_button element.
If you need to call a method with a different this ... say for example, with this referring to the jQuery object itself, then you will want to use call or apply on your function.
Chuck and meder have already given you examples of each ... but to have everything all in one place:
// Call
my_function.call(object_to_use_for_this, argument1, argument2, ... argumentN);
// Apply
my_function.apply(object_to_use_for_this, arguments_array);
SEE: A List Apart's Get Out of Binding Situations
Are you looking for..
functionRef.apply( objectContext, arguments);
You should of course learn to master call() and apply() as people have stated but a little helper never hurts...
In jQuery, there is $.proxy. In pure js, you can re-create that niftyness ;) with something like:
function proxyFn( fn , scope ){
return function(){
return fn.apply(scope,arguments);
}
}
Usage Examples:
var myFunctionThatUsesThis = function(A,B){
console.log(this,arguments); // {foo:'bar'},'a','b'
};
// setTimeout or do Ajax call or whatever you suppose loses "this"
var thisTarget = {foo: 'bar'};
setTimeout( proxyFn( myFunctionThatUsesThis, thisTarget) , 1000 , 'a', 'b' );
// or...
var contextForcedCallback = proxyFn( myAjaxCallback , someObjectToBeThis );
performAjaxRequest(myURL, someArgs, contextForcedCallback );
If you dont abuse it, it's a sure-fire tool to never loose the scope of "this".
use a closure
i.e assign this to that early on; then you can do what you like with it.
var that = this;
Note: This question uses jQuery but the question has nothing to do with jQuery!
Okay so I have this object:
var box = new BigBox();
This object has a method named Serialize():
box.AddToPage();
Here is the method AddToPage():
function AddToPage()
{
$('#some_item').html("<div id='box' onclick='this.OnClick()'></div>");
}
The problem above is the this.OnClick() (which obviously does not work). I need the onclick handler to invoke a member of the BigBox class. How can I do this?
How can an object refer to itself in an event handler?
You should attach the handler using jQuery:
function AddToPage()
{
var self = this;
$('#some_item').empty().append(
$("<div id='box'></div>")
.click(function() { self.OnClick(someParameter); })
);
}
In order to force the event handler to be called on the context of your object (and to pass parameters), you need to add an anonymous function that calls the handler correctly. Otherwise, the this keyword in the handler will refer to the DOM element.
Don't add event handlers with inline code.
function AddToPage()
{
$('#some_item').html("<div id='box'></div>");
$('#box').click(this.OnClick);
}
EDIT:
Another way (avoids the extra select):
function AddToPage()
{
var div = $('<div id="box"></div>'); // probably don't need ID anymore..
div.click(this.OnClick);
$('#some_item').append(div);
}
EDIT (in response to "how to pass parameters");
I'm not sure what params you want to pass, but..
function AddToPage()
{
var self = this, div = $('<div></div>');
div.click(function (eventObj) {
self.OnClick(eventObj, your, params, here);
});
$('#some_item').append(div);
}
In jQuery 1.4 you could use a proxy.
BigBox.prototype.AddToPage= function () {
var div= $('<div>', {id: box});
div.click(jQuery.proxy(this, 'OnClick');
div.appendTo('#some_item');
}
You can also use a manual closure:
var that= this;
div.click(function(event) { that.OnClick(event); });
Or, most simply of all, but requiring some help to implement in browsers that don't yet support it (it's an ECMAScript Fifth Edition feature):
div.click(this.OnClick.bind(this));
If you are using jQuery, then you can separate your code from your markup (the old seperation of concerns thing) like this
$(document).ready(function() {
var box = new BigBox();
$('#box').click(function() {
box.serialize();
});
});
You only need to add the click handler once for all divs with id of box. And because the click is an anonymous function, it gets the scope of the function it is placed in and therefore access to the box instance.
The following doesn't work... (at least not in Firefox: document.getElementById('linkid').click() is not a function)
<script type="text/javascript">
function doOnClick() {
document.getElementById('linkid').click();
//Should alert('/testlocation');
}
</script>
<a id="linkid" href="/testlocation" onclick="alert(this.href);">Testlink</a>
You need to apply the event handler in the context of that element:
var elem = document.getElementById("linkid");
if (typeof elem.onclick == "function") {
elem.onclick.apply(elem);
}
Otherwise this would reference the context the above code is executed in.
The best way to solve this is to use Vanilla JS, but if you are already using jQuery, there´s a very easy solution:
<script type="text/javascript">
function doOnClick() {
$('#linkid').click();
}
</script>
<a id="linkid" href="/testlocation" onclick="alert(this.href);">Testlink</a>
Tested in IE8-10, Chrome, Firefox.
To trigger an event you basically just call the event handler for that
element. Slight change from your code.
var a = document.getElementById("element");
var evnt = a["onclick"];
if (typeof(evnt) == "function") {
evnt.call(a);
}
Granted, OP stated very similarly that this didn't work, but it did for me. Based on the notes in my source, it seems it was implemented around the time, or after, OP's post. Perhaps it's more standard now.
document.getElementsByName('MyElementsName')[0].click();
In my case, my button didn't have an ID. If your element has an id, preferably use the following (untested).
document.getElementById('MyElementsId').click();
I originally tried this method and it didn't work. After Googling I came back and realized my element was by name, and didn't have an ID. Double check you're calling the right attribute.
Source: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click
$("#linkid").trigger("click");
Old thread, but the question is still relevant, so...
(1) The example in your question now DOES work in Firefox. However in addition to calling the event handler (which displays an alert), it ALSO clicks on the link, causing navigation (once the alert is dismissed).
(2) To JUST call the event handler (without triggering navigation) merely replace:
document.getElementById('linkid').click();
with
document.getElementById('linkid').onclick();
Have a look at the handleEvent method
https://developer.mozilla.org/en-US/docs/Web/API/EventListener
"Raw" Javascript:
function MyObj() {
this.abc = "ABC";
}
MyObj.prototype.handleEvent = function(e) {
console.log("caught event: "+e.type);
console.log(this.abc);
}
var myObj = new MyObj();
document.querySelector("#myElement").addEventListener('click', myObj);
Now click on your element (with id "myElement") and it should print the following in the console:
caught event: click
ABC
This allows you to have an object method as event handler, and have access to all the object properties in that method.
You can't just pass a method of an object to addEventListener directly (like that: element.addEventListener('click',myObj.myMethod);) and expect myMethod to act as if I was normally called on the object. I am guessing that any function passed to addEventListener is somehow copied instead of being referenced. For example, if you pass an event listener function reference to addEventListener (in the form of a variable) then unset this reference, the event listener is still executed when events are caught.
Another (less elegant) workaround to pass a method as event listener and stil this and still have access to object properties within the event listener would be something like that:
// see above for definition of MyObj
var myObj = new MyObj();
document.querySelector("#myElement").addEventListener('click', myObj.handleEvent.bind(myObj));
If you're using this purely to reference the function in the onclick attribute, this seems like a very bad idea. Inline events are a bad idea in general.
I would suggest the following:
function addEvent(elm, evType, fn, useCapture) {
if (elm.addEventListener) {
elm.addEventListener(evType, fn, useCapture);
return true;
}
else if (elm.attachEvent) {
var r = elm.attachEvent('on' + evType, fn);
return r;
}
else {
elm['on' + evType] = fn;
}
}
handler = function(){
showHref(el);
}
showHref = function(el) {
alert(el.href);
}
var el = document.getElementById('linkid');
addEvent(el, 'click', handler);
If you want to call the same function from other javascript code, simulating a click to call the function is not the best way. Consider:
function doOnClick() {
showHref(document.getElementById('linkid'));
}
In general I would recommend against calling the event handlers 'manually'.
It's unclear what gets executed because of multiple registered
listeners
Danger to get into a recursive and infinite event-loop (click A
triggering Click B, triggering click A, etc.)
Redundant updates to the DOM
Hard to distinguish actual changes in the view caused by the user from changes made as initialisation code (which should be run only once).
Better is to figure out what exactly you want to have happen, put that in a function and call that manually AND register it as event listener.