I have an html list of checkboxes but I don't know how to get the 'document.attachEvent' to find those checkboxes on an onclick?
I wasn't sure if I could set the event model specific to the IE then in a document.attachEvent create a for loop that goes through every checkbox and handles each one? Also, my checkboxes are of all different names so I can't checkboxname.attachEvent unless I did that to each one.
My elements are dynamics enough that I tried adding en event to the broadest ancestor, which was the document to that I could use the event to get the target and type with no avail.
Much Thanks.
Some fixes to your code:
document.attachEvent('onclick', function (e) {
var target = e.srcElement;
if (target.type === 'checkbox') {
if(target.checked){
button.disabled = false;
} else {
button.disabled = true;
}
}
});
The third argument is not used in IE's event handling model. e.srcElement refers to the clicked element.
I'd suggest you to wrap the checkbox(es) in a div or some other element, and then attach the event listener to the wrapper. When your page gets larger, checking all clicks on the document will be a time consuming operation. If you've only one checkbox, it's better to attach the handler to itself ofcourse.
Related
I am trying to clone a div which has input fields.
But the eventListeners are not being fired.
Even though I am doing a deep clone in the following way -
var rows = document.querySelectorAll('.row');
var dupNode = rows[0].cloneNode(true);
sheet.appendChild(dupNode);
Here is the Demo
Each input has a click event and the cloned inputs are not registering with the click event. What am I missing ?
The simplest (and the most effective) thing to do is to bind single event listener on the #sheet container and benefit from event bubbling. In this case you can append as many new elements as you wish and will never need to bind events to them:
document.querySelector('#sheet').addEventListener('click', function(e) {
var target = e.target;
if (target.tagName == 'INPUT') {
alert("clicked");
}
}, false);
A little tricky part is that you need to check on what element event occurred and execute your code on those only you are interested in.
Demo: http://jsbin.com/mejegaqasu/1/edit?html,js,output
Event handlers are not copied during cloning. You will need to delegate the event handlers if you want them to work everywhere in the DOM after cloning.
Event delegation http://davidwalsh.name/event-delegate
cloneNode documentation https://developer.mozilla.org/en-US/docs/Web/API/Node.cloneNode
In jQuery, you can do the following:
$('#j_unoffered').on('click', '.icon_del', function () {...
This puts one handler on the element j_unoffered that fires if any descendant element with class icon_del is clicked. It applies, furthermore, to any subsequently created icon_del element.
I can get this working fine in Closure where the click is on the element itself.
goog.events.listen(
goog.dom.getElement('j_unoffered'),
goog.events.EventType.CLICK,
function(e) {...
How can I specify a parent event target in Closure that works for its children/descendants in the same way as the jQuery example?
I'm assuming I need to use setParentEventTarget somehow, but I'm not sure how to implement it for DOM events. Most of the documentation I've found pertains to custom dispatch events.
-- UPDATE --
I'm wondering if there is anything wrong with this rather simple solution:
goog.events.listen(
goog.dom.getElement('j_unoffered'),
goog.events.EventType.CLICK,
function(e) {
if (e.target.className.indexOf('icon_del') !== -1) {...
It still leaves this bound to the parent, but e.target allows a work-around. The fifth argument in listen (opt_handler) allows you to bind this to something else, so I guess that's an avenue, too.
I don't know about such possibility too, so I suggest other piece of code:
var addHandler = function(containerSelector, eventType, nestSelector, handler) {
var parent = goog.isString(containerSelector) ?
document.querySelector(containerSelector) :
containerSelector;
return goog.events.listen(
parent,
eventType,
function(e) {
var children = parent.querySelectorAll(nestSelector);
var needChild = goog.array.find(children, function(child) {
return goog.dom.contains(child, e.target);
});
if (needChild)
handler.call(needChild, e);
});
});
Usage:
addHandler('#elem', goog.events.EventType.CLICK, '.sub-class', function(e) {
console.log(e.target);
});
Update:
If you will use this e.target.className.indexOf('icon_del') there will be possibility to miss the right events. Consider a container div with id = container, it has couple of divs with class innerContainer, and each of them contains couple of divs with class = finalDiv. And consider you will add event handler with your code above, which will check e.target for innerContainer class. The problem is when user will click on finalDiv your handler will be called, but the event target will be finalDiv, which is not innerContainer, but contained by it. Your code will miss it, but it shouldn't. My code checks if e.target has nested class or contained by it, so you will not miss such events.
opt_handler can't really help you either, because there might be many nested elements you want to hanlde (which of them will you pass here? maybe all, but not that helpful, you can get them in event handler whenever you want), moreover they can be added dynamically after, so when you add handler you could not know about them.
In conclusion, I think doing such a job in an event handler is justified and most efficient.
What you are referring to is called event delegation
It seems that this is not possible (out of the box) with Google Closure Library; So my recommandation is to use jQuery or another similar event handling library that offers this functionality. If this is not possible or if you wanna do it by hand here's one possible approach (NOTE: this is not for production use)
var delegateClick = function(containerId, childrenClass, handler){
goog.events.listen(goog.dom.getElement(containerId), goog.events.EventType.CLICK, function(event){
var target = event.target;
//console.log(event);
while(target){
if ( target.className && target.className.split(" ").indexOf(childrenClass)!== -1) {
break;
}
target = target.parentNode;
}
if(target){
//handle event if still have target
handler.call(target, event);
}
});
}
//then use it, try this here: http://closure-library.googlecode.com/git/closure/goog/demos/index.html
//..select the nav context
delegateClick( 'demo-list' ,'goog-tree-icon', function(event){console.log(event);})
Here's a more in depth analysis of event delegation
Again, you should use a proven library for this, here are some options: jQuery, Zepto, Bean
I need to click on a document to call some function, but the problem is that when I click on some element that want it doesnt react, so the code:
<body>
<div class="some_element">
some element
</div>
</body>
and js:
$(document).click(function(){
//something to happen
})
and now if I click on the div with class="some_element" the document.click event will be called, but I need to call that event ONLY when I click on the document; or it is possible the make this element an exception?
More detailed:
$('#forma').click(function(e){
e.stopPropagation();
$('#assignment_type_list').slideUp();
})
Lets say #forma - its a parent element of those element, so when I click on the page I want to slideUp someElement and:
$('#assignment_type_select, #assignment_type_label').click(function(){
$('#assignment_type_list').slideToggle();
})
this is the elements when they are clicked the other element is toggled, but the problem is that when I click on this elements the $('#forma').click - also executes, because its parent and the e.stopPropagation() - doesn't help.
All this stopPropagation stuff is right, though this'll cause your script to throw errors on older versions of a certain browser. Guess which one? a cross-browser way:
$('#elem').click(function(e)
{
e = e || window.event;//IE doesn't pass the event object as standard to the handler
//event would normally work, but if you declared some event variable in the current scope
//all falls to pieces, so this e || window.event; thing is to be preferred (IMO)
if (e.stopPropagation)//check if method exists
{
e.stopPropagation();
return;
}
e.cancelBubble = true;//for IE
});
However, you wanted to check if the element that was actually clicked, is the one you need. The problem with that is, that the way the event is passed through the DOM. In W3C browsers the event is first passed to the document, and then clambers down to the element that was actually clicked (propagates through the dom). By contrast IE dispatches its events on the element itself, and then sends it up to the document (except for the change event triggered by select elements... to add insult to injury). What this effectively means is that a click event that is registered in to body element in W3C browsers might be on its way to a checkbox of sorts, or it could be a click inside an empty div. Again, in IE, when a click event reaches the body tag, it could have been dispatched too any element on the page. So it may prove useful in your case to google: event delegation, or turn to jQuery's .delegate() method.
Or check the event object to see if the event is allowed to propagate through or not:
var target = e.target || e.srcElement;//target now holds a reference to the clicked element
The property names neatly show the difference between the bubbling model and the propagating one: in the first case (srcElement), the event is coming from a source element in the dom. In the W3C propagating model, the event is cought while it's headed for a target element somewhere in the dom. Look at it like a heat-seeking missile (w3c) versus a shower of debris after the target was shot down (IE, always the destructive one, and in this case often to late to respond to the events, and therefore to late to handle them:P)
One way to do it is to check for the event's target.
$('html').click(function(event){
if (event.target != this){
}else{
//do stuff
}
});
Here's a working fiddle
Elements on the document are part of the document, so if you click "some_element" in the document, it is obvious that event registered on document will be fired/triggered. If you dont want to execute code which was for "document" then first get the element OR "event source" which originates this event, and check if it was "some_element" in your question above.
I have a bunch of <td>'s in a table that have onmouseover and onmouseout events. I would like for there to only be one event handler, say on the <table> itself that knows how to handle all the events to prevent so many event handlers on the page. Using native javascript, what is the best way to do this?
The events should bubble up to the table unless they are cancelled in the process from an element lower down in the hierarchy. You can setup event listeners for mouseover and mouseout events on the table and check if the target is a <td> cell. If so, then process those events. Here's a quick example.
var table = document.getElementById("myTable");
table.addEventListener('mouseover', function(event) {
var target = event.target;
if (target.nodeName == 'TD') {
target.style.backgroundColor = '#FFF';
}
});
See the compatibility table for mouse events. Since older versions of IE do not follow the DOM event registration model as per the w3c spec, you would have to use attachEvent to attach events for IE only.
Read up this article to get a better understanding of the differences in IE and other browsers.
Checkout a non cross-browser example here.
You should use unobtrusive javascript so that you have the two handlers set up for each but it doesn't clutter up the page. Use getElementsByTagName("td") to get the - modify that as appropriate. Then loop through and for each element, tableElem, tableElem.onMouseOver = function(){whatever... } and tableElem.onMouseOut = function(){whatever ... }.
You can make it terser if you want to use jQuery, e.g. $("td").mouseOver(function(){whatever;});
Example code:
table.onmouseover = function(event) {
var target = (event && event.target) || window.event.srcElement;
if(target.nodeName.toLowerCase() !== 'td') return;
// do stuff
};
Basically, I want to make clicking anywhere on a page except in an input field and on one block level element (an by extension all children there in) erase said input field. So I put an onclick event on the whole document. To keep the conditions above I put conditions before the clear instructions to only do it if the event did not arise from specific elements.
clearSearch = function (e){
e ? e : e = window.event;
e.target ? target = e.target : target = e.srcElement;
if (target.nodeName != "INPUT" && document.getElementById('ul'))
document.getElementById('input').value = "";
}
With this method if I want to keep clicking on the ul from causing the action I have to explicitly state it in this if statement. Then I have to do the same for the li elements under the ul. Then any other children I create.
Basically this seems really inefficient and I was wondering if someone could help me out in thinking up a better solution to this mess I have created for myself.
You can remove the complex logic from clearSearch and just have it clear the search box. Then add some new onClick handlers to the elements you don't want to call this method. In those handlers set event.cancelBubble to true to prevent the clearSearch function being called.
From quirksmode:
For a complete cross-browser experience do:
function doSomething(e)
{
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}