Why do many pass an event in the callback - javascript

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.

Related

Why is there an 'event' variable available without being defined when there was an event upstream?

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

calling a function using a mouse event without attaching it to an HTML element?

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>

Adding a javascript function call to an event (such as 'window.resize') instead of overwriting what is already there

Is there a way to tell the browser to run an addtional java script function on an event such as 'window.resize' instead of overwriting what is already there?
Using jquery's
$(window).resize(<something>);
Seems to replace what is already there. Is there a way to tell it to do something in addition?
Is this a poor design / wrong way to do it?
I wouldn't think that jQuery would break what's there, but you could wrap the functions in a single function:
// if a function already exists...
if( window.onresize ) {
var prev_func = window.onresize; // cache the old function
window.onresize = function( event ) { // new function for resize
prev_func.call( window, event ); // call the old one, setting the
// context (for "strict mode") and
// passing on the event object
// call your code or function
};
}
EDIT: Fixed it to use onresize instead of resize.
EDIT2: Missed one! Fixed.
If you're using jQuery to bind all event handlers, then you're not breaking anything. jQuery supports multiple handlers for same event.
But if other code (not using jQuery) binds to the event, then you'll overwrite handler with your statement. The solution will be: always use jQuery for event binding or try to save old handler (see patrick dw's answer).
See element.addEventListener (element.attachEvent in IE 8 and under):
// Standards
if (window.addEventListener){
window.addEventListener("resize", callOnResize, false);
// IE 8 and under
} else if (window.attachEvent){
window.attachEvent('resize', callOnResize);
}
function callOnResize() {
console.log("resized");
}
Keep in mind this is pure JavaScript—jQuery (and pretty much any big JS library) has a method to handle creating standards and IE handlers without you needing to write each. Still, it's good to know what's happening behind the scenes.
jQuery and all other frameworks supporting custom events attach a function to the event of the elem (or observe it). That function then triggers all functions that have been bound (using bind) for a specific event type.
domelement.addEventListener does not override an other function and your function added can't be removed by other (bad) javascript, except when it would know the exact footprint of your function.

Safely using hook events in JavaScript

In source code here
http://www.daftlogic.com/sandbox-javascript-slider-control.htm
There is these instructions:
// safely hook document/window events
if (document.onmousemove != f_sliderMouseMove) {
window.f_savedMouseMove = document.onmousemove;
document.onmousemove = f_sliderMouseMove;
}
I don't understand what it does and why it would be safer to do that this way, does someone understand?
It might be that some other code already assigned an event handler to document.onmousemove. The problem with this method, as opposed to addEventListener, is that only one function can be assigned to element.onXXXX. Thus, if you blindly assign a new event handler, an already existing one might be overwritten and other code might break.
In such a case, I would write:
if (document.onmousemove) {
(function() {
var old_handler = document.onmousemove;
document.onmousemove = function() {
old_handler.apply(this, arguments);
f_sliderMouseMove.apply(this, arguments);
};
}());
}
else {
document.onmousemove = f_sliderMouseMove;
}
This way it is ensured that both event handlers are executed. But I guess that depends on the context of the code. Maybe f_sliderMouseMove calls window.f_savedMouseMove anyway.
It is just saving the current hook, presumably so it can call it at the end of its own hook method.
It avoids stamping on some other codes hook that was already set up.
You would expect the hook code to be something like:
f_sliderMouseMove = function(e) {
// Do my thing
// Do their thing
window.f_savedMouseMove();
}
[obligatory jquery plug] use jquery events and you can ignore problems like this...
It appears that this code is storing the function that is currently executed on a mouse move, before setting the new one. That way, it can presumably be restored later, or delegated to, if need be. This should increase compatibility with other code or frameworks.

How to expand an onchange event with JavaScript

This is a question I ran into about expanding on an element's JavaScript onchange event. I have several select elements that conditionally will have one onchange event attached to each of them (when they change to specific values, it hides/unhides certain elements). I want to conditionally add or append to another onchange event so they set a global variable if they do change without modifying or disabling the previous function already attached to them. Is there a way to "append" an additional function or add more functionality onto the already existing one?
Here is what I believe an example would be like:
<select id="selectbox1">
<option>...</option>
<option>...</option>
</select>
if (<certain conditions>) {
document.getElementById("selectbox1").onchange = function () {
//hides elements to reduce clutter on a web form ...
}
}
....
if (<other conditions>) {
document.getElementById("selectbox1").onchange = function2 () {
//set global variable to false
}
}
Alternatively I'd like to simply add the 1-liner "set global variable to false" to the original function.
You can cheat by simply having a composite function that calls the other functions.
document.getElementById("selectbox1").onchange = function() {
function1();
function2();
}
You can also use the observer pattern, described in the book Pro JavaScript Design Patterns. I have an example of its use in an article (here).
//– publisher class —
function Publisher() {
this.subscribers = [];
};
Publisher.prototype.deliver = function(data) {
this.subscribers.forEach(function(fn) { fn(data); });
};
//– subscribe method to all existing objects
Function.prototype.subscribe = function(publisher) {
var that = this;
var alreadyExists = publisher.subscribers.some(function(el) {
if (el === that) {
return;
}
});
if (!alreadyExists) {
publisher.subscribers.push(this);
}
return this;
};
You want to look at the addEventListener() and attachEvent() functions (for Mozilla-based browsers and IE respectively).
Take a look at the docs for addEventListener() and attachEvent().
var el = document.getElementById("selectbox1");
try { //For IE
el.attachEvent("onchange", function(){ code here.. });
}
catch(e) { //For FF, Opera, Safari etc
el.addEventListener("change", function(){ code here.. }, false);
}
You can add multiple listeners to each element, therefore more than one function can be called when the event fires.
Can you use jQuery? This will allow you to bind/manipulate/unbind events pretty easily. The only hitch is event handlers are activated in the order they are bound.
if (<certain conditions>) {
$("#selectbox1").bind("change", eventdata, function1);
}
if (<other conditions>) {
$("#selectbox1").bind("change", eventdata, function1);
}
And, you can also look into triggering custom events, if your needs are complex. For example, instead of "interpreting" onChange, maybe there is a way to specifically trigger custom events. See the last example on jQuery's page.
If you use jQUery you would have something like
<select id="selectbox1">
<option>...</option>
<option>...</option>
</select>
if (<certain conditions>) {
$("#selectbox1").change(function () {
//hides elements to reduce clutter on a web form ...
});
}
....
if (<other conditions>) {
$("#selectbox1").change(function () {
//set global variable to false
});
}
This will mostly take care of browser compatibility as well.
There are currently three different methods for defining event handlers (a function which is fired when a certain event is detected): the traditional method, the W3C method, and the Microsoft method.
Traditional method
In the traditional method, event handlers are defined by setting the onevent property of an element in Javascript (as you are doing in your example code), or by setting the onevent attribute in an HTML tag (such as <select onchange="...">). While this is the simplest method to use, its use is generally frowned upon now, because as you have discovered, it is rather rigid -- it is not easy to add and remove event handlers to an element that already has an event handler attached. As well, it is not considered proper practice anymore to mix javascript in with HTML, but rather it should be contained within or loaded via a <script> tag.
W3C / Microsoft methods
The W3C (World Wide Web Consortium) and Microsoft both define their own event models. The two models works essentially the same way, but use different syntaxes. The Microsoft model is used in Internet Explorer, and the W3C model is used in other browsers (Firefox, Opera, Safari, Chrome, etc.). In both of these models, there are functions provided to add event handlers (addEventListener for W3C, attachEvent for Microsoft) and remove event handlers (removeEventListener / detachEvent). This allows you to dynamically add and remove specific handlers to an element; in your case, you could add the first handler based on the first condition and the second based on the second condition. The "problem" with these methods is that there are two of them, and thus both methods need to be used in order to ensure that your event handler will be registered in all browsers (there are also a few subtle differences between the two models, but those differences are not important to the scope of this question). In fact, if you look, you will find a large number of "addEvent" functions which use both methods as necessary (and generally fall back to the traditional method for older browsers). For example, a contest was run on the QuirksMode blog back in 2005 to build the best "addEvent" function, the result of which (along with the winning function) you can see here.
As well, if you use a javascript library such as Prototype or jQuery, they come with built in event handling functions that will take care of the above for you.
Have a look at addEventListener - https://developer.mozilla.org/en/DOM/element.addEventListener
I feel as though I may be missing something important about your question, but would this more simple solution not work for you?
Simply check for the conditions inside of the onChange event and perform the actions as desired. It would be much easier than having to dynamically re-add/remove eventListeners
document.getElementById("selectbox1").onchange = function () {
if (<certain conditions>) {
//hides elements to reduce clutter on a web form ...
}
if (<other conditions>) { ... }
}

Categories

Resources