Checking Clicked Element in Bubbling - javascript

I was tinkering with event bubbling and I created the typical 3 divs each other exercise for myself and I was able to get the code working and event to stop "bubbling" where I wanted.
My code was clunky so I tried to make it a little more simplified. By having all my <divs> point to a single function, that had a statement that said if this <div> was clicked, run the function and then stop propagation, but I believe the way I am "checking" the click is wrong.
Link: https://jsfiddle.net/theodore_steiner/t5r5kov0/3/
var d1 = document.getElementById("d1");
var d2 = document.getElementById("d2");
var d3 = document.getElementById("d3");
function showme(event) {
if (d3.onclick == true) {
alert("hello")
event.stopPropagation();
} else {
alert("hello");
}
};
d1.addEventListener("click", showme);
d2.addEventListener("click", showme);
d3.addEventListener("click", showme);
#d1 {
height: 300px;
width: 300px;
border: 1px solid black;
}
#d2 {
height: 200px;
width: 200px;
border: 1px solid black;
}
#d3 {
height: 100px;
width: 100px;
border: 1px solid black;
}
<div id="d1">
<div id="d2">
<div id="d3">
</div>
</div>
</div>

Use event.target.id as event.target will return element on which event is invoked and id is a property of the DOMElement
Event.target, A reference to the object that dispatched the event.
var d1 = document.getElementById("d1");
var d2 = document.getElementById("d2");
var d3 = document.getElementById("d3");
function showme(event) {
if (event.target.id == 'd3') {
alert("hello")
event.stopPropagation();
} else {
alert("hello");
}
};
d1.addEventListener("click", showme);
d2.addEventListener("click", showme);
d3.addEventListener("click", showme);
#d1 {
height: 300px;
width: 300px;
border: 1px solid black;
}
#d2 {
height: 200px;
width: 200px;
border: 1px solid black;
}
#d3 {
height: 100px;
width: 100px;
border: 1px solid black;
}
<div id="d1">
<div id="d2">
<div id="d3">
</div>
</div>
</div>

Related

How to get html element without its children in JavaScript?

I would like to get an html element without its children, to set an event addEventListener("click) on it, so that the function will only be executed when it is clicked, not on its children. I can only use Javascript. Is this possible?
const divs = document.querySelectorAll("div");
const body = document.querySelector("body");
const myFunction = function() {
this.classList.add("clicked")
}
divs.forEach(function(element) {
element.addEventListener("click", myFunction)
});
.grandparent {
padding: 20px;
border: 5px solid black;
}
.parent {
padding: 20px;
border: 5px solid blue;
}
.child {
padding: 20px;
height: 100px;
width: 100px;
border: 5px solid yellow;
}
.clicked {
background-color: red;
}
<div data-time="3000" class="grandparent">
<div data-time="2000" class="parent">
<div data-time="1000" class="child"></div>
</div>
</div>
this function adds a class to each div, now I would like clicking outside of div to remove that class, however the body variable contains including its children.
If you want to ignore all and any child clicks, then check if the currentTarget is different than the target of the event.
target is the element the event originated from (the deepest child that received the event)
currentTarget is the element on which the event handler is attached
const divs = document.querySelectorAll("div");
const body = document.querySelector("body");
const myFunction = function(event) {
if (event.target === event.currentTarget) {
this.classList.add("clicked")
}
}
divs.forEach(function(element) {
element.addEventListener("click", myFunction)
});
.grandparent {
padding: 20px;
border: 5px solid black;
}
.parent {
padding: 20px;
border: 5px solid blue;
}
.child {
padding: 20px;
height: 100px;
width: 100px;
border: 5px solid yellow;
}
.clicked {
background-color: red;
}
<div data-time="3000" class="grandparent">
<div data-time="2000" class="parent">
<div data-time="1000" class="child"></div>
</div>
</div>

dispatch Event on the most inner element doesn't cause bubbling to upper elements?

innerDiv = document.getElementById("inner");
innerDiv.onclick = function(e) {
console.log("PURPLE COLOR DIV");
}
outterDiv = document.getElementsByClassName("outter")[0];
outterDiv.onclick = function(e) {
console.log("ORANGE COLOR DIV");
};
containerDiv = document.getElementsByClassName("container")[0];
containerDiv.onclick = function() {
console.log("RED COLOR DIV");
}
setTimeout(() => document.getElementById("inner").dispatchEvent(new Event('click'), {
bubbles: true
}), 2000)
.container {
width: 260px;
height: 170px;
background-color: maroon;
padding-top: 40px;
}
.outter {
width: 200px;
height: 80px;
background: orange;
margin: 0 auto;
padding-top: 35px;
}
#inner {
background: purple;
width: 100px;
height: 50px;
margin: auto;
}
<div class="container">
<div class="outter">
<div id="inner"></div>
</div>
</div>
PROBLEM
After setTimeout causes the dispatchEvent to fire, the Orange and Red color callbacks do not execute. Why is that so if bubbles property is set to true?
In case of clicking on the UI purple div, the bubbling does its job as supposed.
Fiddle example: http://jsfiddle.net/0tko5qwe/
You need to add the options to the Event() constructor itself:
new Event('click', { bubbles: true})
innerDiv = document.getElementById("inner");
innerDiv.onclick = function(e) {
console.log("PURPLE COLOR DIV");
}
outterDiv = document.getElementsByClassName("outter")[0];
outterDiv.onclick = function(e) {
console.log("ORANGE COLOR DIV");
};
containerDiv = document.getElementsByClassName("container")[0];
containerDiv.onclick = function() {
console.log("RED COLOR DIV");
}
setTimeout(() => document.getElementById("inner").dispatchEvent(new Event('click', {
bubbles: true
})), 500)
.container {
width: 260px;
height: 170px;
background-color: maroon;
padding-top: 40px;
}
.outter {
width: 200px;
height: 80px;
background: orange;
margin: 0 auto;
padding-top: 35px;
}
#inner {
background: purple;
width: 100px;
height: 50px;
margin: auto;
}
<div class="container">
<div class="outter">
<div id="inner"></div>
</div>
</div>

Trigger event if click outside selector

I'm trying to click somewhere other than the button, hide the element, but I do not get it, I have no idea how to do it.
$(function(){
$(document).on('click','#foo',function(){
let div = $('#bar');
if( div.css('display') === 'none' ){
div.show();
}
else{
div.hide();
}
});
})
#foo{
min-width: 35%;
}
#bar{
max-width: 35%;
min-height: 100px;
background-color: aliceblue;
border: 1px solid blue;
border-radius: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="foo">Toggle</button><br><br>
<div id="bar"></div>
I got an idea but it doesn´t work.
$(document).on('click','html',function(e){
if(e.eventTarget !== 'foo'){
$('#bar').hide();
}
});
I got 2 issues, if the selector is html, the page will not answer, and the code in, is just to show what I'm trying to get.
No need for jQuery, you can simply test to see if .closest('#bar') exists:
const bar = document.querySelector('#bar');
let hidden = false;
document.body.addEventListener('click', (e) => {
if (e.target.closest('#foo')) {
console.log('clicked inside, returning');
return;
}
console.log('clicked outside');
bar.style.display = hidden ? 'block' : 'none';
hidden = !hidden;
});
body {
height: 200px;
}
#foo{
min-width: 35%;
}
#bar{
max-width: 35%;
min-height: 100px;
background-color: aliceblue;
border: 1px solid blue;
border-radius: 5px;
}
<button id="foo">Toggle</button>
<div id="bar"></div>
You may have to consume events bubbling up to parent node.
$(function() {
$('#foo').on('click', function(e) {
e.stopPropagation();
e.preventDefault();
$('#bar').toggle();
console.log('toggle ... bar');
});
$(document).on('click', function(e) {
e.stopPropagation();
e.preventDefault();
$('#bar').hide();
console.log('hide ... bar');
});
})
#foo {
min-width: 35%;
}
#bar {
max-width: 35%;
min-height: 100px;
background-color: aliceblue;
border: 1px solid blue;
border-radius: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="foo">Toggle</button><br><br>
<div id="bar"></div>
I think it is better way to use focus and blur functions for that
Here is how I did
<button id="test">
Hello
</button>
<div class="blue-container" tabindex="1">
</div>
The tabindex global attribute indicates if its element can be focused
$('#test').focus(function () {
$('.blue-container').show().focus();
});
$('.blue-container').blur(function () {
$('.blue-container').hide();
});
This is the simplest approach.
$(document).on("click",function() {
if(event.target.id == 'foo') {
//if #foo is clicked, do nothing
}
else {
//if the button is not clicked
$('#bar').hide();
}
});
#foo{
min-width: 35%;
}
#bar{
max-width: 35%;
min-height: 100px;
background-color: aliceblue;
border: 1px solid blue;
border-radius: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="foo">Toggle</button><br><br>
<div id="bar"></div>

JQuery Traversing through DOM on click

I am having problem of traversing through each HTML element one by one.There are two buttons #up and #down.On click of #up the id #myID should move to the next element upwards and vice versa for #down.The problem is I am able to move through the siblings but not through the child elements.
For example if I click on #down the id #myID should have moved to p tag which is the child of that div on next click to span which is child of p then on next click to div.But in my code it is directly jumping to div ignoring the children.
JSFIDDLE
Here is the code:
$("#up").click(function() {
$("#startHere").find("#myID").next().attr('id', 'myID');
$('#startHere').find("#myID").removeAttr('id');
});
$("#down").click(function() {
$("#startHere").find("#myID").prev().attr('id', 'myID');
$('#startHere').find("#myID").next().removeAttr('id');
})
#myID {
border: 2px solid yellow;
}
#startHere {
height: 100%;
width: 50%;
}
div {
height: 100px;
width: 100px;
border: 2px solid;
margin: 10px;
}
p {
height: 50px;
width: 50px;
border: 2px solid blue;
margin: 10px;
}
h1 {
height: 40px;
width: 40px;
border: 2px solid red;
margin: 10px;
}
span {
display: block;
height: 25px;
width: 25px;
border: 2px solid green;
margin: 10px;
}
button {
height: 25px;
width: 100px;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="up">GO DOWN</button>
<button id="down">GO UP</button>
<div id="startHere">
<div id="myID">
<p><span></span></p>
</div>
<div><span></span></div>
<div>
<h1></h1>
</div>
<p></p>
<h1></h1>
<p><span></span></p>
</div>
I think you can just find all the elements first, jQuery returns them in DOM order, which is what you want. No need to search for the next/prev element on-the-fly.
var allElements = $("#startHere").find('*');
var currentIndex = allElements.index('#myID');
function move(delta) {
// Find the new index
var index = currentIndex + delta;
// Clamp to 0…lengh of list
// Here we could also make it wrap instead
index = Math.max(Math.min(index, allElements.length - 1), 0);
// Remove the ID from the old element
allElements.eq(currentIndex).removeAttr('id');
// Add the ID to the new element
allElements.eq(index).attr('id', 'myID');
// Update the index
currentIndex = index;
}
$("#up").click(function() {
move(1);
});
$("#down").click(function() {
move(-1);
})
var allElements = $("#startHere").find('*');
var currentIndex = allElements.index('#myID');
function move(delta) {
// Find the new index
var index = currentIndex + delta;
// Clamp to 0…lengh of list
// Here we could also make it wrap instead
index = Math.max(Math.min(index, allElements.length - 1), 0);
// Remove the ID from the old element
allElements.eq(currentIndex).removeAttr('id');
// Add the ID to the new element
allElements.eq(index).attr('id', 'myID');
// Update the index
currentIndex = index;
}
$("#up").click(function() {
move(-1);
});
$("#down").click(function() {
move(1);
})
#myID {
border: 2px solid yellow;
}
#startHere {
height: 100%;
width: 50%;
}
div {
height: 100px;
width: 100px;
border: 2px solid;
margin: 10px;
}
p {
height: 50px;
width: 50px;
border: 2px solid blue;
margin: 10px;
}
h1 {
height: 40px;
width: 40px;
border: 2px solid red;
margin: 10px;
}
span {
display: block;
height: 25px;
width: 25px;
border: 2px solid green;
margin: 10px;
}
button {
height: 25px;
width: 100px;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="down">GO DOWN</button>
<button id="up">GO UP</button>
<div id="startHere">
<div id="myID">
<p><span></span></p>
</div>
<div><span></span></div>
<div>
<h1></h1>
</div>
<p></p>
<h1></h1>
<p><span></span></p>
</div>
If you do need the elements on-the-fly (because they might have changed), you can still use the same tactic (and simply build up the allElements list in the move function and get the index using allElements.index('#myID')) but it might be more performant to update the list only when you know it changed (after an Ajax request, after modification on event handlers, etc.).
Edit:
The code for searching the next/prev element on-the-fly is a bit more work because it has to recurse when traversing up but makes it possible to have a different set of rules for up vs. down movement.
var boundary = $("#startHere");
function findNext(node, anchor) {
if(!anchor && node.children(':first-child').length) {
return node.children(':first-child');
}
if(node.next().length) {
return node.next();
}
if(!boundary.find(node.parent()).length) {
// Out of boundary. Stick to the last node
return anchor||node;
}
return findNext(node.parent(), anchor||node);
}
function findPrev(node, anchor) {
if(!anchor && node.children(':last-child').length) {
return node.children(':last-child');
}
if(node.prev().length) {
return node.prev();
}
if(!boundary.find(node.parent()).length) {
// Out of boundary. Stick to the last node
return anchor||node;
}
return findPrev(node.parent(), anchor||node);
}
function move(finder) {
// Find the current item
var current = boundary.find('#myID');
// Find the next item
var next = finder(current);
// Remove the ID from the old element
current.removeAttr('id');
// Add the ID to the new element
next.attr('id', 'myID');
}
$("#up").click(function() {
move(findPrev);
});
$("#down").click(function() {
move(findNext);
})
var boundary = $("#startHere");
function findNext(node, anchor) {
if(!anchor && node.children(':first-child').length) {
return node.children(':first-child');
}
if(node.next().length) {
return node.next();
}
if(!boundary.find(node.parent()).length) {
// Out of boundary. Stick to the last node
return anchor||node;
}
return findNext(node.parent(), anchor||node);
}
function findPrev(node, anchor) {
if(!anchor && node.children(':last-child').length) {
return node.children(':last-child');
}
if(node.prev().length) {
return node.prev();
}
if(!boundary.find(node.parent()).length) {
// Out of boundary. Stick to the last node
return anchor||node;
}
return findPrev(node.parent(), anchor||node);
}
function move(finder) {
// Find the current item
var current = boundary.find('#myID');
// Find the next item
var next = finder(current);
// Remove the ID from the old element
current.removeAttr('id');
// Add the ID to the new element
next.attr('id', 'myID');
}
$("#up").click(function() {
move(findPrev);
});
$("#down").click(function() {
move(findNext);
})
#myID {
border: 2px solid yellow;
}
#startHere {
height: 100%;
width: 50%;
}
div {
height: 100px;
width: 100px;
border: 2px solid;
margin: 10px;
}
p {
height: 50px;
width: 50px;
border: 2px solid blue;
margin: 10px;
}
h1 {
height: 40px;
width: 40px;
border: 2px solid red;
margin: 10px;
}
span {
display: block;
height: 25px;
width: 25px;
border: 2px solid green;
margin: 10px;
}
button {
height: 25px;
width: 100px;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="down">GO DOWN</button>
<button id="up">GO UP</button>
<div id="startHere">
<div id="myID">
<p><span></span></p>
</div>
<div><span></span></div>
<div>
<h1></h1>
</div>
<p></p>
<h1></h1>
<p><span></span></p>
</div>
This is really bad UI. To select some nodes in some states, you first have to navigate “UP” and then “DOWN” again. But it seems to do what you ask for.

Temperamental JavaScript: function works only sometimes

I have a javascript function that is only working sometimes. I'm getting Uncaught TypeError: Cannot read property of undefined sometimes and sometimes the function works as expected.
To clearly explain and demonstrate the issue I recorded this short video: https://youtu.be/uSSes2_DPXU
Here is my function:
function openLightBox() {
var itemId = event.target.id;
var lightBox = document.getElementsByClassName(itemId);
console.log(lightBox);
lightBox[0].style.display = 'block' ;
}
SOLUTION
Final HTML:
<a onClick="openLightBox(<?php echo get_the_ID()?>)">
Final JS:
function openLightBox(id) {
var lightBox = document.getElementsByClassName(id);
console.log(lightBox);
lightBox[0].style.display = 'block';
}
The reason why it sometimes works, and I'm a bit guessing here, depends on where on the element you click. Since you have wrapped your a tag around the div that holds the id, does your a tag nicely wrap around the div dimension wise?
Actually it would make more sense to add the id to the a tag.
<a onClick="openLightBox(<?php echo get_the_ID()?>)">
Then your function will be someting like this:
function openLightBox(id) {
var lightBox = document.getElementById(id);
console.log(lightBox);
lightBox.style.display = 'block';
}
extra info: event bubbling and target -vs- currentTarget
You use event.target a bit wrong. So in snippet below you can see that target change depends where you click.
function openlightbox(e) {
console.log(e);
document.getElementById('target').innerHTML = e.target.outerHTML.replace(/</g, '<').replace(/>/g, '>');
}
.overlay {
background-color: #fbfbfb;
width: 100%;
height: 100%;
border: 1px solid black;
}
.title {
width: 200px;
height: 200px;
border: 1px solid red;
margin: auto;
}
h3 {
border: 1px solid green;
}
p {
border: 1px solid blue;
}
<article>
<a onclick="openlightbox(event)">
<div class='overlay' id="v1">
<div class="title">
<h3>Title</h3>
<p>category</p>
</div>
</div>
</a>
</article>
<pre id='target'></pre>
For solving you can just pass it directly to function. Or add event handler with addEventHandler function
var a = document.querySelectorAll('article a.open');
console.log(a);
for (var i = 0, len = a.length; i < len; i++) {
console.log(a[i],a[i].addEventListener);
a[i].addEventListener('click', function(e) {
document.getElementById('target').innerHTML = e.target.outerHTML.replace(/</g, '<').replace(/>/g, '>');
document.getElementById('target').innerHTML += '<br /> this.id:'+ this.id;
document.getElementById('target').innerHTML += '<br /> this:'+ this.outerHTML.replace(/</g, '<').replace(/>/g, '>');
},false);
}
.overlay {
background-color: #fbfbfb;
width: 100%;
height: 100%;
border: 1px solid black;
}
.title {
width: 200px;
height: 100px;
border: 1px solid red;
margin: auto;
}
h3 {
border: 1px solid green;
}
p {
border: 1px solid blue;
}
<article>
<a class="open" href="#" id="v1">
<div class='overlay'>
<div class="title">
<h3>Title</h3>
<p>category</p>
</div>
</div>
</a>
</article>
<pre id='target'></pre>

Categories