From within a function like
function eventHandler(e) {
// ...
}
is there a reliable and efficient way to determine whether e is a DOM event?
I don't believe there is a reliable way to determine if a given object is NOT a DOM event.
typeof e will always return 'object' for genuine Event objects, which is not helpful.
Any property that you might check for on the object can exist in both a genuine Event object or any non-Event object.
You might think that the prototype chain could be of use in determining this, but it has the same problem as #2 (can easily be replicated).
The contructor property might seem promising, but one could do this:
function DummyEvent(){
this.constructor.toString = function(){
return "function MouseEvent() { [native code] }";
}
}
This ends up making console.log(e.constructor) print "function MouseEvent() { [native code] }"
So, is there a "reliable" way to tell if an object is an event? No.
Edit — Note that all of this is irrelevant if you want to prevent event spoofing as you can create real events easily.
var evt = new Event('click');
var evt2 = document.createEvent('MouseEvents');
evt2.initMouseEvent('click', ...);
//etc
Edit2 — I've created a test jsFiddle trying to find some way to distinguish objects, but I haven't found anything definite yet. Note that I didn't bother to specify properties on the DummyEvent because those can obviously easily be spoofed.
Perhaps if the event bubbles event.bubbles up through the DOM it's DOM event.
But even if that statement is true its propagation might be stopped.
UPDATED :
OK the property you are looking for is e.target and for IE e.srcElement.
Both properties return the HTML element the event took place on which defines it as
DOM event.
However perhaps you should specify what you interpret as DOM event.
Do you mean browser's native events, because this is DOM event too:
var zoo = jQuery('#zoo');
zoo.bind('moo');
zoo.trigger('moo');
It is binded to a DOM node
In jquery I created a testing function, something like this:
function isEvent(e) {
try {
e.PreventDefault();
return true;
}
catch (err) {
return false;
}
}
and testing code:
var o = new Object();
var e = $.Event('click');
alert(isEvent(o));
alert(isEvent(e));
the former alert shows false, while the latter shows true. See fiddle.
UPDATE: It's safer to call something like preventDefault instead of trigger.
Related
HTML:
<button onclick="foo(event);">Test</button>
Javascript:
window.foo = function(event) {
console.log(JSON.stringify(event));
}
Console Result:
{"isTrusted":true}
It is happening on Chrome. I haven't tested other browsers yet.
There are a number of reasons why some properties do not get included in JSON.stringify:
They might be functions, which cannot be stringified
They might belong to the prototype (i.e. class) of an object, rather than directly belonging to the object itself.
If you need to include extra data, your best bet is to manually construct a fresh object with the things you want to include:
window.foo = function(event) {
console.log(JSON.stringify({keyCode: event.keyCode));
}
The event itself is full of prototype functions - which are getting hidden by stringify (as ForbesLindesay already pointed out).
Otherwise it's not common to see onclick in HTML markup (since it generates very tight dependencies with your code).
Most browsers (beyond IE7) also allow you unpacking of values inside the console - which you can see here: http://jsfiddle.net/djf2nxwd/14/
document.getElementById('foo').onclick = (event) => {
console.log(JSON.stringify(event));
console.log(event);
};
That's probably the behaviour you expected.
I'm using a function to create other functions that will be used on an document event handler so the signature of the returned functions must match that of the event handler, eg. function (event, ui).
The code is as follows
function createEventHandler(refFn, additionalMods) {
var createdEvent = function (event, ui) {
// Merge the properties of additionalMods with an object { event, ui }
// call function with refFn and the resulting object as parameters
}
createdEvent.ready = true;
return createdEvent;
}
I removed the code of the generated function for clarity but inside the refFn and additionalMods variables are processed inside.
Now when processing the user input I call the following line
var handler = events[i].handler.ready ?
events[i].handler :
createEventHandler(events[i].handler);
Basically process an array of data that each has a property called handler which is either a function or the result of calling createEventHandler.
The bottom line is that if the function in the array has been processed then pass this function 'as is', if not process the function and store the result so in the end all the functions are processed.
Right now i'm attaching a property called ready to signal that the function was processed as it turns out there is no reliable method to obtain the function's name according to this post but this doesn't feel right.
I also tried to use the prototype for comparison but this doesn't work because a new function is created everytime and is inside a scope so I can not get a reference for comparison.
I even tried
events[i].handler.prototype == createEventHandler().prototype
but of course it didn't work.
Does anyone know how can i generate this functions and have a reliable way to compare them to know if they were generated by my code or not.
{Edit}
To add further clarification
All the code above is under the same scope meaning the code that process the array has visibility over the createEventHandler function. I can modify this code all I want what I cannot modify is the content of the array once is created. I have to iterate over it as it comes and generate or not based on if the work was done already.
The createEventHandler is also exposed to the user throught an API function. Let's say the user calls evt.generate('model') this will generate an event handler that does an specific work using the createEventHandler function under the hoods. If then you can call evt.generate('bind') another will be generated that does another work.
This is a lot of behaviour that is provided to the users by default but they can choose to add they custom behaviour if none of the predefined ones are fit for the task.
All the data is declared once but the content of the array is disparate because I can write the following and is supposed to work. I omitted most of the other irrelevant properties.
var events = [
{
event: 'pageshow',
handler: evt.generate('model')
},
{
event: 'pagebeforeshow',
handler: function (params, last) {
// My custom handler for this case
}
}
];
After looping the array all the handlers are in the same format and ready to be binded. The createEventHandler is necessary in all the cases because I use dependency injection to supply data for those parameters so it's basically "if not called already then call it and do al the dependency injection work" this is why I need to compare the functions.
I found an elegant solution and I post it here in case someone runs into the same problem.
The problem with my code is that an user car write a function with a property named ready which is a common name and a value of true which is also a common value and the processing will fail.
Maybe the user didn't write the property, maybe is present because is inherited from it's prototype. The goal is to try to be as certain as possible that the code you are processing was the output or your own functions or not, which in javascript is almost an impossible task.
The most accurate way that I found was when I was reading about Equality comparisons and sameness which tells me that an object is only equal to itself when you use the === equality operator and is not a primitive object. That is
undefined === undefined => true
null === null => true
"foo" === "foo" => true
0 === 0 => true
But
{a:1} === {a:1} => false
So you can write a property ready which is equal to an object and as you hold the reference to that object all the comparissons will fail if this property was not set by you.
This is good but it feels bad to have an extra property called ready with a random object just to compare, maybe there is a better way and yes, there is.
In javascript there are no classes but there is prototype inheritance so you can write a function and use one of the patterns of inheritance to set this function as the ancestor of yours and use that for comparisons.
var parentEvent = function () {};
// This is the function that will be used as parent
function createEventHandler(refFn, additionalMods) {
var createdEvent = function (event, ui) {
// Merge the properties of additionalMods with an object { event, ui }
// call function with refFn and the resulting object as parameters
}
//createdEvent.ready = true This is no longer necessary
// This is the "sharing prototype" inheritance pattern
createdEvent.prototype = parentEvent.prototype
return createdEvent;
}
Now the prototype of your returned function is pointing to a function that you hold in a variable. Then you can compare them using
// Replace the property comparison with the prototype comparison
var handler = events[i].handler.prototype === parentEvent.prototype ?
events[i].handler :
createEventHandler(events[i].handler);
This is not fail proof, I know, but is good enough for most cases.
{Edit}
Thank's to #Bergi for pointing out that this is not inheritance in the strict sense of the word. The reason for that is that most javascript inheritance patterns demand that you use constructor functions and I'm using a factory function here. To make it work you have to write something like this
function createEventHandler(refFn, additionalMods) {
// Same code as before
createdEvent.prototype = parentEvent.prototype
return new createdEvent();
}
And the comparison is done with
events[i].handler.__proto__ === parentEvent.prototype
Note the difference in the way the function is returned ant the way the new prototype property is accessed. This is good when you do have other properties that you want to return that are in the parent function.
Do the contents of the events array change during the execution of your program, aside from replacing them with the converted versions?
If not, a simple solution is just to make a copy of the handlers before you start converting them, and use that for the comparison:
// keep this somewhere that you can access it later
var origHandlers = events.map(function (e) { return e.handler; });
var handler = events[i].handler === origHandlers[i] ?
createEventHandler(events[i].handler) :
events[i].handler;
In jQuery you can call a function like this for example:
$("id").someFunction();
Now after looking at the codebase of jQuery it looks like the object being created by using $ has its protoype modified to return the function which was added via .fn, in my application I would like the same syntax only without requiring jQuery.
Another example of this kind of behavior is some of Javascript's in-built methods such as: .replace, .toLowerCase, .split, .toString, etc. I understand some of those listed methods are on the String.prototype object and extending in-built objects is bad practice (so I hear).
How am I able to add a function to the prototype of every "String" or "Object" that gets assigned. The reason I am doing this is I am trying to create a cross-browser implementation of attaching events without having to do if statements all of the time.
So instead of needing to go: if (el.addEventListener) or if (el.attachEvent) I would like to be able to go el.bindEvent which behind the scenes would be a prototype method behind the scenes that would do all of the checking for event binding, etc.
My advanced JS knowledge when it comes to assigning prototype methods and whatnot isn't that great, so your help in understanding and correcting anything I've said is appreciated.
Extending the prototype of DOM elements is an even worse idea than extending built-in objects †. Please have a look at the article "What's wrong with extending the DOM".
What jQuery provides is a simple wrapper around the DOM API, which is ok. The problem with extending the DOM is that
they are host objects, and might behave differently than how objects are defined in the ECMAScript spec and
older IE versions don't even expose the prototype of DOM nodes.
The reason I am doing this is I am trying to create a cross-browser implementation of attaching events without having to do if statements all of the time.
This alone is no reason to extend the DOM. You can define your own function which binds event handlers and you even have to test whether addEventListener or attachEvent exists only once on page load. Such a function could look like this:
var bindEvent = (function() {
if (document.addEventListener) {
return function(element, event, handler) {
element.addEventListener(event, handler, false);
};
}
else {
return function(element, event, handler) {
element.attachEvent("on" + event, function() {
handler.call(element, window.event);
}
};
}
}());
You can put a lot of normalization into such a function. For example, when using attachEvent, this does normally not refer to the element the handler is bound to (it refers to window), unlike with addEventListener. Also, as you might know, the event object is not passed as argument to the handler in IE. Both of these issues have been solved through the line:
handler.call(element, window.event);
(the disadvantage is that you cannot simply remove the handler, since you didn't bind handler directly, but these problems can be solved as well).
You can find more information about these browser differences and more in the excellent articles at quirksmode.org.
†: Since browsers provide Object.defineProperty and hence the possibility to mark properties as non-enumerable, extending built-in objects is not as bad anymore, but if libraries start using this, the chance of name collisions, method overriding and incompatibilities gets higher.
For your own code it should be ok. DOM objects should still be a tabu.
If I understood correctly, you want to know how to add methods to the built in javascript objects, so:
myObject.prototype.myMethod = function() {
// whatever
}
so, by example:
String.prototype.splice = function() {
// splice function for every string
}
I've worked a bit with jquery tools and I've started browsing through the twitter bootstrap src.
One thing I've noticed is the use of the $.Event constructor for triggering events.
In many cases, (for instance the bootstrap modal) you find events triggered like so:
var e = $.Event('show');
this.$element.trigger(e);
It eludes me as to why this is better than just directly calling:
this.$element.trigger('show');
so I'm interested to know what the advantages are to using the jQuery Event constructor. I've read in the docs that you can attach arbitrary properties to the event, and that makes total sense to me. What I don't understand however is why it might be advantageous to use the constructor if you're not adding any properties to the event at all.
Can someone explain to me why the $.Event constructor is an advantage over calling trigger with the event string?
Many thanks
It's a bit more flexible if you want to add properties to the event later on; and it helps you to know the state of the event after it was triggered, for instance, if someone called stopPropagation() or preventDefault().
Other than that, jQuery will simply take the event type (string), wrap it in a $.Event object and normalize it. Here's the relevant source code of jQuery where this happens:
event = typeof event === "object" ?
// jQuery.Event object
event[ jQuery.expando ] ? event :
// Object literal
jQuery.extend( jQuery.Event(type), event ) :
// Just the event type (string)
jQuery.Event(type);
After taking a look at the bootstrap source, I believe you must be referring to this:
var that = this
, e = $.Event('show')
this.$element.trigger(e)
if (this.isShown || e.isDefaultPrevented()) return
He's defining e so that he can later check to see if the default action has been prevented with e.isDefaultPrevented().
A few months ago i made a Javascript library for my work, and now it looks like it has a problem with the events handler, the problem is that i have a trigger events function by using the fireEvent method, that works great, and i have something like this:
["focus", "click", "blur", ...].each(function(e){
MyEvents[e] = function(fn){
if(!fn){
trigger(element, e);
} else {
addEvent(element, e, fn);
}
}
});
Of course this is just an idea, the original function is lot bigger... well, as you can notice, i created a custom function for all standards events so i just call it like "element.click(function...); and so...
The problem is that now if i do "input.focus();" it doesnt get focus, but it trigger the event, how can i do so the element get actually in focus ?? maybe removing the focus from the array ?? and if i do so, will i have to remove some other events too like submit, blur, etc??
thank you, actually the library is being tested, so this bugs need to be corrected as soon as possible.
Thank you again.
To get the element in focus - (that is, not triggering the event itself, but focus the element) you use the .focus() method.
You can't do that with the function listed above, because that only assigns events..
You just do something like this:
document.getElementById('#inputbox').focus();
yes, it's as simple as that
Of course, I have no idea how you're referencing the elements in the first place.
after clarifications in the comments
I'm going to restate your question:
"I'm overriding the original .focus() method. Is there any way for me to continue to do so, without breaking the original behavior?"
Yes :)
Here's an example - because I don't know your variables or anything, I'm creating an element on the fly for this example - it's not required:
e = document.createElement('input');
document.body.appendChild(e);
// note: I'm using .focus() just because it was easier for me to debug.. you
// just as well replace it with .blur() instead.
e.focus = function () {
HTMLInputElement.prototype.focus.apply(this, arguments);
}
e.focus();
JS Fiddle link: http://jsfiddle.net/DK8M7/
Ok, I'm not sure how many of those variables you're familiar with. I'm giving an overview:
HTMLInputElement is the name of the original object (think of it as a "class name") for all input elements
.prototype is an object referencing a static object shared across all objects that have or have not been created yet. Kind of like an origin.
.apply() is a method used to call a function from a specific context - that is, you choose it's "this" object, the latter argument is an array of it's parameters
arguments is a special javascript array accessible from all functions which includes an array of all of it's parameters.
More on the apply method:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply
Overriding all input elements' blur() methods
One more thing... If you want all your input elements to have this behavior, the most simple way is to override it's prototype actually.. so since we're on this path, this is how you would do that:
HTMLInputElement.prototype.blurCpy = HTMLInputElement.prototype.blur;
HTMLInputElement.prototype.blur = function () {
HTMLInputElement.prototype.blurCpy.apply(this, arguments);
}
Cheers..