var Helloworld = {
onLoad: function() {
// initialization code
this.initialized = true;
},
onMenuItemCommand: function() {
window.open("chrome://helloworld/content/hello.xul", "", "chrome");
}
};
window.addEventListener("load", function(e) { Helloworld.onLoad(e); }, false);
http://kb.mozillazine.org/Getting_started_with_extension_development
I don't understand the function(e) { Helloworld.onLoad(e); part. I think it passes an event parameter e to the onLoad function, but the onLoad function doesn't have onLoad: function(e) {} to receive e, so what's going on?
Just defines an anonymous function: the said function will will be called when the event load is triggered.
Note that in JavaScript, the function declaration isn't strict. One can call a function with parameters even if the declaration doesn't explicitly show such. In other words, there is no such thing as a "function signature" (like in Java, C++ etc.). The JavaScript interpreter will only call the "hasmethod" method on the object to determine if "method X" is implemented.
var Helloworld = {
// parameters can be sent to "onload" but they will be ignored.
onLoad: function() {
// initialization code
this.initialized = true;
},
onMenuItemCommand: function() {
window.open("chrome://helloworld/content/hello.xul", "", "chrome");
}
};
// attach an event handler to the event "load". Pass the event variable "e"
// even though the receiving function will ignore it.
window.addEventListener("load", function(e) { Helloworld.onLoad(e); }, false);
You can change the onLoad if you want to have a parameter, it's just an example onLoad. It's JavaScript, after all, not C/C++ etc.
Related
When I bind a function with the parent this passed in thisArg, I can't unbind the same named function expression, but without it I can unbind the function itself. Why is that?
This works:
choicesList.addEventListener("click", function() {
const self= this;
document.addEventListener("click", function checkClick(e) {
if (!e) e = event;
if (!self.contains(e.target)) {
document.removeEventListener("click", checkClick);
}
}, false);
});
This doesn't:
choicesList.addEventListener("click", function() {
document.addEventListener("click", function checkClick(e) {
if (!e) e = event;
if (!this.contains(e.target)) {
document.removeEventListener("click", checkClick);
}
}.bind(this), false);
});
The reason for this is issue is that calling bind() on a function returns a new instance of that function:
function someHandler() {
alert('hi');
}
const someHandlerBinded = someHandler.bind(document);
// Returns false, seeing as these are different instances of the function
console.log( someHandlerBinded === someHandler );
By setting an event handler directly, via the result of bind() as you are in your second block of code, this causes a new instance of that function handler to be passed to addEventListener(). This in turn means that the subsequent attempt to removing this handler on line:
document.removeEventListener("click", checkClick);
will fail, seeing that the the defined function checkClick is not the same as the actual handler function used for that click event (ie the new function instance returned from function checkClick(){ ... }.bind())
One way to resolve this might be the following:
choicesList.addEventListener("click", function() {
// Declare the bound version of the click handler
const boundClickHandler = function checkClick(e) {
if (!e) e = event;
if (!this.contains(e.target)) {
// Removing the result of bind, rather than the declared
// checkClick handler
document.removeEventListener("click", boundClickHandler);
}
}.bind(this)
// Adding the result of bind as you currently are doing
document.addEventListener("click", boundClickHandler, false);
});
It's because this is in a function that is nested within another function and the nested function doesn't have the same invocation context as the outer one. The first one works because you are caching the object that the outermost this is referencing and you are then able to correctly reference it in the inner function.
You can read more about the volatility of this here.
I'm looking for a way to achieve the following. I could build some mechanism to do this, I'm asking for something built-in or a really simple way, if it exists and I'm missing it.
Edit:
Please note that I'm talking about events in random objects, not in DOM elements. The events order cannot be managed using the parent, etc. as suggested in the possible duplicate.
Edit 2:
Maybe an after-all-handlers-have-been-called callback? Or an always-last-to-be-executed handler?
Given:
var someObject={};
$(someObject).on("event",function() { console.log('Default handler'); });
...
$(someObject).on("event",function() { console.log('Other handler'); });
When doing:
$(someObject).triggerHandler("event");
The output is:
Default handler
Other handler
We all know this. The problem is: What if I would want to make the first event the "default" handler, to be executed if there aren't other event handlers (not a problem there) or if the other handlers didn't stop the event propagation (here is the problem).
I'm looking for a way to be able to do something like:
$(someObject).on("event",function(ev) { ev.preventDefault(); });
and prevent the first event handler from executing. In this example is not working given the execution order. Reversing the execution order is not the correct way to do it.
TL;DR
Is it possible to set a default handler, one to be called in case there's no other handlers and the event hasn't been canceled?
Edit 3: To give you a better idea, the current approach is the following (names are made up for this example):
var eventCanceled=false;
function doTheEvent() {
$(someObject).triggerHandler("event");
if(!eventCanceled) defaultEventHandler();
}
//To be called inside the handlers to stop the default handler
function cancelTheEvent() {
eventCanceled=true;
}
I just want to get rid of this and be able to use triggerHandler directly.
Hope this is what you are looking for. This is called observer pattern.
var someObj = {};
someObj.eventCallbackList = {};
someObj.createEventObject = function(name) {
return {
type: name,
preventDefault: function() {
this.callDefault = false;
},
callDefault: true
}
}
someObj.on = function(eventName, callback, defaultFlag) {
if (!this.eventCallbackList[eventName]) {
// there can be multiple other handlers
this.eventCallbackList[eventName] = {
other: [],
default: null
};
}
if (defaultFlag) {
this.eventCallbackList[eventName]['default'] = callback;
} else {
this.eventCallbackList[eventName]['other'].push(callback);
}
}
someObj.triggerHandler = function(eventName) {
var event = this.createEventObject(eventName);
var callbacks = this.eventCallbackList[eventName];
if (callbacks) {
if (callbacks['other']) {
for (var i = 0; i < callbacks['other'].length; i++) {
callbacks['other'][i](event);
}
}
if (event.callDefault && callbacks['default']) {
callbacks['default'](event);
}
}
}
// Test
someObj.on('myCustomEvent', function(event) {
console.log('OtherHandler');
event.preventDefault();
});
someObj.on('myCustomEvent', function(event) {
console.log('default');
}, true);
$(document).on('click', function() {
someObj.triggerHandler('myCustomEvent');
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
you can add a parameter to set what codes you want to execute inside your triggerHandler function.
you can refer to this thread in adding parameter.
jQuery: receive extraParameters from triggerHandler()
While there is a way to detect if the preventDefault() was called, there is no such thing as a "default handler". Event handlers are executed in the order they are registered. So the first one registered is the first one executed.
If your "default handler" doesn't need to be executed synchronously, you could delay the "default handler" to be executed after all the other handlers are done and in any of the other handlers revert that if necessary:
var defaultHandlerTimeout;
$element.on('event', function() {
defaultHandlerTimeout = setTimeout(function() {
// your actual handler
});
});
$element.on('event', function() {
// prevent the "default handler" from being executed
clearTimeout(defaultHandlerTimeout);
});
jQuery does store all of it's events internally and you can access specific element events using $._data(elem,'events').
Whether this will help your situation or not I'm not sure but is worth digging into. Personally i've never found the need to use it.
See https://stackoverflow.com/a/2518441/1175966
I was wondering if an event listener can be removed by nullifying its callback function?
Simplified Example:
var somefunction = function() {
// some code
}
window.addEventListener(eventType, somefunction, false);
Now, will setting somefunction = null; remove the above EventListener, or will it simply turn it into a zombie EventListener?
The actual code is used in a Firefox (overlay-type) Addon and I was contemplating alternative methods of (automatically) removing EventListeners on unload event, other than the obvious:
window.removeEventListener(eventType, somefunction, false);
Update: Please note that this is part of a code for a Firefox addon. The eventType in this instance is 'popupshowing' which can not be nullified as it would disrupt browser functions.
Thank you in advance for your help
Looking forward to (alternative) suggestions
removeEventListener is the way to go.
Also, you don't actually nullify the function by setting some variable to null. The variable has assigned just a reference (to a non-POD object, like a function). To illustrate this, consider the following:
var a = function() { alert("called"); };
setTimeout(a, 1000); // Will still alert!
var b = a;
a = null; // What you called `nullify`....
console.log(this.a, "a" in this); // null, true
delete this.a; // Actually remove the property from the global scope.
// `a` is really dead at this point!
b(); // will still alert, too.
If you want to avoid some removeEventListener calls, I'd use some helper function(s):
let { addEventListenerUnload, removeEventListenerUnload } = (function() {
let tracked = [];
addEventListener("unload", function removeTracked() {
removeEventListener("unload", removeTracked);
for (let t of tracked) {
try {
removeEventListener(t.type, t.fn, t.capture);
}
catch (ex) {}
}
tracked.length = 0;
});
return {
addEventListenerUnload: function(type, fn, capture) {
addEventListener(type, fn, capture);
tracked.push({type: type, fn: fn, capture: capture});
},
removeEventListenerUnload: function(type, fn, capture) {
tracked = tracked.filter(e => e.type != type || e.fn != fn || e.capture != capture);
removeEventListener(type, fn, capture);
}
};
})();
(Includes some ECMA-6 stuff that Firefox supports, but which you could easily convert. Also, removeEventListenerUnload might not be needed at all, so you might omit it. Also, when using this in overlay script make sure to give it unique names to avoid clashes with other code).
i dont think setting callback function to null will remove eventlistener, you'll still have eventlistener attached, you could use removeEventListener or set the eventType to null, like:
window.eventType = null;
This question already has answers here:
Adding console.log to every function automatically
(6 answers)
Closed 8 years ago.
I am trying to debug a large amount of JS Code inside a JS Object. There are somewhere around 150 functions inside this JS Object that are called individually through a separate script.
JS Object Example
var OmnitureCall = {
reportA: function() {
/* random actions */
},
reportB: function() {
/* random actions */
}
...
};
Other JS File Calling Object
OmnitureCall.reportA(...);
Somewhere in an external JS file, multiple reportA's are happening when only one is supposed to happen, which is why I would like to debug the main object and see when various report functions are being called and see where the double event is fired. However, the only way I can see doing this so far would to have a main function inside the OmnitureCall object that acts as a "handler" for all calls, and does some basic debugging and then runs the function that was called.
Example of JS Object Handler
var OmnitureCall = {
handler: function(callback) {
console.log('something is called');
if(typeof(callback) === "function") {
callback();
}
},
reportA: function() {
this.handler(function(){
/* random actions */
});
},
reportB: function() {
this.handler(function(){
/* random actions */
});
}
...
};
The downsides:
There are 100+ functions I would have to copy and paste this.handler too and fix up
In a majority of those functions the 'this' keyword is used to reference other functions within that OmnitureCall object, and I am worried the context of that referenced 'this' will be lost if it is all wrapped as a callback function and then called.
So my question to any JS devs, is there a way I can attach a function to this object that will always be called prior to whatever function was actually called (keep in mind I am also trying to document that name of said function that is being called so I can figure out what is being fired twice).
If that is not possible and the handler function idea is the only thing that may work, does anyone know how to retain the context of 'this' referring to the object as a whole if the function is passed as a parameter to handler and then called?
Much thanks..
Proxies are what you're looking for, but they are not widely implemented - thus I wouldn't recommend it just yet. But for future's sake, this is what it'd look like:
// Your original object
var OmnitureCall = {
reportA: function() {
console.log(arguments, 'hello from report A');
},
reportB: function() {
console.log(arguments, 'hello from report B');
}
// ...
};
// Create our proxy
var OmnitureCall = new Proxy(OmnitureCall,
{
// Since we want to see the calls to methods, we'll use get
get: function(proxy, property)
{
// Tell us what we're calling
console.log('calling ' + property);
// Return it if it exists
return proxy[property] || proxy.getItem(property) || undefined;
}
});
// Returns "calling reportA", ["test", "foo"], "hello from report A":
OmnitureCall.reportA('test', 'foo');
// Returns "calling reportB", [["array", "of", "args"]], "hello from report B":
OmnitureCall.reportB(['args', 'is', 'an', 'array']);
While Brett's code should work, you'd need to change all of the calls to the object. E.g. you can't do OmnitureCall.reportA(); anymore. It'd need to be OmnitureCall.handler('reportA'). You may or may not have control over this, or it may prove too difficult to change all of the references.
Using your original handler setup you can indeed pass the this reference using apply or call:
var OmnitureCall = {
handler: function(callback, args) {
if(typeof(callback) === "function") {
callback.apply(this, args);
}
},
reportA: function() {
this.handler(function(){
console.log(this);
});
},
reportB: function() {
this.handler(function(){
console.log(arguments);
}, arguments);
}
// ...
};
// Outputs Object {handler: function, reportA: function, reportB: function}
OmnitureCall.reportA();
// Outputs "test", ["foo", 1]
OmnitureCall.reportB('test', ['foo', 1]);
You can do something like this http://jsfiddle.net/L4Z8U/ and just call the functions with the handler.
window.OmnitureCall = {
handler: function(callback) {
console.log('something is called',typeof(this[callback]));
if(typeof(this[callback]) === "function") {
this[callback]();
}
},
reportA: function() {
console.log('reportA fired');
},
reportB: function() {
console.log('reportB fired');
}
};
Edit: Ive doen this before using "this" in this context with no issues
In the following code, where would I use preventDefault? I have been testing it to make it work but have not found a good way to cancel the default submit action. Which function needs to be capture the event object?
Here is the code:
f_name = 'someFunc' ;
f_args(1,2,3) ;
if(element.addEventListener) {
element.addEventListener('submit',(function(name,args,element) {
return function() {
window[name].apply(element,args) ;
}
})(f_name,f_args,element)
,false) ; }
f_name = 'someFunc';
f_args = [1, 2, 3];
if (element.addEventListener) {
element.addEventListener('submit', (function (name, args, element) {
return function (e) {
// here
e.preventDefault();
window[name].apply(element, args);
};
})(f_name, f_args, element), false);
}
Assuming you want to preventDefault on only some event handlers, you would use preventDefault() inside the callback that you use to handle a specific event. In your example, this would the global function represented by the name argument which would be the someFunc global function.
If you wanted to do it for all event handlers (which doesn't make a lot of sense to me), then you'd do it in your code right before or after you call window[name].
Your handler function will be called with the event as a parameter
return function(evt) {
evt.preventDefault();
window[name].apply(element,args) ;
}