can a click event be triggered on absolutely stacked elements? - javascript

I have a click event thats firing. It's working great and does what I need it to do. Here's the problem
The nature of the widget i'm building stacks elements on top of each other through position: absolute When i click on one of these stacked elements, only one event is firing, but id like every element to fire that is under the mouse cursor of the click. Is there a way to do this?

Please check the demo or run the code snippet in full page and click through all the divs to see the result message.
DEMO:
http://plnkr.co/edit/KRWvLmRhGbO200pFkOxL?p=preview
What I am doing here is :
Hide the top element
and
get the next absolute element's co-ordinate with document.elementFromPoint and then repeat.
Stack Snippet:
jQuery(document).ready(function(){
$common = $("div.common").on('click.passThrough', function (e, ee) {
var $element = $(this).hide();
try {
if (!ee) $("#output").empty();
$("<div/>").append('You have clicked on: '+$element.text()).appendTo($("#output"));
ee = ee || {
pageX: e.pageX,
pageY: e.pageY
};
var next = document.elementFromPoint(ee.pageX, ee.pageY);
next = (next.nodeType == 3) ? next.parentNode : next //Opera
$(next).trigger('click.passThrough', ee);
} catch (err) {
console.log("click.passThrough failed: " + err.message);
} finally {
$element.show();
}
});
$common.css({'backgroundColor':'rgba(0,0,0,0.2)'});
});
#output {
margin-bottom: 30px;
}
.common {
position: absolute;
height: 300px;
width: 300px;
padding: 3px;
border: 1px #000 solid;
}
.elem5 {
top: 150px;
left: 150px;
}
.elem4 {
top: 180px;
left: 180px;
}
.elem3 {
top: 210px;
left: 210px;
}
.elem2 {
top: 240px;
left: 240px;
}
.elem1 {
top: 270px;
left: 270px;
}
<script data-require="jquery#3.0.0" data-semver="3.0.0" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"></script>
<div id="output"></div>
<div class="common elem1">Top Most Element</div>
<div class="common elem2">Element 2</div>
<div class="common elem3">Element 3</div>
<div class="common elem4">Element 4</div>
<div class="common elem5">Bottom Element</div>
Credit for source:
http://jsfiddle.net/E9zTs/2/

You can use customEvent property
Place all div in a parent div
add a click handler to the parent div
if there is a click in the parent box..determine whether the click is in any of the child boxes
If true. then send a click event to all child box
snippet
//This function changes the color of all child divs
function changeColor(e) {
this.style.background = "red";
}
//this function is attached to the parent div which will send that click event to all divs
function trigger(e) {
//create an event
event = new CustomEvent('click');
//if the event originates from a child div
if (e.target.className == 'box')
//loop through all child div
for (var i = 0; i < all_box.length; ++i) {
//dispatch a click event to each child div
all_box[i].dispatchEvent(event);
}
}
document.getElementById('parent').addEventListener('click', trigger)
var all_box = document.getElementsByClassName('box');
for (var i = 0; i < all_box.length; ++i) {
all_box[i].addEventListener('click', changeColor)
}
.box {
padding: 10px;
display: inline-block;
border: solid black;
}
#parent {
border: solid black;
padding: 10px;
}
;
<div id="parent">
<div class="box" id="primary">box1</div>
<div class="box">box2</div>
<div class="box">box3</div>
<div class="box">box3</div>
</div>

Related

switch z-index change on click (vanillaJS)

I'm trying to change the z-index on the clicked item on vanilla JS.
When a click event happens, the clicked item should come front. There are only two divs atm, but there will be more.
I'm storing the last clicked item, so that if new click event happens the last clicked item have less z-index. But it doesn't seem to work.(both of items are position relative)
Any help would be appreciated.
<div class="post" data-name="post" draggable="true">1</div>
<div class="post" data-name="post" draggable="true">2</div>
const a = new A()
memoSection.addEventListener('click', ({target})=>{
switch(target.dataset.name){
case "post":
let clickedItem ="";
a.bringFront(clickedItem)
break;
})
class A{
constructor(){
this.selected = null
}
bringFront(clickedItem){
this.selected = clickedItem; //store the previously clicked element
if(!clickedItem.style.zIndex == 10 || !clickedItem.style.zIndex){
this.selected.zIndex = 0
clickedItem.style.zIndex = 10
} else {
this.selected.zIndex = 10
clickedItem.style.zIndex = 0 }
}
}
I don't understand what your code was trying to do, but here is example how to get desired effect:
document.querySelectorAll('.post').forEach(item => {
//get all elements with class .post
item.addEventListener('click', event => {
// add Event Listener to each
document.querySelectorAll('.post').forEach(el => {
el.style.zIndex = "0";
})
// when clicked fetch all again and set all back to 0
item.style.zIndex = "10";
//set clicked only to 10
console.clear()
console.log(item.innerHTML);
})
})
.post:first-of-type {
position: absolute;
background-color: blue;
left: 100px;
top: 50px;
width: 150px;
height: 150px;
z-index: 0;
}
.post {
position: absolute;
background-color: red;
left: 25px;
top: 50px;
width: 150px;
height: 150px;
z-index: 0;
}
<div class="post" data-name="post" draggable="true">1</div>
<div class="post" data-name="post" draggable="true">2</div>

I have a problem with understanding the event system

I have this code:
<template>
<div class="chart"
v-bind:style="chartStyleObject"
v-on:mousedown.left="initHandleMousedown($event)"
v-on:mouseup.left="initHandleMouseup()"
v-on:mouseout="initHandleMouseup()">
<div class="chartContent">
</div>
<!-- <div class="chartContent"> end -->
</div>
<!-- <div class="chart"> end -->
</template>
<script>
import axios from 'axios';
export default{
created () {
},
data () {
return {
ticket: null,
chartStyleObject: {
width: '500px',
widthWrapper: '1600px',
heightWrapper: '500px',
height: '247px',
marginTop: '15px',
marginRight: '0px',
marginBottom: '0px',
marginLeft: '15px',
},
XCoord: null,
YCoord: null,
}
},
methods: {
initHandleMousedown(event) {
this.startMousedownXCoord = event.clientX;
this.startMousedownYCoord = event.clientY;
this.XCoord = event.clientX;
this.YCoord = event.clientY;
console.log('XCoord', this.XCoord);
console.log('YCoord', this.YCoord);
window.addEventListener('mousemove', this.initHandleMouseMove);
},
initHandleMouseMove(event) {
this.XCoord = event.clientX;
this.YCoord = event.clientY;
console.log('XCoord', this.XCoord);
console.log('YCoord', this.YCoord);
},
initHandleMouseup() {
window.removeEventListener('mousemove', this.initHandleMouseMove);
},
},
}
</script>
<style scoped>
.chart{
position: relative;
border-radius: 10px;
padding: 27px 10px 10px 10px;
background-color: #45788b;
box-sizing: border-box;
cursor: move;
}
.chart .chartContent{
position: relative;
top: 0;
left: 0;
height: 100%;
width: 100%;
margin: 0 0 0 0;
background-color: #2f2c8b;
}
</style>
HTML design consists of 2 blocks:
(parent and child)
The event is tied to the parent tag `<div class =" chart ">`
Also, the parent block has padding on all 4 sides:
If you click on the parent block and drive with the mouse (holding the button pressed) without affecting the padding space, the mousemove event will fire without problems.
But as soon as the mouse cursor touches the padding territory, the event ceases to function.
If you click on the padding, the event also works correctly - but it stops working if I move the mouse cursor over the block space outside the paddings (internal space)
Question:
Why is this happening - and is this behavior normal for js + nuxt.js?
I can't exactly follow your descriptions of the various regions of the page but I can have a go at explaining what I think you're seeing.
The key to this is that you have a mouseout listener that removes your mousemove listener. The mouseout event propagates, which means it will fire even if the mouseout occurred on a child element. Contrast with mouseleave which will only fire if the event occurs on the element itself.
The example below illustrates how a mouseout listener will fire even if the mouse cursor doesn't leave the root element. Just moving the cursor outside a child is sufficient.
document.getElementById('outer').addEventListener('mouseout', () => {
document.getElementById('out').innerHTML += 'mouseout\n'
})
div {
border: 1px solid;
display: inline-block;
padding: 20px;
}
<div id="outer">
<div></div>
</div>
<pre id="out"></pre>
I suspect that when you observe the event ceasing to function what is actually happening is that a mouseout event is occurring and that is removing the mousemove listener.
skirtle answer is correct. I am only providing this answer to illustrate how to do it using your own code. The only line I changed was this v-on:mouseleave="initHandleMouseup(). Notice I changed it to mouseout to mouseleave.
To summarize:
mouseleave is fired once per element regardless of its children
hover.
mouseout is fired every time the element abandoned (whether
moving the mouse away or hovering over its children).
new Vue({
el: "#app",
template: `
<div class="chart"
v-bind:style="chartStyleObject"
v-on:mousedown.left="initHandleMousedown($event)"
v-on:mouseup.left="initHandleMouseup()"
v-on:mouseleave="initHandleMouseup()">
<div class="chartContent">
</div>
<!-- <div class="chartContent"> end -->
</div>
<!-- <div class="chart"> end -->
`,
created: function() {},
data() {
return {
ticket: null,
chartStyleObject: {
width: '500px',
widthWrapper: '1600px',
heightWrapper: '500px',
height: '247px',
marginTop: '15px',
marginRight: '0px',
marginBottom: '0px',
marginLeft: '15px',
},
XCoord: null,
YCoord: null,
}
},
methods: {
initHandleMousedown: function(event) {
this.startMousedownXCoord = event.clientX;
this.startMousedownYCoord = event.clientY;
this.XCoord = event.clientX;
this.YCoord = event.clientY;
console.log('XCoord', this.XCoord);
console.log('YCoord', this.YCoord);
window.addEventListener('mousemove', this.initHandleMouseMove);
},
initHandleMouseMove: function(event) {
this.XCoord = event.clientX;
this.YCoord = event.clientY;
console.log('XCoord', this.XCoord);
console.log('YCoord', this.YCoord);
},
initHandleMouseup: function() {
window.removeEventListener('mousemove', this.initHandleMouseMove);
}
}
});
.chart {
position: relative;
border-radius: 10px;
padding: 27px 10px 10px 10px;
background-color: #45788b;
box-sizing: border-box;
cursor: move;
}
.chart .chartContent {
position: relative;
top: 0;
left: 0;
height: 100%;
width: 100%;
margin: 0 0 0 0;
background-color: #2f2c8b;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id='app'></div>
To see the different between mouseout/mouseover vs mouseenter/mouseleave events see this demo (taken from jQuery documentation) :
var i = 0;
$("div.overout")
.mouseout(function() {
$("p", this).first().text("mouse out");
$("p", this).last().text(++i);
})
.mouseover(function() {
$("p", this).first().text("mouse over");
});
var n = 0;
$("div.enterleave")
.on("mouseenter", function() {
$("p", this).first().text("mouse enter");
})
.on("mouseleave", function() {
$("p", this).first().text("mouse leave");
$("p", this).last().text(++n);
});
div.out {
width: 40%;
height: 120px;
margin: 0 15px;
background-color: #d6edfc;
float: left;
}
div.in {
width: 60%;
height: 60%;
background-color: #fc0;
margin: 10px auto;
}
p {
line-height: 1em;
margin: 0;
padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="out overout">
<p>move your mouse</p>
<div class="in overout">
<p>move your mouse</p>
<p>0</p>
</div>
<p>0</p>
</div>
<div class="out enterleave">
<p>move your mouse</p>
<div class="in enterleave">
<p>move your mouse</p>
<p>0</p>
</div>
<p>0</p>
</div>

How to prevent parent click event when the user clicks on his child element? AngularJS

I have a <div> with a ng-click but this <div> have a child element with also a ng-click directive.
The problem is that the click event on the child element trigger also the click event of the parent element.
How can I prevent the parent click event when I click on his child?
Here is a jsfiddle to illustrate my situation.
Thank you in advance for your help.
EDIT
Here is my code:
<body ng-app ng-controller="TestController">
<div id="parent" ng-click="parentClick()">
<div id="child" ng-click="childClick()"></div>
<p ng-bind="elem"></p>
</div>
<div><p style="text-align:center" ng-bind="childElem"></p></div>
</body>
<script>
function TestController($scope) {
$scope.parentClick = function() {
$scope.elem = 'Parent';
}
var i = 1;
$scope.childClick = function() {
$scope.elem = 'Child';
$scope.childElem = 'Child event triggered x' + i;
i++;
}
}
</script>
You should use the event.stopPropagation() method.
see: http://jsfiddle.net/qu86oxzc/3/
<div id="child" ng-click="childClick($event)"></div>
$scope.childClick = function($event) {
$event.stopPropagation();
...
}
Use event.stopPropagation to stop the event from bubbling up the DOM tree from child event handler.
Updated Demo
function TestController($scope) {
$scope.parentClick = function() {
$scope.elem = 'Parent';
}
var i = 1;
$scope.childClick = function(e) {
$scope.elem = 'Child';
$scope.childElem = 'Child event triggered x' + i;
i++;
e.stopPropagation(); // Stop event from bubbling up
}
}
body {
width: 100%;
height: 100%;
overflow: hidden;
}
#parent {
width: 100px;
height: 100px;
position: relative;
z-index: 1;
margin: 10px auto 0 auto;
background-color: #00acee;
border-radius: 2px;
cursor: pointer;
}
#parent:hover {
opacity: 0.5;
}
#child {
width: 20px;
height: 20px;
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
background-color: #FFF;
border-radius: 2px;
cursor: pointer;
}
#child:hover {
opacity: 0.5;
}
#parent p {
width: 100%;
position: absolute;
bottom: 0px;
text-align: center;
color: #FFF;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<body ng-app ng-controller="TestController">
<div id="parent" ng-click="parentClick()">
<div id="child" ng-click="childClick($event)"></div>
<!-- ^^^^^^^ -->
<p ng-bind="elem"></p>
</div>
<div>
<p style="text-align:center" ng-bind="childElem"></p>
</div>
</body>
also you can use this as it use do the same.
<div id="child" ng-click="childClick();$event.stopPropagation();"></div>
Even if Pieter Willaert's answer is much more beautiful i updated your fiddle with a simple boolean check:
http://jsfiddle.net/qu86oxzc/6/
function TestController($scope) {
$scope.boolean = false;
$scope.parentClick = function () {
if (!$scope.boolean) $scope.elem = 'Parent';
$scope.toggleBoolean();
}
var i = 1;
$scope.childClick = function () {
$scope.boolean = true;
$scope.elem = 'Child';
$scope.childElem = 'Child event triggered x' + i;
i++;
}
$scope.toggleBoolean = function () {
$scope.boolean = !$scope.boolean;
}
}
You can also try $event.stopPropagation();. write it after the child function. It working like a preventdefault.
<body ng-app ng-controller="TestController">
<div id="parent" ng-click="parentClick()">
<div id="child" ng-click="childClick();$event.stopPropagation();"></div>
<p ng-bind="elem"></p>
</div>
<div><p style="text-align:center" ng-bind="childElem"></p></div>
</body>

Troubles when closing div by clicking outside it

I am working on creating a website and I am stuck on a certain function I am trying to build. I am trying to slide back a div to its original place if anyplace outside the div is clicked. I've looked everywhere on stack but to no avail. What happens to me is that the background clicks remain active at all times, I only need it to be active when the div has slid to become sort of a popup.
Here is my jsfiddle: https://jsfiddle.net/DTcHh/10567/
Here is the jquery for one of the divs (the rest are similar)
var text = 1;
$('.login1').click(function(e){
e.preventDefault();
$('.loginform_hidden').toggleClass('loginform_visible');
$(".animateSlide").toggle(300, function(){
$(this).focus();
});
if(text == 1){
$(".div1").toggleClass("animateSlide col-xs-12");
$('.login1').html('Go Back');
$('.imageOne').toggleClass('animateSlideTop');
// If an event gets to the body
$('.div2, .div3, .patientAccess').toggle("fast");
document.addEventListener('mouseup', function(event){
var box = document.getElementsByClassName('animateSlide');
if (event.target != box && event.target.parentNode != box){
$('.div2, .div3, .patientAccess').toggle("fast");
$(".div1").toggleClass("animateSlide ");
text=0;
}
});
text = 0;
} else {
$(".div1").toggleClass("animateSlide");
$('.login1').html('Start Animation');
$('.imageOne').toggleClass('animateSlideTop');
$('.div2, .div3, .patientAccess').toggle("fast");
text = 1;
}
});
$(".div1").on('blur', function() {
$(this).fadeOut(300);
});
EDIT: The jsfiddle now incorporates what I have been trying to utilize.
As a demonstration, I built a simplified version of what I think you're aiming to achieve.
I'm using the "event.target" method described in this answer.
Since you are using CSS transitions, I'm using jQuery to detect the end of those transitions using a method found here.
I've given all boxes a class of "animbox" so that they can all be referenced as a group. I've also given each box its own ID so it can be styled individually with CSS.
I've commented the code in an attempt to explain what's going on.
// define all box elements
var $allBoxes = jQuery('.animbox');
// FUNCTION TO SHOW A SELECTED BOX
function showBox($thisBox) {
$allBoxes.hide(); // hide all boxes
$thisBox.show().addClass('animateSlide'); // show and animate selected box
$('div.login', $thisBox).text("Go Back"); // change the selected box's link text
}
// FUNCTION TO RETURN BOXES TO THE DEFAULT STATE
function restoreDefaultState() {
var $thisBox = jQuery('div.animbox.animateSlide'); // identify an open box
if ($thisBox.length) { // if a box is open...
$thisBox.removeClass('animateSlide'); // close this box
$thisBox.one('webkitTransitionEnd'+
' otransitionend'+
' oTransitionEnd'+
' msTransitionEnd'+
' transitionend', function(e) { // when the box is closed...
$allBoxes.show(); // show all boxes
$('div.login', $thisBox).text("Start Animation"); // change the link text
});
}
}
// CLICK HANDLER FOR ALL "login" TRIGGERS
$('div.login').click(function(e) {
var $thisBox = $(this).closest('div.animbox'); // identify clicked box
if (!$thisBox.hasClass('animateSlide')) { // if the box is not open...
showBox($thisBox); // open it
} else { // otherwise...
restoreDefaultState(); // restore the default state
}
});
// CLICK HANDLER TO RESTORE DEFAULT STATE WHEN CLICK HAPPENS OUTSIDE A BOX
$('body').click(function(evt) {
if ($(evt.target).hasClass('animbox') || // if a box is clicked...
$(evt.target).closest('div.animbox').length > 0) { // or a child of a box...
return; // cancel
}
restoreDefaultState(); // restore the default state
});
div.container-fluid {
background-color: #464646;
}
.v-center {
display: table;
height: 100vh;
}
.content {
display: table-cell;
vertical-align: middle;
text-align: center;
}
.patientAccess {
transition: all .5s;
background: white;
height: 200px;
width: 90%;
position: absolute;
opacity: 0.7;
margin-top: -100px;
}
.patientAccess p {
font-size: 1.5em;
font-weight: bold;
}
div.animbox {
transition: all .5s;
position: absolute;
cursor: pointer;
width: 90%;
height: 100px;
opacity: 0.7;
}
div#animbox1 {
background: #e76700;
}
div#animbox2 {
background: #74b8fe;
}
div#animbox3 {
background: #848484;
}
div.login {
color: white;
font-size: 1em;
cursor: pointer;
}
div#animbox1.animateSlide {
width: 200px;
height: 300px;
margin-left: 100px;
opacity: 1;
}
div#animbox2.animateSlide {
width: 250px;
height: 450px;
margin-left: -25px;
margin-top: -150px;
}
div#animbox3.animateSlide {
width: 150px;
height: 150px;
opacity: .5;
margin-left: -100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<div class="container-fluid">
<div class="row-fluid">
<div class="col-xs-12 v-center">
<div class="content text-center">
<div class="col-xs-2 animated slideInRight "></div>
<div class="col-xs-2 animated slideInRight ">
<div class="patientAccess">
<p>Patient Resource Access</p>
</div>
</div>
<div class="col-xs-2 animated slideInRight">
<div class="animbox" id="animbox1">
<div class="login">Start Animation</div>
<div class="loginform_hidden "></div>
</div>
</div>
<div class="col-xs-2 animated slideInRight">
<div class="animbox" id="animbox2">
<div class="login">Start Animation</div>
<div class="registrationform_hidden"></div>
</div>
</div>
<div class="col-xs-2 animated slideInRight">
<div class="animbox" id="animbox3">
<div class="login">Start Animation</div>
</div>
</div>
</div>
</div>
</div>
</div>
You can namespace an event handler using this syntax:
$("#myElement").on("click.myEventHandlerName", function() { ... });
At any point, you can remove the event handler again by calling
$("#myElement").off("click.myEventHandlerName", "#myElement");

z-index and onclick in HTML

I have the following HTML code:
<script src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
<div id="A" style="width:100px; height: 100px; background: #00FF00; padding: 15px;
z-index: 50; opacity: .5" onclick="javascript:alert('A')">
<div id="B" style="width:50px; height: 50px; background: #FF0000; z-index:10;"
onclick="javascript:alert('B')" >
</div>
</div>
I was hoping this would make it so that clicking on div B's position would not invoke it's onclick, but only A's since A ha a higher z-index.
If not with z-index, how can I achieve this ?
You can use event delegation for that - no need for z-indexes and the like. Assing one (1) click handler to the topmost div and, within the handler, use the event target/srcElement to decide what (not) to do with the originating element. Something like:
<div id="A" style="width:100px; height: 100px;
background: #00FF00; padding: 15px;
z-index: 50; opacity: .5"">
<div id="B" style="width:50px; height: 50px;
background: #FF0000; z-index:10;" ></div>
</div>
The handler function:
function myHandler(e){
e = e || event;
var el = e.srcElement || e.target;
// no action for #B
if (el.id && /b/i.test(el.id)){ return true; }
alert(el.id || 'no id found');
}
// handler assignment (note: inline handler removed from html)
document.querySelector('#A').onclick = myHandler;
See it in action
Your z-index's won't work as you need to change the css position to relative, fixed, or absolute. reference.sitepoint.com/css/z-index.
<div id="A" style="width:100px; height: 100px; background: green; padding: 15px;
z-index: 50; opacity: .5; position:relative;" onclick="alert('A'); return false;">
<div id="B" style="width:100%; height:100%; background: red; z-index:100;position:relative;"
onclick="window.event.stopPropogation();alert('B'); return false;" >
</div>
</div>
http://jsfiddle.net/SmdK8/
I think using position: absolute in your styles and positioning one over the other would do this. Currently div A and div B sit side by side.
<div id="A" style="width:100px; height: 100px; background: #00FF00; padding: 15px;
z-index: 50; opacity: .5" onclick="javascript:alert('A')">
<div id="B" style="width:50px; height: 50px; background: #FF0000; z-index:10;"
onclick="javascript:event.preventDeafult();" >
</div>
</div>
Do a "preventDefault" based on when you don't want B to fire.
Here's one way to handle toggling B's onclick event
example: http://jsfiddle.net/pxfunc/cZtgV/
HTML:
<div id="A">A
<div id="B">B
</div>
</div>
<button id="toggle">Toggle B onclick</button>
JavaScript:
var a = document.getElementById('A'),
b = document.getElementById('B'),
toggleButton = document.getElementById('toggle'),
hasOnClick = true;
a.onclick = function() { alert('hi from A') };
b.onclick = function() { alert('hi from B') };
toggleButton.onclick = function() {
if (hasOnClick) {
b.onclick = "";
} else {
b.onclick = function() { alert('hi from B') };
}
hasOnClick = !hasOnClick;
};
for bonus points there's a jQuery solution in the example.

Categories