Should i assign JavaScript events directly just to unify code? - javascript

Am I programming poorly or bending backwards too much for IE by assigne event handlers like:
element.someEvent = someFunction;
instead of:
element.attachEventListener("someEvent", someFunction, false);
What would be some cons and pros to this idea?
What am i missing out or setting my self up to by not just testing and choosing the proper method?

If you use the element.onclick = xxx; style, you're limiting yourself (and any other scripts you're using on the page) to a single event handler for the event on that element (any previous handler for that event gets bludgeoned). With the addEventListener / attachEvent mechanism, you can attach and remove event handlers independently of one another, without cross-talk.
So for instance, if you do:
document.getElementById('foo').onclick = function() {
};
...then any previous click handler on element foo gets blown away and no longer called when foo is clicked. In contrast:
document.getElementById('foo').addEventListener('click', function() {
});
Now your click handler gets called, but any other click handlers that have previously been attached to the element also get called. You're playing nicely with others. :-)
It's easy to create a function that irons these things out for you:
function hookEvent(element, eventName, handler) {
if (element.attachEvent) {
element.attachEvent("on" + eventName, handler);
}
else if (element.addEventListener) {
element.addEventListener(eventName, handler, false);
}
else {
element["on" + eventName] = handler;
}
}
...or if you really want to go to town:
var hookEvent = (function() {
function hookViaAttach(element, eventName, handler) {
element.attachEvent("on" + eventName, handler);
}
function hookViaAdd(element, eventName, handler) {
element.addEventListener(eventName, handler, false);
}
function hookDOM0(element, eventName, handler) {
element["on" + eventName] = handler;
}
if (document.attachEvent) {
return hookViaAttach;
}
if (document.addEventListener) {
return hookViaAdd;
}
return hookDOM0;
})();
...which creates a function that detects once what kind of handler to use and then uses that throughout.
Frankly, though, for this stuff it's useful to leverage the work of others and use a library like jQuery, Prototype, YUI, Closure, or any of several others.

If you have full control over the page, and you're sure that all event binding happens in one place, then .onevent is the simplest and most supported way.
If you're worried about overriding a previous event handler, you can always do:
var oldOnclick = el.onclick;
el.onclick = function () {
// blah blah
if (oldOnclick) oldOnclick();
};

In IE, you need to use attachEvent. (The standard DOM mechanism is addEventListener.) I would highly recommend using a library like jQuery that takes care of these browser differences for you. Frameworks like jQuery also take care of ironing out the differences between the event models in different browsers.

Nowadays there are many javascript library out there that will take care of the cross browsers compatibility for you and also make coding in javascript fun. One of the most popular one out there is Jquery.
You can register events with Jquery like so:
$(elem).click(function () {
alert('Event handler');
});
or
$(elem).bind('click', function() {
alert('Event handler');
});

Related

Should I use bind() or on() with DOMSubtreeModified [duplicate]

I found two great articles talking about the new function .on(): jquery4u.com, elijahmanor.com.
Is there any way where the .bind() still is better to use than .on()?
For example, I have a sample code that look like this:
$("#container").click( function( e ) {} )
You can note that I just have one item retrieved by the selector and in my case, the <div> named #container already exists when my page was loaded; not added dynamically. It’s important to mention that I use the latest version of jQuery: 1.7.2.
For that sample, should .on() be used instead of .bind() even if I don’t use the other features provided by the .on() function?
Internally, .bind maps directly to .on in the current version of jQuery. (The same goes for .live.) So there is a tiny but practically insignificant performance hit if you use .bind instead.
However, .bind may be removed from future versions at any time. There is no reason to keep using .bind and every reason to prefer .on instead.
These snippets all perform exactly the same thing:
element.on('click', function () { ... });
element.bind('click', function () { ... });
element.click(function () { ... });
However, they are very different from these, which all perform the same thing:
element.on('click', 'selector', function () { ... });
element.delegate('click', 'selector', function () { ... });
$('selector').live('click', function () { ... });
The second set of event handlers use event delegation and will work for dynamically added elements. Event handlers that use delegation are also much more performant. The first set will not work for dynamically added elements, and are much worse for performance.
jQuery's on() function does not introduce any new functionality that did not already exist, it is just an attempt to standardize event handling in jQuery (you no longer have to decide between live, bind, or delegate).
The direct methods and .delegate are superior APIs to .on and there is no intention of deprecating them.
The direct methods are preferable because your code will be less stringly typed. You will get immediate error when you mistype an
event name rather than a silent bug. In my opinion, it's also easier to write and read click than on("click"
The .delegate is superior to .on because of the argument's order:
$(elem).delegate( ".selector", {
click: function() {
},
mousemove: function() {
},
mouseup: function() {
},
mousedown: function() {
}
});
You know right away it's delegated because, well, it says delegate. You also instantly see the selector.
With .on it's not immediately clear if it's even delegated and you have to look at the end for the selector:
$(elem).on({
click: function() {
},
mousemove: function() {
},
mouseup: function() {
},
mousedown: function() {
}
}, "selector" );
Now, the naming of .bind is really terrible and is at face value worse than .on. But .delegate cannot do non-delegated events and there
are events that don't have a direct method, so in a rare case like this it could be used but only because you want to make a clean separation between delegated and non-delegated events.
If you look in the source code for $.fn.bind you will find that it's just an rewrite function for on:
function (types, data, fn) {
return this.on(types, null, data, fn);
}
http://james.padolsey.com/jquery/#v=1.7.2&fn=$.fn.bind
From the jQuery documentation:
As of jQuery 1.7, the .on() method is the preferred method for attaching event handlers to a document. For earlier versions, the .bind() method is used for attaching an event handler directly to elements. Handlers are attached to the currently selected elements in the jQuery object, so those elements must exist at the point the call to .bind() occurs. For more flexible event binding, see the discussion of event delegation in .on() or .delegate().
http://api.jquery.com/bind/
As of Jquery 3.0 and above .bind has been deprecated and they prefer using .on instead. As #Blazemonger answered earlier that it may be removed and its for sure that it will be removed. For the older versions .bind would also call .on internally and there is no difference between them. Please also see the api for more detail.
jQuery API documentation for .bind()

Javascript: addEventListener(), removeEventListener() and bind()

I want to add and event listener, I want the function the event listener calls to be bound to the calling scope and I want to be able to remove the listener at some arbitrary date in the future.
The obvious thing doesn't work:
function Thing(){
this.thingINeed = "important!"
}
// the function that does the thing.
Thing.prototype.handlerFunction = function(e){
console.log(this.thingINeed)
e.preventDefault;
}
// do the binding.
window.document.body.addEventListener('click', this.handlerFunction.bind());
// sometime later...this is my best guess. The event listener remains active.
window.removeEventListener('click', this.handlerFunction.bind());
// this also doesn't work:
window.removeEventListener('click', this.handlerFunction);
So I flogged together some code that does work:
function Thing(){
this.thingINeed = "important!"
}
Thing.prototype.handlerFunction = function(e){
console.log(this.thingINeed);
e.preventDefault;
}
// Where the 'magic' happens...
this.boundHandlerFunction = this.handlerFunction.bind(this);
window.document.body.addEventListener('click', this.boundHandlerFunction);
// sometime later...
window.removeEventListener('click', this.boundHandlerFunction);
MDN goes into some detail on matching event listeners with removal, but it doesn't mention .bind() and I can't find any examples of other people doing it this way. The code isn't exactly obvious without extensive commenting.
Is there a better way?
jQuery event listeners can be named, which makes them really easy to remove, but that isn't possible with vanilla?
Thanks.
The issue boils down to - Function.prototype.bind returns a new function. It works when you set the bound function to a variable and use it in both addEventListener and removeEventListener because both are referencing the same function. The first block of code does not work because they are referencing different functions. Here is a contrived example:
function foo () {}
// does not work because foo.bind() returns a new function each time
// these functions are not the same object
document.addEventListener('click', foo.bind())
document.removeEventListener('click', foo.bind())
//does work because both reference the same function
var boundFoo = foo.bind()
document.addEventListener('click', boundFoo)
document.removeEventListener('click', boundFoo)
I can't speak much to how jQuery handles events under the hood, but there is no getting around this behavior in vanilla JS.

Passing 'this' and argument to addEventListener function without using bind

Subsequent to removeEventListener in bootstrapped addon not working when addon disabled, I am exploring other possibilities.
Beside using bind() and caching the bound function, is there a way to use 'this' and pass argument?
// works fine but can't pass argeement
contextMenu.addEventListener('popupshowing',
this.contextPopupShowing, false);
// passes the argument but 'this' is no longer available
contextMenu.addEventListener('popupshowing',
function(){this.contextPopupShowing(window);}, false);
I have been using a number of event listeners with bind() and I am looking for alternative methods without using bind()
I even tried to grab window with a recursive function from <menupopup id="contentAreaContextMenu" ...>
Update: bind() interferes with removeEventListener
Since we're talking restartless add-ons... A lot of restartless add-ons use unload and unloadWindow helper functions, to make it easier to implement shutdown properly and also help with stuff like addEventListener, so bear with me for a bit.
The helpers - unload
First, unload is a helper function that you pass another function to, that will be run upon shutdown (or can be called manually). Most implementations are extremely similar to this:
var unloaders = []; // Keeps track of unloader functions.
function unload(fn) {
if (typeof(fn) != "function") {
throw new Error("unloader is not a function");
}
unloaders.push(fn);
return function() {
try {
 fn();
}
catch (ex) {
Cu.reportError("unloader threw " + fn.toSource());
Cu.reportError(ex);
}
unloaders = unloaders.filter(function(c) { return c != fn; });
};
}
You'd then hook up shutdown to do the right thing:
function shutdown() {
...
for (let i = unloaders.length - 1; i >= 0; --i) {
try {
unloaders[i]();
}
catch (ex) {
Cu.reportError("unloader threw on shutdown " + fn.toSource());
Cu.reportError(ex);
}
}
unloaders.length = 0;
}
Using unload
Now you can do stuff like:
function startup() {
setupSomething();
unload(removeSomething);
setupSomethingElse();
var manualRemove = unload(removeSomethingElse);
...
if (condition) {
manualRemove();
  }
}
The helpers - unloadWindow
You'll usually want to create a second function unloadWindow to unload stuff when either your add-on is shut down or the window gets closed, whatever happens first. Not removing stuff when the window gets closed can be very tricky, and create Zombie compartments of your bootstrap.js and/or code modules very easily (this is from experience writing and reviewing restartless add-ons).
function unloadWindow(window, fn) {
let handler = unload(function() {
window.removeEventListener('unload', handler, false);
try {
fn();
}
catch (ex) {
Cu.reportError("window unloader threw " + fn.toSource());
Cu.reportError(ex);
}
});
window.addEventListener('unload', handler, false);
};
(Some people might want to "optimize" this, as to have only one "unload" handler, but usually you only have so unloadWindow calls that it won't matter.)
Putting it all together
Now you can .bind stuff and do whatever and let the the unloader closures keep track of it. Also, you can use this to keep your shut down code next to your initialization code, which might increase readability.
function setupWindow(window, document) {
var bound = this.contextPopupShowing.bind(this);
contextMenu.addEventListener('popupshowing', bound, false);
unloadWindow(window, function() {
contextMenu.removeEventListener('popupshowing', bound, false);
});
// Or stuff like
var element = document.createElement(...);
contextMenu.appendChild(element);
unloadWindow(window, function() {
contextMenu.removeChild(element);
});
// Or just combine the above into a single unloader
unloadWindow(window, function() {
contextMenu.removeEventListener('popupshowing', bound, false);
contextMenu.removeChild(element);
});
}
Before bind() was supported you had to save a reference to this outside the function. Then pass in a function that can forward the invocation the way you want.
var self = this;
contextMenu.addEventListener('popupshowing', function() {
self.contextPopupShowing.apply(self, arguments);
}, false);
In this case we use apply to set the context to self, our saved version of this, and to send it whatever arguments were passed to the anonymous function via the magic arguments keyword that contains the list of arguments that a function was passed when invoked.
You don't have to use bind for addEventListener. You can use handleEvent. It was in that topic I linked you too:
Removing event listener which was added with bind
MDN :: EventTarget.addEventListener - The value of "this" within the handler
handleEvent is actually a common way the javascript code in the firefox codebase does it.
Copied straight from MDN:
var Something = function(element) {
this.name = 'Something Good';
this.handleEvent = function(event) {
console.log(this.name); // 'Something Good', as this is the Something object
switch(event.type) {
case 'click':
// some code here...
break;
case 'dblclick':
// some code here...
break;
}
};
// Note that the listeners in this case are this, not this.handleEvent
element.addEventListener('click', this, false);
element.addEventListener('dblclick', this, false);
// You can properly remove the listners
element.removeEventListener('click', this, false);
element.removeEventListener('dblclick', this, false);
}
Where I mostly use bind is when doing a for loop and I make anonymous functions with something in the array like arr[i]. If I don't bind it then it always just takes the last element of the array, I have no idea why, and I hate it, so then I go to using [].forEach.call(arr, function(arrI).
http://2ality.com/2013/06/auto-binding.html
var listener = myWidget.handleClick.bind(myWidget);
domElement.addEventListener('click', listener);
...
domElement.removeEventListener(listener);

How to rebind the events in jquery

I have some events like click, dblclick that are attached on large number of elements. To unbind click and dblclick events with all the elements at once I used this in jquery :
$(document).unbind("click").unbind("dblclick");
Now I have to rebind these events to all the elements again. For this I used :
$(document).bind("click").bind("dblclick");
But this is not working. Events are not rebind. How can I do this?
$(document).bind("click").bind("dblclick");
I don't think this will bind anything, you need callbacks.
$(document).bind("click", onClick).bind("dblclick", onDbClick);
Also, in this case you might want to consider using namespaced events:
$(document).bind("click.myclick", onClick)
And then later unbind only this event, leaving the other click untouched.
$(document).unbind("click.myclick");
P.S. It's now considered better practice to use the new on, off methods for binding.
unbind:
$(document).off("click", myFunction);
bind:
$(document).on("click", myFunction);
function myFunction() {
alert('you clicked the document');
}
jQuery on() and off() would be the way to go, and when rebinding, the function would have to be passed in again, you can't just rebind and expect it to know what function to call.
The best way is to name the callback functions, just as #adeneo suggested. But sometimes you don’t know when the handlers are bound (f.ex in a plugin), or perhaps you added anonymous callbacks using something like:
$(document).click(function() {
// anonymous
});
then you can restore all those callbacks using the $._data object. Here is a function for you:
function restoreHandlers(elem, type) {
var orig = $._data(elem, 'events');
if ( type in orig ) {
return $.map(orig[type], function(o) {
return o.handler;
});
} else return [];
}
Use it like this (before you unbind):
var handlers = restoreHandlers(document, 'click');
then unbind:
$(document).off('click');
then rebind:
$.each(handlers, function(i, fn) {
$(document).on('click', fn);
});

What is difference between $(".anything").click() and $(".anything").bind(click)

What is difference between $(".anything").click() and $(".anything").bind(click)
$(".anything").click(function() {
});
$(".anything").bind('click', function() {
});
Nothing, click(function() { }) is just a shortcut for bind("click", function() { }).
From the jQuery docs:
The jQuery library provides shortcut methods for binding the standard event types, such as .click() for .bind('click').
You can read more about bind() here.
The first is a shortcut of the second. The second is actually wrong, click should have been quoted. Further, in the second you have the added benefit that you can bind the same function to multiple events, each separated by a space. E.g.
$(".anything").bind("click keypress blur", function() {
});
In that specific case, absolutely nothing.
However:
A) If you give .click() no argument, it triggers the event instead of setting the handler.
B) only .bind() lets you use the "namespaced" way of registering handlers:
$(whatever).bind('click.myEvent', function (e) { ... });
$(whatever).unbind('click.myEvent'); // Removes just that handler
See this post, which points to the JQuery source to show that .click(fn) just calls .bind('click', fn): jQuery: $().click(fn) vs. $().bind('click',fn);
I usually only use the latter if:
I want to bind multiple things, i.e. .bind('click focus', fn)
Just to maintain convention if I call unbind later, i.e.:
var fn = function() { alert('foo'); }
$('#foo').bind('click', fn);
...
$('#foo').unbind('click', fn);

Categories