Javascript: Clicking a wrapper element but not a child element - javascript

I am writing some javascript (jQuery) that enables a div wrapped around a checkbox, when clicked, will toggle the checkbox element. However, the problem I'm running into is that when you click on the checkbox, it doesn't work because it's being toggled twice (at least I think that's what's happening).
Here's a demo.
Here's the code:
$('.checkbox-wrapper').click(function(){
var $checkbox = $(this).find('input[type="checkbox"]');
if ($checkbox.is(':checked')) {
$checkbox.attr('checked', false);
} else {
$checkbox.attr('checked', true);
}
});
How can I make it so that clicking the checkbox works as normal, but if you click in the surrounding area, it toggles the checkbox?
Solution:
Thanks to jAndy's comment for showing how this can be done by checking the event.target property:
$('.checkbox-wrapper').click(function(e){
if( e.target.nodeName === 'INPUT' ) {
e.stopPropagation();
return;
}
var $checkbox = $(this).find('input[type="checkbox"]');
if ($checkbox.is(':checked')) {
$checkbox.attr('checked', false);
} else {
$checkbox.attr('checked', true);
}
});
And as others have pointed out, this may not be the best example since you get the same functionality (without needing javascript) by wrapping the checkbox with a label tag instead of a div tag. Demo

Check the event.target property.
The target property can be the element that registered for the event or a descendant of it. It is often useful to compare event.target to this in order to determine if the event is being handled due to event bubbling.

Try this
$('.checkbox-wrapper').click(function(e){
if(!$(e.target).is(":checkbox")){
var $checkbox = $(this).find('input[type="checkbox"]');
if ($checkbox.is(':checked')) {
$checkbox.attr('checked', false);
} else {
$checkbox.attr('checked', true);
}
}
});

Try just wrapping a <label> around it.

it sounds like you've not stopped the event bubbling up to the parent elements
How to stop event bubbling on checkbox click

Related

JQuery .on("click", function() ) doesn't work

I am trying to fire JQuery when I checkbox is checked. At first I realized my JQuery only works for static elements. I read through a couple posts and found out that I need .on("click, function()) in order to fire that same piece of javascript for dynamically added elements.
However, this method still doesn't work for me. Can anyone help? Thank you.
$(document).ready(function(){
$("input[name='todo']").on('click', function(){
var isChecked = this.checked
if (isChecked == true){
$(this).next().remove();
$(this).remove();
}
if (isChecked == false)
{
alert("checkbox is NOT checked");
}
});
});
My example app: http://jsfiddle.net/JSFoo/7sK7T/8/
You need delegation:
$('#ToDoList').on('click', "input[name='todo']", function() { ... });
http://jsfiddle.net/7sK7T/9/
Documentation: http://api.jquery.com/on/#direct-and-delegated-events
PS: the important note - you need to specify the element you're binding your handler to as close to the target elements as possible. In your case it's #ToDoList, not body or document as other answers advice.
For dynamic elements you would want to do something like this
$(document).ready(function () {
$(document).on('click', "input[name='todo']", function () {
var isChecked = this.checked
if (isChecked == true) {
$(this).next().remove();
$(this).remove();
}
if (isChecked == false) {
alert("checkbox is NOT checked");
}
});
});
if you use it like you were before on('click') is basically the same as click(); because you are still selecting the elements required and applying the event to those elements, the way the above example works is it is only applies the event to the document then checking the selector when the event is fired.
You can also change document to a close container of the elements to be clicked if you wish.
This is called Event Delegation
jQuery Learn Event Delegation
The below is what you needed. It is slightly different syntax
$(document).on('click', "input[name='todo']", function(){
You bind the elements on document ready, it's before they're created.
You have to bind AFTER their creation.
Alternatively you can bind their container on document.ready:
$("#containerDiv").on('click', "input[name='todo']", function(){
// .... your code
});
"containerDiv" should be the parent element of those checkboxes, it should be in the page on document ready!

Making checkbox when container is clicked

I have a div element containing a checkbox, when I click the div I want the checkbox to toggle.
I did this by assigning the following code to $(#div).click:
$(this).find(":checkbox").prop("checked", !$(this).find(":checkbox").prop("checked"));
The problem now is that if the checkbox itself is clicked, the code above is still executed and thus the checkbox stays in the same state.
How would I fix this?
The event is bubbling up, you need to stop event propogation, one way is
$('#myDiv :checkbox').click(function(e){
e.stopPropagation();
});
Edit
In case you want to handle the event at div use this
$('div').click(function(e) {
// check if the event was triggered by an input box
if (e.target.nodeName != "INPUT") {
$(this).find(":checkbox").prop("checked", !$(this).find(":checkbox").prop("checked"));
}
// do something else
alert('hey there!');
});​
Demo - http://jsfiddle.net/QnbhX/
onclick() of div element, attach a javascript function which toggles the checkbox output. Isn't that a simpler way?
You should use a better selector like :
$('#myDiv').find(":checkbox").prop("checked", !$(this).find(":checkbox").prop("checked"));

jQuery click anywhere in the page except on 1 div

How can I trigger a function when I click anywhere on my page except on one div (id=menu_content) ?
You can apply click on body of document and cancel click processing if the click event is generated by div with id menu_content, This will bind event to single element and saving binding of click with every element except menu_content
$('body').click(function(evt){
if(evt.target.id == "menu_content")
return;
//For descendants of menu_content being clicked, remove this check if you do not want to put constraint on descendants.
if($(evt.target).closest('#menu_content').length)
return;
//Do processing of click event here for every element except with id menu_content
});
See the documentation for jQuery Event Target. Using the target property of the event object, you can detect where the click originated within the #menu_content element and, if so, terminate the click handler early. You will have to use .closest() to handle cases where the click originated in a descendant of #menu_content.
$(document).click(function(e){
// Check if click was triggered on or within #menu_content
if( $(e.target).closest("#menu_content").length > 0 ) {
return false;
}
// Otherwise
// trigger your click function
});
try this
$('html').click(function() {
//your stuf
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
you can also use the outside events
I know that this question has been answered, And all the answers are nice.
But I wanted to add my two cents to this question for people who have similar (but not exactly the same) problem.
In a more general way, we can do something like this:
$('body').click(function(evt){
if(!$(evt.target).is('#menu_content')) {
//event handling code
}
});
This way we can handle not only events fired by anything except element with id menu_content but also events that are fired by anything except any element that we can select using CSS selectors.
For instance in the following code snippet I am getting events fired by any element except all <li> elements which are descendants of div element with id myNavbar.
$('body').click(function(evt){
if(!$(evt.target).is('div#myNavbar li')) {
//event handling code
}
});
here is what i did. wanted to make sure i could click any of the children in my datepicker without closing it.
$('html').click(function(e){
if (e.target.id == 'menu_content' || $(e.target).parents('#menu_content').length > 0) {
// clicked menu content or children
} else {
// didnt click menu content
}
});
my actual code:
$('html').click(function(e){
if (e.target.id != 'datepicker'
&& $(e.target).parents('#datepicker').length == 0
&& !$(e.target).hasClass('datepicker')
) {
$('#datepicker').remove();
}
});
You could try this:
$(":not(#menu_content)").on("click", function(e) {
e.stopPropagation();
// Run your function when clicked anywhere except #menu_content
// Use with caution, 'cause it will prevent clicking on other elements
});
$("#menu_content").on("click", function(e) {
e.stopPropagation();
// Run when clicked on #menu_content
});

click outside DIV

<body>
<div id="aaa">
<div id="bbb">
</div>
</div>
</body>
$(#?????).click(function(){
$('#bbb').hide();
})
http://jsfiddle.net/GkRY2/
What i must use if i want hide #bbb if user click outside box #bbb? But if i click on div #bbb then box is still visible - only outside.
$('body').click(function(e){
if( e.target.id == 'bbb' )
{ return true; }
else
{ $('#bbb').hide(); }
});
A note of explanation: There are a few ways to do this, either way we need to listen for a click on a parent element, weather it be a direct parent like #aaa or a distant parent like the body or the document. This way we can capture clicks that occur outside of #bbb.
Now that we have that we need the .hide to NOT occur if the user did click inside of #bbb. We can do this two ways
Stop propagation if the user clicks on #bbb. This will make the click event not 'bubble' up to the parent. That way the click event never reaches the parent and so #bbb will not hide. I personally don't like this method because stop propagation will so ALL click events from bubbling, and you may have click events that you would like to bubble to a local parent and not a distant parent. Or you may have listeners delegated from a distant parent, which will stop working if click propagation is stopped.
Check for the #bbb element in the parent listener. This is the method shown above. Basically this listens on a distant parent, and when a click occurs it checks to see if that click is on #bbb specifically. If it IS NOT on #bbb .hide is fired, otherwise it returns true, so other things that may be tied into the click event will continue working. I prefer this method for that reason alone, but secondarily its a-little bit more readable and understandable.
Finally the manner in which you check to see if the click originated at #bbb you have many options. Any will work, the pattern is the real meat of this thing.
http://jsfiddle.net/tpBq4/ //Modded from #Raminson who's answer is very similar.
New suggestion, leverage event bubbling without jQuery.
var isOutSide = true
bbb = documment.getElementById('bbb');
document.body.addEventListener('click', function(){
if(!isOutSide){
bbb.style.display = 'none';
}
isOutSide = true;
});
bbb.addEventListener('click', function(){
isOutSide = false;
});
Catch the click event as it bubbles-up to the document element. When it hits the document element, hide the element. Then in a click event handler for the element, stop the propagation of the event so it doesn't reach the document element:
$(function () {
$(document).on('click', function () {
$('#bbb').hide();
});
$('#bbb').on('click', function (event) {
event.stopPropagation();
});
});
Here is a demo: http://jsfiddle.net/KVXNL/
Docs for event.stopPropagation(): http://api.jquery.com/event.stopPropagation/
I made a plugin that does this. It preserves the value for this where as these other solutions' this value will refer to document.
https://github.com/tylercrompton/clickOut
Use:
$('#bbb').clickOut(function () {
$(this).hide();
});
You can use target property of the event object, try the following:
$(document).click(function(e) {
if (e.target.id != 'bbb') {
$('#bbb').hide();
}
})
DEMO
This will work
$("#aaa").click(function(){
$('#bbb').hide();
});
$("#bbb").click(function(event){
event.stopPropagation();
})​
Becouse bbb is inside the aaa the event will "bubbel up to aaa". So you have to stop the bubbling by using the event.stopPropagation when bbb is clicked
http://jsfiddle.net/GkRY2/5/
OK
* this is none jquery. you can easly modify it to work with IE
first create helper method to facilitate codding don't get confused with JQuery $()
function $g(element) {
return document.getElementById(element);
}
create our listener class
function globalClickEventListener(obj){
this.fire = function(event){
obj.onOutSideClick(event);
}
}
let's say we need to capture every click on document body
so we need to create listeners array and initialize our work. This method will be called on load
function initialize(){
// $g('body') will return reference to our document body. parameter 'body' is the id of our document body
$g('body').globalClickEventListeners = new Array();
$g('body').addGlobalClickEventListener = function (listener)
{
$g('body').globalClickEventListeners.push(listener);
}
// capture onclick event on document body and inform all listeners
$g('body').onclick = function(event) {
for(var i =0;i < $g('body').globalClickEventListeners.length; i++){
$g('body').globalClickEventListeners[i].fire(event);
}
}
}
after initialization we create event listener and pass reference of the object that needs to know every clcik on our document
function goListening(){
var icanSeeEveryClick = $g('myid');
var lsnr = new globalClickEventListener(icanSeeEveryClick);
// add our listener to listeners array
$g('body').addGlobalClickEventListener(lsnr);
// add event handling method to div
icanSeeEveryClick.onOutSideClick = function (event){
alert('Element with id : ' + event.target.id + ' has been clicked');
}
}
* Take into account the document body height and width
* Remove event listeners when you don't need them
$(document).click(function(event) {
if(!$(event.target).closest('#elementId').length) {
if($('#elementId').is(":visible")) {
$('#elementId').hide('fast');
}
}
})
Change the "#elementId" with your div.

Simple click event delegation not working

I have a div
<div class="myDiv">
somelink
<div class="anotherDiv">somediv</div>
</div>
Now, using event delegation and the concept of bubbling I would like to intercept clicks from any of myDiv, myLink and anotherDiv.
According to best practices this could be done by listening for clicks globally (hence the term 'delegation') on the document itself
$(document).click(function(e) {
var $eventElem = $(e.target);
var bStopDefaultClickAction = false;
if ($eventElem.is('.myDiv'))
{
alert('Never alerts when clicking on myLink or anotherDiv, why????');
bStopDefaultClickAction = true;
}
return bStopDefaultClickAction;
});
See my alert question above. I was under the impression that clicks bubble. And it somewhat does because the document actually receives my click and starts delegating. But the bubbling mechanism for clicks on myLink and anotherDiv doesn't seem to work as the if-statement doesn't kick in.
Or is it like this: clicks only bubble one step, from the clicked src element to the assigned delegation object (in this case the document)? If that's the case, then I need to handle the delegation like this:
$('.myDiv').click(function(e) {
//...as before
});
But this kind of defeates the purpose of delegation as I now must have lots of 'myDiv' handlers and possibly others... it's dead easy to just have one 'document' event delegation object.
Anyone knows how this works?
You should use live event from JQuery (since 1.3), it use event delegation :
http://docs.jquery.com/Events/live
So you code will be :
$(".myDiv").live("click", function(){
alert('Alert when clicking on myLink elements. Event delegation powaa !');
});
With that, you have all the benefices of event delegation (faster, one event listener etc..), without the pain ;-)
The event target will not change. You need to mirror what jquery live does and actually check if $eventElem.closest('. myDiv') provides a match.
Try:
$(document).click(function(e) {
var $eventElem = $(e.target);
var bStopDefaultClickAction = false;
if ( $eventElem.closest('.myDiv').length )
{
alert('Never alerts when clicking on myLink or anotherDiv, why????');
bStopDefaultClickAction = true;
}
return bStopDefaultClickAction;
});
Event.target is always the element that triggered the event, so when you click on 'myLink' or 'anotherDiv' you store a reference to these objects using $(e.target); So what you do in effect is: $('.myLink').is('.myDiv') which returns false, and that's why the alert() is not executed.
If you want to use event delegation this way, you should check wheter event.target is the element or any of its children, using jQuery it could be done like this:
$(e.target).is('.myDiv, .myDiv *')
Seems to work fine to me. Try it here: http://jsbin.com/uwari
Check this out: One click handler in one page
var page = document.getElementById("contentWrapper");
page.addEventListener("click", function (e) {
var target, clickTarget, propagationFlag;
target = e.target || e.srcElement;
while (target !== page) {
clickTarget = target.getAttribute("data-clickTarget");
if (clickTarget) {
clickHandler[clickTarget](e);
propagationFlag = target.getAttribute("data-propagationFlag");
}
if (propagationFlag === "true") {
break;
}
target = target.parentNode;
}
});

Categories

Resources