In a previous question I asked about toggling a child div from the parent which was answered and the answer given works. Now I have a different issue: When I click anywhere within the inner (such as the link "inside inner div" it causes the outer div to toggle. I want the inner div to toggle only by itself. If I click as described, then div containing "This is the inner div" should disappear but the class openData should be active for the outer div. Instead it gets set to closeData.
This is my code:
<html>
<head>
<style>
.openData{font-size:18px;background-color:lightgreen;color:blue;}
.closeData{font-size:18px;background-color:lightblue;color:blue;)
</style>
<script src="/js/jquery.js"></script>
<script>
function insideDiv(f) {
var e=window.event;
e.cancelBubble=true;
e.stopPropagation();
toggleDiv(f);
return false;
}
function setCursor(e) {
e.style.cursor="pointer";
}
function clearCursor(e) {
e.style.cursor="default";
}
function togglePlusMinus(f) {
$(f).children('div:first').toggle();
$(f).toggleClass("openData closeData");
}
</script>
</head>
<body>
<div class="closeData" onmouseover="setCursor(this);" onmouseout="clearCursor(this);" onclick="togglePlusMinus(this);">This is the outer div
<p>Inside outer div</p>
<div style="display:none;;background-color:#ffffff;" onclick="insideDiv(this);">This is the inner div
<div>
<p style="margin-left:20px;margin-top:0px;font-size:15px">inside inner div</p>
</div>
</div>
<p>Also in outer div</p>
</div>
This is just some trailing text.
</body>
</html>
Everything I've read says that I need to stop the click propagation (which I think I am doing correctly).
This is the smallest working failure example. The actual program shows expandable data sets nested within each other.
Try adding an eventListener to your inner div and use stopPropagation like in the parent.
var myDiv = document.querySelector('#myDiv'); //assuming your child div has a "myDiv" id
myDiv.addEventListener(pEvent) {
pEvent.stopPropagation();
};
EDIT :
To make it work with every first child div of each .closeData element you can do (assuming you have only one direct child div):
//Selects all .closeData elements
var parents = document.querySelectorAll('.closeData');
//For each .closeData, find the first div and stops the propagation
for(var i = 0; i < parents.length; i++) {
var child = parents[i].querySelector('div');
child.addEventListener('click', function(pEvent) {
pEvent.stopPropagation();
})
}
Edit: it seems that the problem is that you are calling stopPropagation to the window event, not the actual element that you are clicking.
You are looking most likely for the event bubbling tutorial (at least if I understood correctly). Please check this out as it describes your problem:
http://javascript.info/tutorial/bubbling-and-capturing
And in shorthand to stop the inner event to propagate to outer div use:
event.stopPropagation()
and for IE9 earlier:
event.cancelBubble = true
If only the outer div function is firing try setting a z-index on the inner div that is higher than the parent div.
Explicitly pass event to your function such that
Onclick="function(event || window.event, this)"
Then add a a param for the event on your function and do event propagation cancelation on that.
Related
I'm developing a web page using jQuery. In this web page, there is a div tag that contains a p and a button tag.
The HTML code is like this:
<div class="container">
<div id="attribute" style="border:solid;border-width:2px;border-color:#ccc">
<p id="cell-content" style="display:inline">Id</p>
<button id="remark-view" class="btn btn-primary">Detail</button>
</div>
</div>
and the corresponding JavaScript code is like this:
$(document).ready(function(){
$("#attribute").on('click', function(){
console.log("click on the div attribute");
});
$("#attribute").on('dblclick', function(){
console.log("double click on the div attribute");
});
$("#remark-view").on('click', function(){
console.log("click on the button remark-view");
});
});
As the code shows, a outer div has a p and button child element, and the outer div element listens on the single click and double click event while the inner button element listens on the single click event.
When I run the code in my browser and click on the button, the console shows that both click functions of the outer div and inner button element are called, which is against my purpose: only the click function of inner button should be called at this situation. Thus, is there any way to block the click event for the father element(in this case, outer div element).In other words, is there any way to stop passing the click event to the father element after the child element has handled it?
Thank you in advance!
stopPropagation function will stop the event from bubbling up the DOM.
$("#remark-view").on('click', function(event){
console.log("click on the button remark-view");
event.stopPropagation()
});
From the jQuery documentation
Prevents the event from bubbling up the DOM tree, preventing any
parent handlers from being notified of the event.
This is something I use in one of my sites to do something similar to your problem. What the below code does is it prevents the middle div from closing if the button click is on that div.
//Function for the pop up with the background dimmer
$(document).mouseup(function(x) {
var container = $("#divContent"),
dimmer = $('#bgDimmer');
if (container.is(":visible")) {
if (!container.is(x.target) //check if the target of the click isn't the container...
&& container.has(x.target).length === 0) {
container.hide();
dimmer.hide();
}
}
});
Let try to relate to your code.
//Function for the pop up with the background dimmer
$(document).mouseup(function(x) {
var attribute = $('#attribute');
if (attribute.is(":visible")) {
if (!attribute.is(x.target) //check if the target of the click isn't the container...
&& attribute.has(x.target).length === 0) {
console.log("click on the button remark-view");
}
}
});
I have a series of click events which are causing me some propagation issues. The data for this container is loaded in via ajax hence the body on click method. Here's the code I have:
$(function () {
$("body").on("click","#top-div",function(){
console.log("top event");
});
$("body").on("click","#middle-div",function(e){
e.stopPropagation();
});
$("body").on("click","#bottom-div",function(){
console.log("bottom event");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top-div">
top
<div id="middle-div">
middle
<div id="bottom-div">
bottom
</div>
</div>
</div>
The top div has an event which the middle one needs to not inherit hence the stopPropagation(). However the bottom one needs to have an event of its own but the stopPropagation() from the middle one is stopping it from executing the event (if I remove the stop propagation the bottom event triggers but coincidentally so does the top)
How can I get around this bubbling issue?
I think what you want to happen is that the propagation is only stopped if it originated from the middle div; if it originated from the bottom div you want the event to bubble all the way to the top.
You need to call stopPropagation conditionally. If the event did not originate on or inside #bottom-div, you want to call it. You can test for this using closest:
if (!$(e.target).closest('#bottom-div').length) {
e.stopPropagation();
}
It is better to use closest for this, rather than testing the element's id directly, because with closest it will continue to work as expected even if there are other elements (a or em, for example) within the various divs.
$(function () {
$("body").on("click","#top-div",function(){
console.log("top event");
});
$("body").on("click","#middle-div",function(e){
if (!$(e.target).closest('#bottom-div').length) {
e.stopPropagation();
}
});
$("body").on("click","#bottom-div",function(){
console.log("bottom event");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top-div">
top
<div id="middle-div">
middle
<div id="bottom-div">
bottom
</div>
</div>
</div>
Bubbling works from most nested element to tree root, I think You misunderstanding it in some way. So for example click in bottom-div will:
call event on bottom-div --> call event on middle-div --> call event on top-div.
As I understand the need You want to some how check what div was clicked, so to have control on it just create single event and check clicked target like that:
$("body").on("click","#top-div",function(e){
if ( e.target.id ==='top-div'){
//click in top div
}
if ( e.target.id ==='middle-div'){
//click in middle div
}
if ( e.target.id ==='bottom-div'){
//click in bottom div
}
});
In this proposition You know exactly which div was clicked, but it will always be the most nested one, so target will be middle-div only when will be clicked part of middle-div which is not bottom-div, it will be for example padding space, the same is with top-div it will be target only if click will be in space without child elements.
More about event bubbling You can find in http://maciejsikora.com/standard-events-vs-event-delegation/.
Instead of attaching event to every div. You can consider doing it as follows
check this snippet
$(document).ready(function() {
$("body").on("click", "div", function(event) {
console.log(event.currentTarget.id + "event");
if (event.target.id === "middle-div") {
event.stopPropagation()
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top-div">
hello
<div id="middle-div">
hi
<div id="bottom-div">
bott-mid-v
</div>
</div>
</div>
Hope it helps
All the answers I could find in relation to this question are trying to do things the opposite direction. So… is this even possible?
I have a simple snippet of HTML
<div class="launcher" data-info="something">
Text, or image, or whatever
</div>
The launcher class attaches an event to the div, which takes the data-info value and does something with it (e.g. launches a video in an overlay).
How do I get only the div's event to fire? (The inner link is there for no-script and semantic reasons; the full code includes various schema.org properties.)
Embedded script - onClick="return false;" - is not an option; behavioural coupling is evil. I have no problem attaching another event to the <a>, because that is a class definition. Yes, I need it to work pre IE9 (because there are people out there who…) A pure javascript (not jQuery) response would be appreciated.
You can check the event.target as follows:
var parent = document.getElementsByClassName("launcher")[0];
parent.addEventListener("click",function(e){
if(e.target.className.indexOf("launcher")> -1){
alert("click!");
}
});
div{
height:50px;
background:dodgerblue;
}
<div class="launcher" data-info="something">
Text, or image, or whatever
</div>
Or you can add another click event listener for the anchor and prevent it's propagation as follows:
var parent = document.getElementsByClassName("launcher")[0];
parent.addEventListener("click",function(e){
if(e.target.className.indexOf("launcher")> -1){
alert("click!");
}
});
var anchor = document.querySelector(".launcher a");
anchor.addEventListener("click",function(e){
e.stopPropagation();
})
div{
height:50px;
background:dodgerblue;
}
<div class="launcher" data-info="something">
Text, or image, or whatever
</div>
I have a click function bound to many elements. It is possible that sometimes these elements may sit within one another. So, the click event is bound to a child and also bound to its parent. The method is specific to the element clicked. Naturally, because of event bubbling, the child's event is fired first, and then the parents. I cannot have them both called at the same time because the parents event overwrites the event of the child. So I could use event.stopPropagation() so only the first element clicked receives the event. The problem is that there are other click events also attached to the element, for example, I am using jQuery's draggable on these elements. If I stop the propagation of the click event, then draggable doesn't work, and the following click events are not called.
So my question is: Is there a way to stop the event bubbling of the method the event will call and not the entire event?
Brilliant John, but here is the problem:
<div id="Elm1"><!-- relative -->
<div class="Elmchildren"></div><!-- absolute-->
<div class="Elmchildren"></div><!-- absolute-->
<div class="Elmchildren"></div><!-- absolute-->
<div id="Elm2"><!-- relative -->
<div class="Elmchildren"></div><!-- absolute-->
<div class="Elmchildren"></div><!-- absolute-->
<div class="Elmchildren"></div><!-- absolute-->
</div>
</div>
Click event is bound to #Elm1 and #Elm2. The .Elmchildren are width and height 100%. So they are actually the current targets.
try someting like this
$(mySelector).click(function(evt) {
if (evt.target == evt.currentTarget) {
///run your code. The if statment will only run this click event on the target element
///all other click events will still run.
}
});
The suggested solution
evt.target == evt.currentTarget
is nice, but there are cases where it does not help.
Example: A (suckerfish-style) menu structure with nested ul/li lists.
The mousemove event comes from a link inside a list item, which is a child of an ul-list, which is again a child of another list item. Typical for a html menu structure with submenus.
The evt.target would be the link tag, but we are interested in the mousemove on the list item.
Even worse: The link tag could contain span or img tags or other nested stuff. Then evt.target would be this span or img.
What seems to work here is to catch the event on a parent / root item, and then check the parents of evt.target.
Like this (with jQuery),
var $menu = $('div#menu');
$('body').mousemove(function(evt){
var element = evt.target;
// find the deepest list item that was affected by this mouseover event.
var list_item;
var in_menu = false;
while (element) {
if (element == $menu[0]) {
in_menu = true;
break;
}
else if (!list_item && element.tagName == 'LI') {
// we found a list item, but we are not sure if we are inside the menu tree.
list_item = element;
}
}
// do something with the result.
if (!in_menu) {
.. // close all submenus
}
if (list_item) {
.. // open the submenu for this list item.
}
else {
// mouse in menu, but not hovering an item.
// leave the submenus open. (?)
}
});
Maybe some of this could be abbreviated with jQuery like $(evt.target).parents().is($menu), but I did not get this to work. Also, I would guess that this explicit loop with element.tagName is faster.
What is the difference between event.target and this?
Let's say I have
$("test").click(function(e) {
$thisEventOb = e.target;
$this = this;
alert($thisEventObj);
alert($this);
});
I know the alert will pop different value. Anyone could explain the difference? Thanks a million.
They will be the same if you clicked on the element that the event is rigged up to. However, if you click on a child and it bubbles, then this refers to the element this handler is bound to, and e.target still refers to the element where the event originated.
You can see the difference here: http://jsfiddle.net/qPwu3/1/
given this markup:
<style type="text/css">div { width: 200px; height: 100px; background: #AAAAAA; }</style>
<div>
<input type="text" />
</div>
If you had this:
$("div").click(function(e){
alert(e.target);
alert(this);
});
A click on the <input> would alert the input, then the div, because the input originated the event, the div handled it when it bubbled. However if you had this:
$("input").click(function(e){
alert(e.target);
alert(this);
});
It would always alert the input twice, because it is both the original element for the event and the one that handled it.
Events can be attached to any element. However, they also apply to any elements within said object.
this is the element that the event is bound to. e.target is the element that was actually clicked.
For example:
<div>
<p>
<strong><span>click me</span></strong>
</p>
</div>
<script>$("div").click(function(e) {
// If you click the text "click me":
// e.target will be the span
// this will be the div
}); </script>
Crispy answer
this gives you the reference of the DOM element where the event is actually attached.
event.target gives you the reference of the DOM element where the event occurs.
Long answer
jQuery(document).ready(function(){
jQuery(".outer").click(function(){
var obj = jQuery(event.target);
alert(obj.attr("class"));
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer">
Outer div starts here
<div class="inner">
Inner div starts here
</div>
</div>
When you run the above code snippet you will see that event.target is alerting the class name of the div that is actually clicked.
However this will give the reference of the DOM object on which the click event is bind. Check the below code snippet to see how this works, always alerting the class name of the div on which the click event is bind even if you click the inner div.
jQuery(document).ready(function(){
jQuery(".outer").click(function(){
var obj = jQuery(this);
alert(obj.attr("class"));
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer">
Outer div starts here
<div class="inner">
Inner div starts here
</div>
</div>