Safe, universal, way to use addEventHandler in Javascript? - javascript

Before I get into the details of this problem, I'd like to make the situation clear. Our web analytics company works as a consultant for large sites, and (other than adding a single SCRIPT tag) we have no control over the pages themselves.
Our existing script installs handlers using "old" way (a fancy version of element.onclick = blah; that also executes the original handler) which is completely unaware of "new" (addEventListener or attachEvent) handlers on the page. We'd like to fix this to make our script able to run on more sites without requiring as much custom development.
The initial thought here was to have our own script use addEventListener/attachEvent, but this presents a problem: of the client's site sets a handler using the "old" way, it would wipe out the handler we installed the "new" way. Quick and dirty testing shows this happens in both IE7 and FF3, although I didn't test the whole range of browsers. There's also a risk that if we use the "new" way after the page's event handlers are already set, we could erase their handlers.
So my question is: what safe technique can I use to add an event handler in Javascript using addEventListener/attachEvent that works regardless of how other event handlers on the page are installed?
Please remember: we have no way of modifying the site that our script is installed on. (I have to emphasize that because the default answer to questions like this is always, "just rewrite the page to do everything the same way.")

Can you try your quick-and-dirty testing again? This doesn't happen for me in FF3.
elem.onclick = function() { alert("foo"); };
elem.addEventListener("click", function() { alert("bar"); }, false);
Both handlers fire for me when I click on the element.
I'm guessing you forgot the final boolean argument in addEventListener (whether to use the capture phase). I'm also guessing you forgot that IE's attachEvent needs onclick, not click.

addEventListener/attachEvent is safe in a sense you ask. They add a new event handler to a Node without altering any handlers previously added to it (even once assigned through a property onxxx). For a company that bring some to a foreign page using addEventListener/attachEvent must be the only practice. Assigning onxxx handler via properties indeed would break hosting pages scipts (that have been previously assigned the same way)

Related

How does JQuery create it's Custom Events and can I recreate in Javascript?

I am looking to create events in Javascript using the same methodology as JQuery- Does anyone know how JQuery does it?
My reasoning is that using raw Javascript such this:
var myEvent = new CustomEvent("userLogin", eventProperties);
...does not actually work on Android native browser, as it does not support DOM Level 3 like Chrome and other browsers do.
However, JQuery does work on Android's stock browser, and simply uses:
$.event.trigger('MyEvent');
My question is, what is the code behind this? I tried to find it by going through JQuery's source code, but cannot get my head around it!
The fundamental thing here is this: When you hook an event handler up with jQuery, jQuery doesn't directly add that handler to the DOM element. Instead, jQuery hooks up a handler of its own on the DOM element (if it doesn't already have one on it). When the event occurs, jQuery looks at the list of jQuery-registered handlers for the event and fires them in order. (There are several reasons for this; initially it was primarily around IE memory leaks and the fact that IE fired handlers in one order, and everyone else in a different order; so jQuery took over and ensured the order.)
(You might be able to see where I'm going with this...)
So when you use trigger, jQuery sends the synthetic event to the DOM element, but it doesn't rely on that synthetic event to work; it calls the handlers you've registered through jQuery directly. In fact, it sets a flag so that it knows that it's done that, so if the browser does send the event to jQuery's handler for it, jQuery knows to ignore it (since it's already done its work).
You can see this in all its glory starting with line 4,464 of the current uncompressed jQuery file.
So basically jQuery's build its own pub/sub system, and only uses the browser event system as an input to it. So custom events don't usually have to talk to the browser at all.

How to override an event in JavaScript whatever the library previously used to bind it?

I'm trying the greasemonkey extension for Firefox in order to override some JavaScript events on the websites I visit.
For instance, I want to display a message when the blur event has been bound to the window element, whatever the page I visit.
I thought it was pretty easy. But it is much harder to make this working on every case, because depending on the library the website uses, the JavaScript events seems to be stored in different places.
Here is a test I have made in a greasemonkey user script :
window.onload = function(event) {
// Handle pure JS
if (window.onblur) {
console.log('There is a blur event on the window element of this page');
}
// Handle jQuery
if (window.jQuery) {
var jExpando = window[window.jQuery.expando];
if (jExpando.events.blur) {
console.log('There is a blur jQuery event on the window element of this page');
}
}
}
This works on every page that uses pure JavaScript or jQuery, but I'm not able to catch blur events listeners of other libraries such as Mootools, Prototype...
Thus, this is pretty bad code, considering I have to handle all the existing JavaScript libraries myself and I have no idea how to do that.
Is there a way to get all event listeners bind on a specific JavaScript object (whatever the library used to attached them) so I can override some of them?
Most libraries use addEventListener, so you'll want to override that with a function of your own. Be sure to keep a reference to the original addEventListener method so the events can still be added.
I have an example Greasemonkey script here: http://userscripts.org/scripts/show/174829
It will listen to all events and tell you which ones were added. Obviously, you can modify this to make 'blur' events not register.

Detect if an Input has Changed dynamically

Other javascript is changing the value of an input and I was wondering if there was a way to detect the change.
This question has nothing to do with Keyup or Change. This is not being typed in by the user it is being changed by other javascript though various actions of the user.
When changing an event programatically, you can trigger a change event to make sure event handlers that are attached to the element are fired. jQuery has a trigger() method to do this:
$('#elementID').on('change', function() {
alert( this.value );
});
$('#elementID').val('some new value').trigger('change');
The quick run-down of what I am going to say is: there is no way other than to modify the third-party scripts to output stuff, or to use setInterval (costly).
The bottom line of this issue is a simple one, that does not appear to be so at first: How can you get your scrips to communicate with each other?
When a script modifies the value of an input through JS methods (i.e. not user input), they have to go through specific hoops to get the "change" event to fire (they can fire it manually by calling it, which most devs never do and is easily forgotten when writing code). In practice, people tend to rely on the observation events (user-defined ones) to track code changes. This is very similar to DOM events - you bind callbacks to your script, which allow you to tap callbacks in that will fire whenever your scripts do something interesting (like modifying inputs. This is just one example). You then teach your scripts and developers to fire events on useful stuff using the callbacks to notify other scripts.
A great library for this is Postal, which is originally a Node library. jQuery also has an event system you can tap into. However, if you want to roll your own, all you have to read into is the Observer design pattern. It is trivial: you bind a function to your object to pick up callbacks, and another to fire them. Whenever you change the thing, you fire the callback. Simples.
Failure to do so means setInterval. Sucks, but there you go :-(

Setting event handlers to DOM objects

I was looking for the most proper way to attach DOM events avoiding browser compatibility issues and found that the Mozilla developers site states:
The old way is to just assign it like this:
document.getElementById('cupcakeButton').onclick = getACupcake;
As above, the event object is either a global or an argument. This
method may have problems and is not the preferred method, but it still
works and a lot of people still use it.
What type of problems does this refer to?
The most obvious one is already mentioned, it would replace a previously assigned handler.
document.getElementById('id') should work in all browsers except in really old ones (Netscape 4-, IE 4-), there you should use document.layers['id'] and document.all[id] respectively.
IE 5 up to IE 7 have one more issue, which is that they will also return elements where name='id' instead of only the elements where id='id'. That could really stuff you up.
Have a look at jQuery for a way to attach DOM event handlers avoiding browser compatibility issues.
The biggest problem I can think of is that it won't allow assigning multiple click handlers, by doing another .onclick = fn; you basically unbind the previous handler if it was there.
Even if that magically worked, you would have no way to unregister a specific handler; it's all or nothing.

Protect jQuery function from outside access

I'm using jQuery in an app which registers user clicks to perform a given action through the .click() binding, and I want this functionality to be available only through a user mousedown. One of my friends pointed out today that it's possible to run $(element).click() from a javascript terminal in Firebug (or something similar), and achieve the same functionality as clicking on the element -- something I'd like to prevent. Any ideas on how to go about doing this? Thanks for your input.
Short answer: No, you can't really prevent it.
Long answer: Any event like a click event is bound to such called Event handlers. Those handlers are functions which are executed when a given event occurs. So if you click on an element, your browser checks if any event handlers are bound to it, if so it fires them. If not, the browser will try to bubble up the event to the parent elements, again checks if there are any event handlers bound for that kind of event .. and so forth.
jQuerys .trigger() method (which is what you actually call if calling .click() for instance) just does the same thing. It calls the event handlers which are bound to a specific element, for a specific event.
EDIT
There might some simple ways to somekind of soft detect a real click, for instance you might check for the toElement property within an event object. That property is not set when triggered. But then again, you can easily fake that aswell with .trigger(). Example:
$(document).ready(function() {
$('#invalid2').bind('click', function(e){
alert('click\nevent.target: ' + e.toElement.id);
console.log(e);
});
$('#invalid1').bind('click', function(){
$('#invalid2').trigger({
type: 'click',
toElement: {id: 'Fake'}
});
});
});​
Working example: http://www.jsfiddle.net/v4wkv/1/
If you would just call $('#invalid2').trigger('click') the toElement property would not be there and therefore fail. But as you can see, you can add like anything into the event object.
What are you trying to prevent? Someone messing with your client side script? You can do things like obfuscate your code but not much other than that. But even doing this is just making it more hassle than it's worth in my opinion.
If you don't want people doing certain things move the functionality to the server.
Sorry to be bearer of bad news.
You cannot really do anything against it, it would also be possible to write the complete function and then fire it.
But why is this a problem? If somebody is changing something client side it only affects him. Or are you trying to check some data? This MUST always be done in the backend, because you can never be sure what is really sent to it.
You can check event object (which is passed as first argument to handler) originalEvent.
It will be undefined if event is simulated by .click()
But it's completely useless. You cannot use javascript for security - client has full control over it (and firebug console is just most obvious tool). Client-side security checks should be only hint for user and protection against errors, malicious input can be stopped on server-side only.

Categories