I am having a specific problem with the JS event "onclick" and how it works in a very specific case. I have a set of nested DIVs that looks like this (somewhat):
In code:
<div id="A" ...>
<div id="B" ...> </div>
<div id="C" ...> </div>
</div>
The exact position of the DIVs are arbitrary, just know that there are nested DIVs.
DIV A has an onclick event attached to it.
When a user clicks on the divs I intend for the srcElement to without exception be div A.
Or maybe I'm just not using the right property.
Basically I want to be able to get the name of the div whose onclick event was called.
from inside the onclick function
If the onclick is on A, then this in the handler will represent that element.
document.getElementById('A').onclick = function() {
// "this" is the A element
alert( this.id ); // alert the ID attribute of the element
};
Well, divs don't have a name per se in HTML4. They can have a title, though. In JS without a framework, you could get the title this way:
document.getElementById("A").onclick = function() {
alert(this.title);
}
This appears to work well in my tests.
You can use Patrick's solution provided you attach the listener as a property of the element.
If you use IE's attachEvent method, it will not work as it doesn't set the listener's this keyword to the element when called. There are numerous work arounds for that. IE 9 introduces addEventListener, hopefully with the this keyword set per other browsers.
Event.srcElement is the IE equivalent of the W3C Event.target property and is the element on which the event originally occured. For bubbling events, to find the element whose handler called the listener, it is best to set the listener's this keyword in the call so that you can refer to the associated element as 'this'.
Note also that there is no consistency in the order that listeners will be called when attached using attachEvent or addEventListener (HTML5 tries to fix that, but it isn't widely implemented yet and will not fix attachEvent's order anyway).
It is usually best to keep event registration as simple as possible. Once you have a complex system, you'll likelyl need your own event registration and calling system to avoid the various browser peculiarities (there are a number of those).
--
Rob
Related
JavaScript
function productBox(event){
event.stopPropagation();
console.log(event.target);
}
var product = document.getElementsByClassName('product');
for (var g = 0, length = product.length; g < length; g++){
console.log('here');
product[g].addEventListener('click',productBox);
}
HTML
<div class="product">
<div class="productContent">
<img src="file:///C|/Users/Jacob/Downloads/12939681_1597112303636437_733183642_n.png" />
</div>
<div class="productName">
Here
</div>
<div class="productDescription">
</div>
So the problem lies in the fact that when the product element is clicked, event.target returns the actual child element of the event listener. For example, i click a "product" and it'll return productContent, productName or productDescription as the target, when actually what i want is the "product" element and then to do a .childNodes and find the image within that.
Please note jQuery is not an option, it is 30kb of stuff i won't use as this is a static html page with barely any javascript.
I've thought perhaps,
doing a check if the element is 'product' if not, take the parent and check if it's a 'product', if not go to that parent and so on. Then find the img tag within that. But i feel like that is a long winded work around.
Any thoughts?
To get the element to which the handler is bound, you can use this within the handler.
As #DaveNewton pointed out, the event object has a .currentTarget property that can be used as well. This is nice because you can have functions that have a manually bound this using .bind() or you may be using the new arrow functions, which have a this defined by its original environment, making it impossible to get the bound element that way.
You can use parentElement property of the target.
function productBox(event){
var target = event.target;
var parent = target.parentElement;//parent of "target"
//Rest of your code
}
I asked a similar question: if you have a element.click(function(){}) and the element has two or multiple siblings. If the event trigger is attached to the parent and not the target, this post popped up and is actually irrelevant to my question so I decided to post my search here for someone else.
I used this method:
if (target.closest('.NEftune').attr('rel') != undefined){
/*do something here*/
}
The closest method in jQuery and JavaScript starts from the clicked target and then bubbles up until it finds an attribute you looking for. In my case the event was attached to an Element with the class (.NEftune) and by adding an extra attribute I could determine if I was inside the container (.NEftune) which has an image inside.
I know it's a bit late, but the simplest solution to get the actual target to which the event handler was attached to is to use the event currentTarget property.
This will avoid unnecessary further DOM check and works out of the box on all modern browsers.
Event.currentTarget on MDN
The currentTarget read-only property of the Event interface identifies the current target for the event, as the event traverses the DOM. It always refers to the element to which the event handler has been attached, as opposed to Event.target, which identifies the element on which the event occurred and which may be its descendant.
My code is this:
document.addEventListener('click', function(ev){
if(ev.path[0].className == 'linkTogether'){//do something}
if(ev.path[0].id == "createNewPage"){//do something}
});
Which has actually been working well for dynamically created buttons and nodes, but something just feels off about it. So I'm wondering if this is best practice or if there is a better way to add event listeners to dynamically created elements.
Thanks,
Jack
In specific cases where you have huge amount of objects that behave in the same way you can use this technique (adding event listener to their parent) to improve the performance of your script.
In a general page however you just have to many different objects and iterating trough all of them to check which one you've clicked is not faster.
Here is an article for the technique you are referring to - event delegation.
This is the proper pattern for creating event listeners that will work for dynamically-added elements. It's essentially the same approach as used by jQuery's event delegation methods (e.g. .on).
However, it does have performance implications. Every time you click anywhere in the document, the code will run, and have to go through the entire list of event bindings that you need to check. You can improve this by adding your event listener to a more specific element. If the dynamic elements are always added inside a specific DIV, add your listener to that DIV rather than document.
This also avoids another pitfall of event delegation. Event delegation depends on the event bubbling up from an inner element to all its containers. But if there are any handlers along the way that call event.stopPropagation, the event won't make it out to document. If you add the listener to a lower element, you're less likely to have a conflict like that.
And I would like to add, if you create elements dynamically, better you include the listeners inline and filter in the function what you want to do.
<div class="form_ID1" onclick="myfunction(this, event);">
... children bubbling
... Use if statements in caught js myFunction function.
In myfunction function you will capture the child element by event.target and the element that contains the listener by this.className.
There are scenarios however that you need a universal ( document ) click event. e.g You need to close a pop up when you click outside of a pop up !! It is like: e.g.
if (clicked.className != popup.className) popup.remove()
Even in this case there is a workaround by inserting an onblur="myfunction(this); in the parent DIV of the popup.
I have a difficult question.
events = $._data( element[0], 'events');
$.each(events, function(_event_name, _event_handler){
var _handlers=[];
for(var i= 0 ;i < _event_handler.length;i ++)
_handlers.push(_event_handler[i].handler);
event_handler.push(_handlers);
event_name.push(_event_name);
});
element.off();
I have above code to successfully read all what event name and its handler assigned for a element.
Then I save each of them into event_name and event_handler before I turn the events off;
However, this method is only work on when the events are directly assigned for the element.
When the events are delegated assigned, how can I develop the code to do that?
$(document).on('click', '#id', handler);
Above code will only show the event name click and its handler handler, but no the name or selector of the delegate assigned element #id.
I want to know how can I read the name of delegate assigned element out that I can do delegate off or all the events on parent will be off.
Thank you very much for your advice.
The selector is stored in the object within the array of click events under selector as seen below.
It seems that the selector field is empty when you look at click events bound directly to the element.
I would assume you can build a check into your code somehow. Mind you, the selector property is apparently only intended for internal use as I had a question myself in the past about it, when I found it contained occasionally strange values.
To that end, though it might work for you, I do not know how reliable it is or even if it is going to be used in future versions of jQuery.
However, if you only use it for delegate events you might find it contains useful values but keep an eye on it.
If I have some HTML like this:
<a onmouseover='SetTopLeft(this);'href='#'>Click me!</a>
Can I get both the object AND the event in the function? So, for example, can I have a method signature like this?
function SetTopLeft(e, o)
...where e is the event and o is 'this'? I may actually not need the object, but I think I probably DO need the event. I wouldn't mind understanding a little better how this works in JavaScript - i.e., when/how can I pass an event and when/how can I pass the calling object? Can I choose which to pass? Can I pass both?
Basically, what I really need to do is get the mouse coordinates within the DIV within which the anchor is located (even if that DIV is only a portion of a web page and whether or not the browser is full-screen) and I'm having a terrible time getting it done. Most of the examples I have seen for getting these coordinates within some element use the event and the pageX and pageY properties of that event.
By the way, this must work in IE 6 onward. Firefox would be good, too. Others are not necessary.
Yes, in the inline code this refers to the HTML DOM object for the element, and event refers to the event object. So you could do the following:
HTML
<a onmouseover='SetTopLeft(event, this);' href='#'>Click me!</a>
JavaScript
function SetTopLeft(e, obj) {...}
In general you should avoid using inline event handlers. It mixes representation (HTML) with logic (JavaScript). quirksmode.org offers a nice collection of articles of all there is to know about event handling.
Since inside an event handler, this typically refers to element the handler is bound to, you can also explicitly set this to the element and pass the event object as first argument:
<a onmouseover='SetTopLeft.call(this, event);'href='#'>Click me!</a>
See .call() [MDN] for more information.
Besides that, if your link is not linking to anything, better use a simple span element or a button and style it accordingly.
I am doing browser automation using C#, and I would like to modify or possibly just eliminate event handlers on some of the html elements in webpages that I am looking at. E.g., suppose there is a button there which might (or might not) have an attached onClick event. How do I go about:
- finding out if there are any event handlers attached to onClick for it?
- removing them?
Replacing element with its own clone should effectively discard all of its event listeners (well, technically listeners are still on an element, but since an element is replaced with its own clone, it looks as if listeners were simply removed):
el.parentNode.replaceChild(el.cloneNode(true), el);
Unfortunately, this won't work in IE, since IE erroneously transfers event listeners of an element on clone. To work around that you can reassign innerHTML of element's parentNode:
el.parentNode.innerHTML = el.parentNode.innerHTML;
Note that this will not only remove event listeners of an element, but also listeners of all of element's siblings.
Alternatively, you can work around IE issue by reassigning outerHTML of an element:
el.outerHTML = el.outerHTML;
This depends on how the event handlers have been attached to the element.
If they are attached using addEventListener or one of the proprietary addWhatever listener methods, there is no way to list them.
If they are attached by modifying the event property, ie. node.onclick = whatever, then you can read the value of the property to get the function and it'll work the same as any other JS func.
There is a third way too:
You can override the default addEventHandler/addListener behavior if the code you automate uses those. By doing this, you can replace the default behavior by one which pushes each handler into an array, which you can then loop over yourself.
The following code might work:
var oldAddEventListener = HTMLElement.prototype.addEventListener;
HTMLElement.prototype.addEventListener = function(event, handler, bubbling) {
/* do whatever you want with event parameters */
oldAddEventListener.call(this, event, handler, bubbling);
}
As far as I know, it's not currently possible to use javascript to get all the event handlers attached to an element.
See this link for more info:
http://www.howtocreate.co.uk/tutorials/javascript/domevents