I am displaying context menu on right-clicking a button in a page. The code used for displaying context menu is
window.addEventListener('contextmenu',function (e){e.preventDefault();},false);}
When I am right clicking the button, the context menu method called is
displaycontextmenu(obj,event)
{
console.log("Context");
console.log(event);
// Displaying context menu
}
The code executes fine in IE browser, even in chrome I can see in console that "Context" and event is printed. But in firefox , it is printing as "Context" and undefined.
I am really confused to see here that the event is undefined.
I am using the event to get the x and y co-ordinates to display the context menu at right place. Since the event is undefined, am not able to proceed further.
The reason why you can see the event show up in the console of chrome and IE is that IE has always had the bad habbit of assigning the event object to a global reference (window.event).
Chrome has implemented the W3C model correctly, and passes the event object to the handler as you'd expect, but also keeps a global reference to that event object ready, just in case.
Try logging obj, it'll log the event object in FF... I'm fairly confident of that. Chrome will behave in exactly the same manner, too: the event object will be passed to the handler.
Since the event parameter is left undefined, I can only assume that event is either ignored (treated as a reserved keyword, but only quietly), or there is some behind-the-scenes scope-scanning going on that resolves the local event to the global [window.]event.
At any rate, using varnames that might be keywords, or that already exist (like document, window or this) is either not allowed or frowned upon. That's why you'll often see this:
function eventHandler (e)
{
e = e || event;
}
e is allowed as a varname, and there is no name conflict, unless we ourselves create one. if the handler didn't receive an event object, we reference event which, thanks to scope scanning, will be resolved to window.event.
Related
I recently learned that you don't have to pass the event as a parameter for an event. But I wonder why many still pass the event.
Example click event
btn.addEventListener("click", myfn.bind());
function myfn(event) {
console.log(event.target);
console.log(this);
}
Is there a reason for this? Because that works to:
btn.addEventListener("click", myfn.bind());
// without passing event
function myfn() {
console.log(event.target);
console.log(this);
}
btn.addEventListener("click", myfn.bind());
// without passing event
function myfn() {
console.log(event.target);
console.log(this);
}
Above works because event can access through a global variable, window.event
The read-only Window property event returns the Event which is
currently being handled by the site's code. Outside the context of an
event handler, the value is always undefined.
function myfn(anotherArgName) {
console.log(anotherArgName === window.event); // true
}
Not recommend to use the 2nd one as MDN docs says,
Deprecated: This feature is no longer recommended. Though some
browsers might still support it, it may have already been removed from
the relevant web standards, may be in the process of being dropped, or
may only be kept for compatibility purposes. Avoid using it, and
update existing code if possible; see the compatibility table at the
bottom of this page to guide your decision. Be aware that this feature
may cease to work at any time.
Plus depending on external dependencies makes your function hard to read, test & maintain.
I stumbled across an odd behaviour today. Basically, I had a function bound to a knockout.js click event. The function was making use of the knockout event, but was not explicitly taking it as an argument.
this.myClickHandler = function(){
console.log(event); //event gets logged in Chrome/IE11, not Firefox
}
This looked weird to me, but in Chrome it was working as expected. It also worked correctly in IE11. In Firefox however, it did not function. As soon as I explicitly it worked in all browsers. This is what I would expect would be needed for it to work at all.
this.myClickHandler = function(model, event){ //event is second parameter passed from knockout click event
console.log(event); //event gets logged in all browsers
}
I had a play around and reproduced this with jQuery as well
function func(){
alert(event);
}
function runFunc(callback){
callback();
}
$(document).ready(function(){
runFunc(func);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
So how is this working? Why do I have access to a variable called 'event' in some browsers but not others? Is this intended behaviour?
It is something that got carried from the old version of IE, where the event object was not passed to the handler method instead it was set in the global context.
For backward compatibility IE still supports this model and chrome also has added support for this feature. But FF is not supporting it.
Event object references
I am taking a web development class. Today the teacher gave us a piece of code that raised some questions that I haven't been able to satisfactorily solve through my own searching. The code in question was essentially this:
<script>
function selectmouse(e){
...
...
}
document.onmousedown = selectmouse;
</script>
My first question, is this a legitimate way of calling functions? Is this something that is done? I am of course familiar with the typical way of calling functions from HTML elements, for example
<body onmousedown="selectmouse(event)">
The code was supposed to be calling the function and passing it the event object for the onmousedown. After playing with the code for a while I found a few unusual things.
First, if I put parenthesis after the function call, like I am used to doing (i.e. selectmouse();), then the function resolved immediately upon loading the page, with a value of 'undefined' for the variable. This makes intuitive sense to me, because I assume the browser is treating it like a variable assignment and therefore calling the function as it parses the code, as it normally would to assign a variable.
However the part that is weird to me happened when I deleted the '()' and left it as it is coded above. In this instance it seemed to function like she wanted it to. It would call the function when the mouse was pressed in any part of the body, and it sent the event object as the variable for the function. But I can't figure out why. I can't find reference to anything similar to it online, and I've never seen anything like it before. Is this a legitimate way to do something like this? Or is this bad code that happens to be working for some reason and would probably cause problems in the future? Why is it working?
document.onmousedown = selectmouse; //note: never do this except in old browsers
However the part that is weird to me happened when I deleted the '()' and left it as it is coded above. In this instance it seemed to function like she wanted it to.
That's not weird. You are passing the reference of the function to the browser, not executing it.
For example, you have this function:
function callback(){
alert("clicked!");
}
document.body.onclick = callback;
You pass the reference to onclick and the browser will know what function to call when the event is triggered. But if you do it like this:
document.body.onclick = callback();
This will be evaluated into:
document.body.onclick = alert("clicked!");
//Note that this is simplified explanation to visualize what is happening.
//The returned value of alert() is not assigned to onclick.
//To be exact the returned value of callback() is the one that is being assigned.
//Similar to:
// ...onclick = (function(){ alert("clicked!"); })();
Then you will see an alert, and the browser will continue executing the rest of the code:
document.body.onclick = undefined;
<body onmousedown="selectmouse(event)"> <!-- Don't do this too -->
The parentheses are necessary because this code is not executed instantly. It is only executed when the event is triggered.
Anyway, you shouldn't attach events both using .onmousedown or onmousdown="...". There is a better way of doing it:
element.addEventListener("mousedown", callback, false);
Reason: If you use the onmousedown property, you can only attach one mousedown event. In most cases you would want to attach more than one.
Also attaching events inline might cause security problems (cross-site scripting), and that is exactly why Google decided to prohibit all developers from using them in developing Chrome apps/extensions.
This is legitimate code and is working as it should.
The way you are comfortable with is just a method we tried while the web was evolving, but at present we should better use the second way you showed, although its changed bit more to make you understand it in a better way using event bindings.
When you do
function selectmouse(e){
...
...
}
javascript will create a variable named selectmouse and save the function in that variable. So selectmouse is a variable of type function with the function body as its value.
document on the other hand can be related to class or specifically an object which is an instance. Each document and each HTML element or DOM node can have in it variables to store the functions to be called on user events like onmousedown.
so when doing
document.onmousedown = selectmouse;
we are inturn saying
when mousedown happens in document, the function named selectmouse
should be called
If you do
document.onmousedown = selectmouse();
it means
run the function selectmouse immediately and get the result, assign
the result to onmousedown event of the DOM Node document.
And if you ask why this is taken apart from the form
<body onmousedown="selectmouse(event)">
To answer in a simple way, HTML is Hyper Text Markup Language, its sole purpose is to represent formatted data, the quick evolution of web inturn made it deranged with behaviours like this and presentation code like inline css. So to make behaviour and presentation out of HTML and thus a better design we do this.
Please take time to take a look at how you can bind a function to an event which is the current tradeoff in doing this same thing.
For a detailed explanation please check the events sectio of ppk blog here
I think that is correct, because the function is being called within the script as if it were an object, to me is not the best way to do it, I would have like this (with jquery):
$(document).mousedown(function (event) {
// here the content of the function
});
<body onmousedown="selectmouse(event)">
In this example the browser evaluates the result of the expression selectmouse(event) and assigns it to the onmousedown property of the body, event is undefined and the selectmouse doesn't return anything so it's result is undefined.
It is equivalent of the following if it was inside a script tag
<script>
function selectmouse(e) {
}
document.body.onmousedown = selectmouse(event);
</script>
<body onmousedown="selectmouse">
When you remove the () you are assigning a function to the onmousedown property. Now the browser fires your callback method whenever the mousedown event is raised and it bubbles up to the body, passing the current event as the parameter you're declaring as "e". If another element also had an onmousedown event handler declared but it cancelled the event ( by calling event.cancelBubble = true ) the body's onmousedown handler will not be invoked.
<script>
function selectmouse(e) {
}
document.body.onmousedown = selectmouse;
</script>
I have a global event manager, allowing you to listen with lambdas to string event names.
// somewhere in the ModuleScript class
Event->Listen("WindowResize", [=]{
// ...
});
Now, I want to register to events from JavaScript, too. Therefore, I wrote this callback.
v8::Handle<v8::Value> ModuleScript::jsOn(const v8::Arguments& args)
{
// get pointer to class since we're in a static method
ModuleScript *module = (ModuleScript*)HelperScript::Unwrap(args.Data());
// get event name we want to register to from arguments
if(args.Length() < 1 || !args[0]->IsString())
return v8::Undefined();
string name = *v8::String::Utf8Value(args[0]);
// get callback function from arguments
if(args.Length() < 2 || !args[1]->IsFunction())
return v8::Undefined();
v8::Handle<v8::Function> callback =
v8::Local<v8::Function>::Cast(args[1]->ToObject());
// register event on global event manager
module->Event->Listen(name, [=]{
// create persistent handle so that function stays valid
// maybe this doesn't work, I don't know
v8::Persistent<v8::Function> function =
v8::Persistent<v8::Function>::New(args.GetIsolate(), callback);
// execute callback function
// causes the access violation
function->Call(function, 0, NULL);
});
return v8::Undefined();
}
When the event is triggered, the application crashes with a access violation. My thoughts are that either the function object isn't valid at this time anymore, or it is a JavaScript scope issue. But I couldn't figure it out.
What causes the access violation and how to overcome it?
I believe there are several potential issues here.
First, you're not using a persistent handle to hold the JavaScript function after ModuleScript::jsOn() terminates. By the time your event handler is invoked, the function might be gone. Consider making callback a persistent handle.
Second, your event handler needs to enter an appropriate V8 context before calling the JavaScript function. Depending on your architecture, explicitly locking and entering the V8 isolate may be required as well.
Third (and this may not be an issue in your specific scenario), you need to manage the lifetime of the V8 isolate. If your event manager fires events on background threads, you have to make sure your event handler somehow prevents the isolate from being disposed from another thread. Unfortunately this is one area where the V8 API doesn't provide much help.
Fourth, to prevent a leak, your event handler should dispose the persistent function handle after invoking the function.
Good luck!
I want to check if an event is available or not before binding a function to it.
The problem is that Google Chrome support the event "loadedmetadata" in the Video element while FireFox don't.
I did the following
$('video').bind('loadedmetadata', videoloaded);
videoloaded();
It worked well in Firefox but when I tried in Chrome, the function was executed twice (which is logical). I want to check if loadedmetadata event handler exists or not to run the function only one time in each browser.
If such possibility doesn't exist, any intelligent work around for this?
Check the $video.data("events") if this object contains your event, since you are using .bind all events of this element will be stored in this object.
var $video = $("#video");
var $ve = $video.data("events");
// checking if a `loadedmetadata` object exists in `data("events")`
if ($ve != null && typeof($ve.loadedmetadata) !== undefined)
{
// has loadedmetadata event
}
Full working example on jsFiddle