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.
Related
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.
EDIT: This is a bug in Firefox, and cannot be resolved without editing Firefox directly, which I have no desire to do. I consider this question resolved for the purpose of asking other people for he.
I am trying to create a pixel-based drawing program using SVG. I initialize the event handlers on a couple of <polygon>s inside a <defs>, and then copy that a bunch of times with <use>s to make the the canvas, and it works fine, in Chrome.
But regardless of whether I assign the .onclick attribute or use .addEventListener, my <use>s don't register anything in Firefox. As I was researching solutions for this, I found another thematically, and possibly technically, related phenomenon: <use>s copied using .cloneNode also do not retain event listeners. I could assign each <use> element the event listeners as I generate them, but it seems to me that that is the least desirable solution.
The only solution I came up with is to register the same event listener on the use element. After that it works in Firefox, but not anymore in IE, Safari and Chrome, because the event gets fired twice. Therefore you have to de-register the event of the copied SVG element. It is a workaround, but no good solution. I hope someone else can give a better answer for that.
Given the following code
<div id="app">
<div id="foo" />
</div>
<script>
$('#foo').bind('click', function(){});
</script>
I plan on replacing the contents of #app [e.g. $('#app').html('...');, or innerHTML = '...';]. I know that I can use jQuery's .remove() which calls a 'destroy' handler that unbinds events. The fact that there is a destroy handler set up to remove events leads me to believe that without unbinding the events, when the DOM element is removed, the handler will still exist in memory.
So, if the DOM element #foo no longer exists, does the handler disappear as well, or does it get lost in browser memory?
jQuery keeps track of event handlers itself, which is part of why you need to use unbind (nowadays it's off) if you're removing the element from the DOM not through a jQuery method (if you use jQuery's empty or remove, as you mentioned it handles this itself inernally). This is so jQuery knows it can release its reference to the handler.
If it weren't for that, then in theory, you wouldn't have to do anything because once the DOM element is removed from memory, it's no longer reachable, and so in theory shouldn't keep the event handler in memory. That's the theory. The reality is very different, it can be very easy to end up with a situation (particularly on IE) where neither the DOM element nor the event handler can get cleaned up because they're each causing the other to stick around — a memory leak. JavaScript has no issue with circular references (it understands them and can release two things that are pointing to each other as long as nothing else is pointing to them), but the DOM part of the browser is likely to be written in a different language with a different garbage collection mechanism (IE uses COM, which uses reference counting rather than reachability). jQuery helps you avoid this pitfall with IE (part of why it keeps track of event handlers), but you have to use unbind (nowadays off) (or remove elements via empty, remove, etc.) as a consequence.
The take away message: As you hook, so shall you unhook. :-) (And/or use jQuery when removing elements, since it will handle this.)
Somewhat related: If you're adding and removing elements a lot, you might look to see if event delegation (which jQuery makes really easy via the delegation signatures for on) might help.
Just happened to read the docs on jQuery's empty() method:
To avoid memory leaks, jQuery removes
other constructs such as data and
event handlers from the child elements
before removing the elements
themselves.
The following codes don't work:
function dnd(){
}
var ele = document.getElementById("relative");
ele.addEventListener("click",dnd,false);
document.write(ele.onclick);
the output is undefined. I guess the output should be function onclick(event){dnd();}
What should I do to solve this problem?
Any suggestion is appreciated.
There are 3 common ways to attach events to DOM nodes.
The addEventListener() method is the way to register an event listener as specified in W3C DOM. It has many benefits, but doesn't work in Internet Explorer. For Internet Explorer you'd have to use the attachEvent() method, which offers similar functionality.
On the other hand, the onclick property is an older, but more supported way to attach event handlers. However it has certain disadvantages, such as allowing just one event handler for each event.
As for how to get back the event handlers that are attached to a particular node, it depends on the method you use to attach the events. The problem with your example is that you're using the addEventListener() method to attach the event, and then trying to read it using the onclick property.
You may want to check out the following Stack Overflow post for further reading into this topic, especially the post by #Crescent Fresh:
How to find event listeners on a DOM node?
The onclick property is only going to show you an event handler if it is registered inline via the markup (e.g, <div id="relative" onclick="alert('foo');"></div>).
Why do you need to read the event handler? This is a relatively strange thing to do...
There are different ways of establishing event handlers, and they use different mechanisms inside the DOM. In particular — and as you've discovered empirically — adding an event listener via "addEventListener" does not affect the "onclick" attribute of the element. The mechanisms are simply separate.
This is one of the current limitation of W3C event registration model. if you register using JS methods then there is no standard way to get the handlers.
Latest DOM LEVEL 3 Events W3C adds eventListenerList spec. i guess we are lacking of some close support for this API across browsers.
Of-course if you add your method using tradition way
then your example would work.
Some StackOverFlow Links
link text
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)