Preventing event bubbling - javascript

function clickHide(){
if (MouseEvent.button === 0) hide();
}
function hide(){
using.style.display = "none";
hidden.style.display = "none";
}
I have a problem with event bubbling. The thing is that I have this function hide(), which should be called only when clicking the red div. The problem is that it also gets called when I click the yellow div, which is placed inside the red div. Is there a nice way to prevent this from happening? Heard something about event.stopPropagation(), but I don't know how I would use it in this case.

You should use Event.stopPropagation(). Simply just call the stopPropagation() method on the event you are listening to.
Also see here for more info.
Edit: You can try it like this, it will only fire the event when you click on the parent element. The if statement checks if the target of the event is the parent element then calls hide() if not then it returns nothing.
const parentElement = document.querySelector(".parentElement");
const childElement = document.querySelector(".childElement");
parentElement.addEventListener("click", clickHide);
function clickHide(event) {
if(event.target !== parentElement){
return;
}
hide();
}
function hide() {
parentElement.classList.add('hidden');
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title></title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="" />
<style>
.parentElement {
border: 20px solid red;
}
.childElement {
background: yellow;
padding: 20px;
}
.hidden {
display: none;
}
</style>
<script src="test.js" async defer></script>
</head>
<body>
<div class="parentElement">
<div class="childElement"></div>
</div>
</body>
</html>

Related

can't select element with dom, after creating that element with other function - appendChild

How i'm supposed to select my element that have been created by function. First function is working well, but while i'm trying to select the element that been created in that function, it doesn't work
let d = document.querySelector(".lop");
let body = document.querySelector(".body");
d.addEventListener("click", function () {
let c = document.createElement("p");
c.appendChild(document.createTextNode("lopas"));
body.appendChild(c);
});
document.querySelector("p").addEventListener("click", function () {
console.log("Hi");
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="lop">s</div>
<div class="body"></div>
<script src="script.js"></script>
</body>
</html>
That's because you are attempting to attach a click event to your paragraph tag before its ever been added to the DOM.
You will need to move this new event listener inside of your onclick and after you append it to your .body div.
Example:
let d = document.querySelector(".lop");
let body = document.querySelector(".body");
d.addEventListener("click", function () {
let c = document.createElement("p");
c.appendChild(document.createTextNode("lopas"));
body.appendChild(c);
document.querySelector("p").addEventListener("click", function () {
console.log("Hi");
});
});
As requested, here is how you could just split some of this out to be its own methods for clarity. Feel free to use your own style as its just an example:
const onClickLop = (e) => {
const el = document.createElement("p");
const bodyDiv = document.querySelector(".body");
el.appendChild(document.createTextNode("lopas"));
bodyDiv.appendChild(el);
el.addEventListener("click", onClickLopas);
};
const onClickLopas = (e) => {
console.log("Hi");
});
document.querySelector(".lop").addEventListener("click", onClickLop);
Problem:
You create the p element at the moment when you click on .lop
You try to add the event listener at the page load. At this point there is no p tag at all.
Solution:
Add the event listener after you created the p tag.
You could also use the reference c instead of querySelector.
let d = document.querySelector(".lop");
let body = document.querySelector(".body");
d.addEventListener("click", function() {
let c = document.createElement("p");
c.appendChild(document.createTextNode("lopas"));
body.appendChild(c);
c.addEventListener("click", function() {
console.log("Hi");
});
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div class="lop">s</div>
<div class="body"></div>
</body>
</html>
The code to add the event listener for the p element is executing before your code that creates it. Move the event handler into the first function so that it isn't triggered until the element is created and added to the document. However, that will mean that each time you click the first element, a new p will be created with its own handler (separate question/answer).
Also, by doing that you can consolidate some code.
let d = document.querySelector(".lop");
let body = document.querySelector(".body");
d.addEventListener("click", function () {
let c = document.createElement("p");
c.appendChild(document.createTextNode("lopas"));
c.addEventListener("click", function () {
console.log("Hi");
});
body.appendChild(c);
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="lop">s</div>
<div class="body"></div>
<script src="script.js"></script>
</body>
</html>

Replace jQuery on() with native javascript [duplicate]

Made this example: https://jsfiddle.net/d8ak0edq/2/
document.getElementById('outer').oncontextmenu = function() { return false; };
outer = document.getElementById('outer');
outer.addEventListener('mousedown', foo);
function foo(evt) {
if (evt.which === 1) {
evt.target.style.backgroundColor = 'red';
} else if (evt.which === 3) {
evt.target.style.backgroundColor = 'blue';
}
}
/*
$outer = $('#outer');
$outer.on('mousedown', 'div', foo);
function foo(evt) {
if (evt.which === 1) {
$(this).css('background-color', 'red');
} else if (evt.which === 3) {
$(this).css('background-color', 'blue');
}
} */
#outer {
border: 2px solid black;
width: 300px;
height: 300px;
}
#inner {
position: relative;
left: 25%;
top: 25%;
border: 2px solid black;
width: 150px;
height: 150px;
}
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
<link rel="manifest" href="site.webmanifest">
<link rel="apple-touch-icon" href="icon.png">
<!-- Place favicon.ico in the root directory -->
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div id="outer">
<div id="inner">
</div>
</div>
<script src="js/vendor/modernizr-3.5.0.min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-3.2.1.min.js"><\/script>')</script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
</body>
</html>
As you can see the jQuery has a nice way to do it, the 2nd parameter sets the 'target' and 'this' and if you click on the outer div nothing will happen even the event handler is on the outer div.
How do I make this without jQuery?
So by making the event handler on inner would obviously "fix" the problem, but I want that the event stays on the outer div but targets only inner, how to make this work (and not by adding && !(evt.target===outer))?
The basic technique for delegation is: set a selectorable attribute on the inner, then attach event handler to the outer, then check for whether the event came from inner:
document.getElementById('outer').addEventListener('mousedown' function(outer_evt) {
if (outer_evt.target.id == 'inner') {
// I mousedowned on inner
}
});
If you have other events attached to outer (this includes events attached to any ancestor of outer), and don't want them fired when you fire the inner event, use outer_evt.stopImmediatePropagation() and/or outer_evt.stopPropagation() (respectively).
If you want to refer to the element that the event bubbled up to, that's .currentTarget. (that is, above, outer_evt.currentTarget === document.getElementById('outer'))
See also EventTarget.addEventListener()

Click on element and stopPropagation() for another one

I have two events: A button event and a container event. I want to apply stopPropagation() for container function, when I click on button. How to do this in vanilla js?
Now when I click on button #btn two functions will called. My goal is, when I click on button #btn, the code for #btn should only run. Click on div with id #container shall do it the same.
const btn = document.getElementById('btn');
btn.addEventListener('click', ()=> {
console.log('click on btn')
})
const container = document.getElementById('container');
container.addEventListener('click', ()=> {
console.log('click on container')
})
#container {
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">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<title>Welcome</title>
</head>
<body>
<div id="container">
<button id="btn">click</button>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
You can use Element.closest() to detect click outside or inside specific element
check this example
const btn = document.getElementById('btn');
btn.addEventListener('click', ()=> {
console.log('click on btn')
})
const container = document.getElementById('container');
container.addEventListener('click', (e)=> {
if(!e.target.closest('#btn')){
console.log('click on container')
}
})
#container {
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">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<title>Welcome</title>
</head>
<body>
<div id="container">
<button id="btn">click</button>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Simply add event.stopPropagation in your btn click handler
btn.addEventListener('click', ()=> {
console.log('click on btn')
event.stopPropagation()
})

How to toggle event listener on javascript matchmedia

I need to know how can i toggle an event listener that works when the screen is less than 700px and when the screen is more than 700px the event listener gets removed
function myFunction(x) {
if (x.matches) { // If media query matches
document.getElementById("resources").addEventListener("click", function() {
alert("media worked")
})
} else {
document.getElementById("resources").removeEventListener("click", function() {
alert("media worked")
})
}
}
var x = window.matchMedia("(max-width: 979px)")
myFunction(x) // Call listener function at run time
x.addListener(myFunction) // Attach listener function on state changes
h1 {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!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>Document</title>
</head>
<body>
<h1 id="title">abrir y cerrar</h1>
<button id="resources">click to toggle</button>
</body>
</html>
You need to use named functions if you want to use removeEventListener, since the function that you remove has to be the same one you added.
function clickHandler() {
alert("media worked");
}
function myFunction(x) {
if (x.matches) { // If media query matches
document.getElementById("resources").addEventListener("click", clickHandler)
} else {
document.getElementById("resources").removeEventListener("click", clickHandler)
}
}
var x = window.matchMedia("(max-width: 979px)")
myFunction(x) // Call listener function at run time
x.addListener(myFunction) // Attach listener function on state changes
h1 {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!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>Document</title>
</head>
<body>
<h1 id="title">abrir y cerrar</h1>
<button id="resources">click to toggle</button>
</body>
</html>
Barmar has given the correct answer to the problem of your code. But I still wanted to add this to show that it's a possibility.
You could use CSS to, for example, disable a click on an element uing the pointer-events property.
var links = document.querySelectorAll('a');
function onClick(event) {
console.log(event);
event.preventDefault();
}
links.forEach(function(link) {
link.addEventListener('click', onClick);
});
.events-none {
pointer-events: none;
}
Without pointer-events: none
With pointer-events: none

Event on a click everywhere on the page outside of the specific div

I'd like to hide a div when user click anywhere on the page outside of that div. How can I do that using raw javascript or jQuery?
Attach a click event to the document to hide the div:
$(document).click(function(e) {
$('#somediv').hide();
});
Attach a click event to the div to stop clicks on it from propagating to the document:
$('#somediv').click(function(e) {
e.stopPropagation();
});
First idea, in raw javascript (from this post):
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<style type="text/css">
<!--
#mydiv{
background-color: #999999;
height: 100px;
width: 100px;
}
-->
</style>
<script type="text/javascript">
document.onclick=check;
function check(e)
{
var target = (e && e.target) || (event && event.srcElement);
var obj = document.getElementById('mydiv');
if(target!=obj){obj.style.display='none'}
}
</script>
</head>
<body>
<div id="mydiv">my div</div>
</body>
</html>
Tested with IE6 and FireFox3.1, it does work as advertised.
It sure sounds like you want a modal dialog. This jQuery plugin http://code.google.com/p/simplemodal/ looks like it has potential.
If the div has the focus, you could attach to the onblur event.

Categories

Resources