Stop event bubbling - onclick listener - javascript

Why clicking on parent div element does the job, but clicking on the child div text Click Me! returns undefined and the page doesn't load in iframe? I used event.stopPropagation to stop bubbling but it is still the same. What is the correct way to do that, when addEvenLisneter is preferred over inline onclick ?
var panel = document.getElementsByClassName('panel');
var iframe = document.getElementById('frame');
[].forEach.call(panel, function(each) {
each.addEventListener('click', linkClick);
});
function linkClick ( event ) {
event.stopPropagation();
event.preventDefault();
iframe.src = event.target.dataset.url;
}
.panel {
padding: 20px;
margin: 10px;
border: 1px solid yellow;
}
.panel div {
display: inline;
}
<div class="panel" data-url="https://www.w3schools.com/jsref/event_preventdefault.asp">
<div>Click Me!</div>
</div>
<iframe id="frame"></iframe>
When I console.log(event) bubble property is still set to true.

If you prevented the click event from bubbling from the inner div, then it would never reach the one to which you bound the event handler and your function would not fire at all.
The problem here is that event.target matches the element that was actually clicked on. You want event.currentTarget if you want to get the element that the event handler was bound to.
var panel = document.getElementsByClassName('panel');
var iframe = document.getElementById('frame');
[].forEach.call(panel, function(each) {
each.addEventListener('click', linkClick);
});
function linkClick ( event ) {
event.stopPropagation();
event.preventDefault();
iframe.src = event.currentTarget.dataset.url;
}
.panel {
padding: 20px;
margin: 10px;
border: 1px solid yellow;
}
.panel div {
display: inline;
}
<div class="panel" data-url="https://www.w3schools.com/jsref/event_preventdefault.asp">
<div>Click Me!</div>
</div>
<iframe id="frame"></iframe>

Well, I am not actually solving the problem as a such, more like a workaround.
Maybe you could try using a button instead, as this is slightly more valid HTML.
I modified the CSS a bit, so it matches your example.
<!DOCTYPE html>
<html>
<style>
.panel {
padding: 20px;
margin: 10px;
border: 1px solid yellow;
display:block;
width:100%;
text-align:left
}
</style>
<body>
<button class="panel" data-url="https://www.w3schools.com/jsref/event_preventdefault.asp" type="button">Click Me!</button>
<iframe id="frame"></iframe>
</body>
<script>
var panel = document.getElementsByClassName('panel');
var iframe = document.getElementById('frame');
[].forEach.call(panel, function(each) {
each.addEventListener('click', linkClick);
});
function linkClick ( event ) {
event.stopPropagation();
event.preventDefault();
iframe.src = event.target.dataset.url;
}
</script>
</html>

Related

JavaScript click() function and propagation

I'm curious why this simple code does not run indefinitely. After reading Eloquent JavaScript, it says that the immediate nodes will execute first, and then the outer nodes will, in turn, execute their click event listeners. However, the code below only gives these 7 lines as output. It seems as if lines 5 and 6 would fire the click listener on the container again. Perhaps I am not understanding something about propagation. I've tried debugging using Visual Studio and it does not enter the function, but I cannot find a way to determine why this is using the debugger. Thanks for the help.
"body clicked"
"container clicked"
"item clicked"
"container clicked"
"body clicked"
"body clicked"
const container = document.getElementById("container");
const item = document.getElementById("item");
const body = document.getElementById("body");
item.addEventListener("click", function () {
console.log("item clicked");
});
container.addEventListener("click", function () {
console.log("container clicked");
item.click();
});
body.addEventListener("click", function () {
console.log("body clicked");
container.click();
});
body {
background: yellow;
}
div.container {
width: 200px;
height: 200px;
background: aqua;
border: 1px solid black;
}
div.item {
width: 100px;
height: 100px;
background: gray;
border: 1px solid red;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My website</title>
<link rel="stylesheet" href="site.css">
</head>
<body id="body">body
<div class="container" id="container">container
<div class="item" id="item">item</div>
</div>
<script src="site.js"></script>
</body>
</html>
As #pilchard pointed out in the comments, calling the click() method of an element will only invoke that element's click event handling function (no different than directly calling the function by its name), but it won't trigger an actual click event.
Below, I've commented out those calls and you are left with being able to clearly see that a mouse click does trigger the event and how that event will bubble up through the clicked element's ancestor elements.
const container = document.getElementById("container");
const item = document.getElementById("item");
const body = document.getElementById("body");
item.addEventListener("click", function () {
console.log("item clicked");
});
container.addEventListener("click", function () {
console.log("container clicked");
//item.click();
});
body.addEventListener("click", function () {
console.log("body clicked");
//container.click();
});
body {
background: yellow;
}
div.container {
width: 200px;
height: 200px;
background: aqua;
border: 1px solid black;
}
div.item {
width: 100px;
height: 100px;
background: gray;
border: 1px solid red;
}
<body id="body">body
<div class="container" id="container">container
<div class="item" id="item">item</div>
</div>
</body>

href link not working with innerHTML script with "onmouseover change text" and onmouseout

My goal is to have text change onmouseover from "hello" (without a link) to "Google" and provide an 'href' on the resulting "Google" text, and then revert to "hello" onmouseout without a link.
The code below works in changing the text from "hello" to "Google" but,
the link on "Google" does not work (even though I can right-click on "Google" and open the link on another tab)
the text does not change back to "hello" onmouseout.
Thanks for your help in advance!
Here is my code:
<style>
.container {
margin-top: 6vw;
margin-left: 40%;
margin-right: 40%;
}
</style>
<div class="container">
<h1>
<div class="hello" id="hello1" onmouseover="changeText()" onmouseout="changeText(this,'Hello.')">Hello.</div>
</h1>
</div>
<script>
function changeText() {
if (document.getElementById("hello1")) {
a = document.getElementById("hello1")
a.innerHTML = 'Google'
}
}
</script>
try this way onmouseout="this.innerHTML='Hello.';"
function changeText() {
if (document.getElementById("hello1")) {
a = document.getElementById("hello1")
a.innerHTML = 'Google'
}
}
.container {
margin-top: 6vw;
margin-left: 40%;
margin-right: 40%;
}
<div class="container">
<h1>
<div class="hello" id="hello1" onmouseover="changeText()" onmouseout="this.innerHTML='Hello.';">Hello.</div>
</h1>
</div>
By changing a class of a parent tag, any and all child tags can be affected via CSS. Having the HTML ready when the page loads and then hiding it is better than constantly creating and destroying HTML for trivial effects.
The events "mouseenter" and "mouselrave" are handled by a property event handler and an event listener. Either one is sufficient, but avoid using attribute event handlers:
<div onmouselame="lameAttributeEventHandler()">...</div>
Details are commented in the example below
// Reference the <header>
const hdr = document.querySelector('.title');
/* This is a property event handler
// Whenever the mouse enters within the borders of
// the <header>:
// '.google' class is added
// '.hello' class is removed
*/
hdr.onmouseenter = function(event) {
this.classList.add('google');
this.classList.remove('hello');
};
/* This is an even listener
// Whenever the mouse exits the <header> the
// opposite behavior of the previous handler
// happens
*/
hdr.addEventListener("mouseleave", function(event) {
this.classList.add('hello');
this.classList.remove('google');
});
.title {
height: 50px;
margin-top: 3vh;
border: 3px solid black;
text-align: center;
}
h1 {
margin: auto 0;
}
.hello span {
display: inline-block;
}
.hello a {
display: none;
}
.google a {
display: inline-block;
}
.google span {
display: none;
}
<header class="title hello">
<h1>
<span>Hello</span>
Google
</h1>
</header>
You can try this, May it help u to solve the problem
<!DOCTYPE html>
<head>
<title>change text on mouse over and change back on mouse out
</title>
<style>
#box {
float: left;
width: 150px;
height: 150px;
margin-left: 20px;
margin-top: 20px;
padding: 15px;
border: 5px solid black;
}
</style>
</head>
<html>
<body>
<div id="box" onmouseover="changeText('Yes, this is Onmouseover Text')" onmouseout="changeback('any thing')" >
<div id="text-display" >
any thing
</div>
</div>
<script type="text/javascript">
function changeText(text)
{
var display = document.getElementById('text-display');
display.innerHTML = "";
display.innerHTML = text;
}
function changeback(text)
{
var display = document.getElementById('text-display');
display.innerHTML = "";
display.innerHTML = text;
}
</script>
</body>
</html>

:hover select target only, not parents with same class

How do I make this only fire :hover for the target element (ignoring the parents)?
Assume this is recursive design of object inside object, therefore with the same classes and an autogenerated id.
.group:hover {
background-color: red;
}
.group {
border: 1px solid black;
width: 100px;
padding: 20px;
}
<div id="g1" class="group">aaaa
<div id="g2" class="group">bbbb
<div id="g3" class="group">cccc
</div>
</div>
</div>
Since you tagged the question with javascript you can achieve this using it. The key is to use .stopProgagation() which will stop events from "falling through" down to your other elements.
See example below:
document.querySelectorAll(".group").forEach(elem => {
elem.addEventListener('mouseover', function(e) {
e.stopPropagation();
this.classList.add('group-hover');
});
elem.addEventListener('mouseout', function(e) {
this.classList.remove('group-hover');
});
});
.group-hover {
background-color: red;
}
.group {
border: 1px solid black;
width: 100px;
padding: 20px;
}
<div id="g1" class="group">aaaa
<div id="g2" class="group">bbbb
<div id="g3" class="group">cccc
</div>
</div>
</div>
Alternatively, you could intead use e.target to get the target of the event if you wish not to use stopPropagation():
document.querySelectorAll(".group").forEach(elem => {
elem.addEventListener('mouseover', e => e.target.classList.add('group-hover'));
elem.addEventListener('mouseout', e => e.target.classList.remove('group-hover'));
});
.group-hover {
background-color: red;
}
.group {
border: 1px solid black;
width: 100px;
padding: 20px;
}
<div id="g1" class="group">aaaa
<div id="g2" class="group">bbbb
<div id="g3" class="group">cccc
</div>
</div>
</div>
Only with JS, and using events delegate for simpler way
const All_g = document.querySelector('#g1');
All_g.onmouseover = function(event) {
let target = event.target;
target.style.background = 'red';
};
All_g.onmouseout = function(event) {
let target = event.target;
target.style.background = '';
};
.group {
border: 1px solid black;
width: 100px;
padding: 20px;
}
<div id="g1" class="group">aaaa
<div id="g2" class="group">bbbb
<div id="g3" class="group">cccc
</div>
</div>
</div>
some explanations :=> https://javascript.info/mousemove-mouseover-mouseout-mouseenter-mouseleave
You can do it in the following way:
var elements = document.getElementsByClassName('group');
var lastElement = null;
elements.each(element =>{
lastElement = element;
});
lastElement.on('hover', function(){
//do anything you wish with element
})
Approach 1
Register hover event to toggle class and use event.stopPropagation();
https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation
The bubbles read-only property of the Event interface indicates
whether the event bubbles up through the DOM or not.
https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles
Approach 2
Mouseenter event - By design it does not bubble - so don't have to perform event.stopPropagation()
Though similar to mouseover, it differs in that it doesn't bubble and
that it isn't sent to any descendants when the pointer is moved from
one of its descendants' physical space to its own physical space.
https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event

Removing certain elements from a selector binded to the body

I have a click event binded to the body element but I don't want it to fire for when the user clicks on certain elements, that being when the element has an attribute of data-dropdown-target, however what I have tried isn't working, it always fires.
CodePen: http://codepen.io/anon/pen/ORQkrb
HTML:
<body>
<div class="foo">foo</div>
<div class="bar" data-dropdown-target="something">bar</div>
<div class="moo">moo</div>
</body>
CSS:
.foo, .bar, .moo {
padding: 10px;
margin: 5px;
}
.foo {
background-color: gray;
}
.bar {
background-color: teal;
}
.moo {
background-color: green;
}
JS:
$('body').not('[data-dropdown-target]').on('click', function(e) {
console.log('Hi!');
});
I assume this is because it is trying to remove body elements that have this attribute, rather than it's children - correct?
How do I go about stopping it from firing on children elements that have this attribute - do I have to loop through everything, as I would like to avoid that because of performance reasons, especially since it's on the body.
Actually your code try to bind event click on every <body> without data-dropdown-target attribute.
This could solve your problem :
$('body').on('click', function(e) {
if($(e.target).data('dropdown-target') || $(e.target).parents('[data-dropdown-target]').length !== 0) return false;
console.log('Hi!');
});
.foo, .bar, .moo {
padding: 10px;
margin: 5px;
}
.foo {
background-color: gray;
}
.bar {
background-color: teal;
}
.moo {
background-color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="foo">foo</div>
<div class="bar" data-dropdown-target="something">bar</div>
<div data-dropdown-target="something">
<div class="moo">moo</div>
</div>
</body>
The not selector just remove the body element if it has [data-dropdown-target] attribute.
Remove elements from the set of matched elements.
$('body').on('click', function(e) {
console.log('Hi!');
});
$('[data-dropdown-target]').on('click',function(e){
return false;
});

How to stop a parent's click event when the child's mousedown event is fired

As described in the title, I want to stop a parent's click event when the child's mousedown event is fired.
My HTML code looks like this
<div class='parent'>
<div class='child'></div>
</div>
my jquery code looks like this
$('.child').mousedown(function(){
//what can I write here to prevent parent's click event from fireing?
//I've tried event.stopPropagation() as well as
//event.stopImmediatePropagation() already
});
$('.parent').on('click',function(){...})
Mouse events are triggered like this way
MouseDown
Click
MouseUp
event.stopPropagation() in mousedown handler only affects on mousedown event. You can try this workaround:
var mdFaired = false;
$('.parent').click(function(e) {
if(!mdFaired) {
var counter = $(this).children('.counter');
var count = parseInt(counter.text());
counter.text(++count);
}
else {
mdFaired = false;
}
});
$('.child').mousedown(function(e) {
e.stopPropagation();
mdFaired = true;
var counter = $(this).children('.counter');
var count = parseInt(counter.text());
counter.text(++count);
});
div {
border: 1px solid black;
padding: 5px;
}
.parent {
width: 300px;
height: 300px;
}
.child {
width: 50%;
height: 50%;
margin: 50px auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='parent'>
<span class="counter">0</span>
<div class='child'>
<span class="counter">0</span>
</div>
</div>
e.stopPropagation();
e.preventDefault();
e is event passed from function call.
Should do the trick.

Categories