how to trigger multi event in same level multi div overlap area? - javascript

i define a event in same level div,but i can't trigger the event with div between multi different div overlap area,just one of them triggered,how to trigger these same level div events by overlap area?
the example code like this
function Triggered(e){
console.log(event.target.id);//
}
.S{
position: absolute;
width: 99px;
height: 99px;
opacity: .7;
}
<body>
<div id="1" style="background-color:blue;left: 50%;top:50%" onclick="Triggered()" class="S"></div>
<div id="2" style="background-color:red;left: 50%;top:50%;transform: translate(-25%,-50%);" onclick="Triggered()" class="S"></div>
<div id="3" style="background-color:green;left: 50%;top:50%;transform: translate(-50px,0);" onclick="Triggered()" class="S"></div>
</body>
(ps: it's use for slove the li sort question,when li A over another li B,the mouseover event in B can't trigger,just trigger A,so i need a solution for slove multi same level div event triggered in overlap area,pointer-events:none it's not helpful,i need trigger multi event in overlap area not one)

If I understood correctly you want to detect click event for your three divs, even when they overlap each other. Since your tags are defined at the same level, you cannot use event bubbling or capturing to handle click at different levels like we do with nested tags.
When user clicks, try calling function document.elementsFromPoint(x, y) to get all elements underneath the mouse pointer.
function Triggered(e) {
var clickedElements = [];
document.elementsFromPoint(event.clientX, event.clientY).forEach(function(element) {
if (element.classList.contains('S')) {
clickedElements.push(element.id);
}
});
console.log(clickedElements);
}
.S {
position: absolute;
width: 99px;
height: 99px;
opacity: .7;
}
<div onclick="Triggered()">
<div id="blue" style="background-color:blue;left: 50%;top:15%" class="S"></div>
<div id="red" style="background-color:red;left: 50%;top:15%;transform: translate(-25%,-50%);" class="S"></div>
<div id="green" style="background-color:green;left: 50%;top:15%;transform: translate(-50px,0);" class="S"></div>
</div>

I started out with an approach along the lines of:
if (e.target.style.getPropertyValue('pointer-events') !== 'none') {
console.log(e.target.id);
}
else {
e.target.style.setProperty('pointer-events', 'none');
e.target.dispatchEvent('click');
e.target.style.removeProperty('pointer-events');
}
But quickly learned that the pointer-events property does not work with computational clicks.
This is my final solution (ultimately very similar to the solution provided by #derloopkat, above, so credit should go to that answer, not this one).
Working Example:
const div1 = document.getElementById('div1');
const div2 = document.getElementById('div2');
const div3 = document.getElementById('div3');
const triggered = (e) => {
// GET MOUSE POSITION
const x = e.clientX;
const y = e.clientY;
// GET STACKED ELEMENTS
const stackedElements = document.elementsFromPoint(x, y);
// BUILD ARRAY OF TARGET ELEMENTS
const targetElements = [];
for (let stackedElement of stackedElements) {
if (stackedElement.classList.contains('s')) {
targetElements.push(stackedElement);
}
}
// LOG IDS OF TARGET ELEMENTS
for (let targetElement of targetElements) {
console.log(targetElement.id);
}
}
div1.addEventListener('click', triggered, true);
div2.addEventListener('click', triggered, true);
div3.addEventListener('click', triggered, true);
.s {
position: absolute;
top:50%;
left: 50%;
width: 99px;
height: 99px;
opacity: .7;
}
#div1 {
background-color: blue;
}
#div2 {
background-color: red;
transform: translate(-25%, -50%);
}
#div3 {
background-color: green;
transform: translate(-50px, 0);
}
<div id="div1" class="s"></div>
<div id="div2" class="s"></div>
<div id="div3" class="s"></div>

Related

Child element is causing issues with parent onmousemove event listener

I have created a very simple example of my problem.
Fiddle Link
In the fiddle, I have created a div named parent containing 2 imgs (i take divs in the example for simplicity but in my project, these are images) and a controller div. I place the images on the top of each other by positioning 2nd image as absolute.
I want to clip the 2nd image using clip-path property whenever, I click and then drag the controller" over the parent div.
But the controller div is causing issue with parent mousemove event whenever cursor goes on controller div, mouseout event is fired on parent div causing glitch in animation.
Adding pointer-events: none property to controller div fix the glitch but it also takes away every kind of mouse interaction from the element and I want click and drag effect.
I want to create similar effect used in this website.
The problem seems to be that the positioning of the controller sometimes (not always) 'interferes' with the reading of offsetX on the parent. And the offset goes down (to 0 or up to about 10 in the given fiddle). Hence you get the flickering as the controller moves back and then up along again.
I cannot at the moment totally explain this, particularly since the controller is an absolutely positioned element.
However, one solution is to move the controller out of the parent.
UPDATE It is though possible to leave the controller in the parent if one ignores any mousemove within the controller (so we don't get readings of 0 to 10 for the offset when the mousemove is within the controller - ignore them and we'll get the event bubbling through to the parent and can then take a reading of offset).
_
<head>
<style>
* {
margin: 0;
padding: 0;
}
#parent {
width: 100%;
position: relative;
}
#img1, #img2 {
display: block;
width: 100%;
height: 200px;
pointer-events: none;
}
#img1 {
background: red;
}
#img2 {
background: green;
position: absolute;
top: 0;
left: 0;
}
#controller {
position: absolute;
top: 0;
left: 10px;
width: 10px;
height: 100%;
height: 200px;
background: black;
z-index: 1;
cursor: ew-resize;
/* pointer-events: none; */
}
</style>
</head>
<body>
<div id="parent">
<div id="img1"></div>
<div id="img2"></div>
<div id="controller"></div>
</div>
<h4>
Click and Drag the controller to clip the front image
</h4>
<!-- img1, img2 are images in my case so i named them as imgs -->
<script>
const parent = document.getElementById('parent'),
img2 = document.getElementById('img2'),
controller = document.getElementById('controller');
let pressed = false;
console.log(pressed)
parent.addEventListener('mousemove', (e) => {
if(!pressed) return;
if (e.target != parent) return;
img2.style.clipPath = `inset(0px 0px 0px ${e.offsetX}px)`;
controller.style.left = `${e.offsetX}px`;
});
// for testing purpose
/* parent.addEventListener('mouseout', (e) => {
console.log('mouse out is called');
}); */
controller.addEventListener('mousedown', (e) => {
pressed = true;
});
controller.addEventListener('mouseup', (e) => {
pressed = false;
});
</script>
</body>
const parent = document.getElementById('parent'),
img2 = document.getElementById('img2'),
controller = document.getElementById('controller');
let pressed = false;
parent.addEventListener('mousemove', (e) => {
if (pressed) {
img2.style.clipPath = `inset(0px 0px 0px ${e.clientX - parent.offsetLeft}px)`;
controller.style.left = `${e.clientX - parent.offsetLeft}px`;
}
});
controller.addEventListener('mousedown', (e) => {
pressed = true;
});
controller.addEventListener('mouseup', (e) => {
pressed = false;
});
#parent {
width: 100%;
position: relative;
}
#img1,
#img2 {
display: block;
width: 100%;
height: 200px;
pointer-events: none;
}
#img1 {
background: red;
}
#img2 {
background: green;
position: absolute;
top: 0;
left: 0;
}
#controller {
position: absolute;
top: 0;
left: 10px;
width: 10px;
height: 100%;
background: black;
z-index: 1;
cursor: ew-resize;
/* pointer-events: none; */
}
<div id="parent">
<div id="img1"></div>
<div id="img2"></div>
<div id="controller"></div>
</div>
<h4>
Click and Drag the controller to clip the front image
</h4>
The problem is, you used offsetX which defines the distance between the top left edge of your controller element. This means the distance is about 5px, your controller jumps to 5px from left, the distance is bigger now, the controller jumps back and so on.
The offsetX read-only property of the MouseEvent interface provides
the offset in the X coordinate of the mouse pointer between that event
and the padding edge of the target node.
So therefore you can use the difference between the mouse x-position and the x-position of parent for positioning your controller:
Instead use clientX which gets the mouse position relative to the window.
img2.style.clipPath = `inset(0px 0px 0px ${e.clientX - parent.offsetLeft}px)`;
controller.style.left = `${e.clientX - parent.offsetLeft}px`;
Top expression has following meaning:
<mouse x-position> - <distance between left screen edge and parent>

How to make an element reset its position after mouseout event in javascript

trying to make a button like this: https://gyazo.com/9afbd559c15bb707a2d1b24ac790cf7a. The problem with the code right now is that it works as it is supposed to on the first time; but after that, instead of going from left to right as intented, it goes from right to left to right.
HTML
<div class="btn-slide block relative mx-auto" style="overflow: hidden; width: 12rem;">
<span class="z-10">View Pricing</span>
<span class="slide-bg block absolute transition" style="background-color: rgba(0,0,0,.1); z-index: -1; top: 0; left:-10rem; width: 10rem; height: 3rem;"></span>
</div>
Javascript
const btns = document.querySelectorAll(".btn-slide");
const slide = document.getElementsByClassName('slide-bg');
btns.forEach(function(btn) {
btn.addEventListener('mouseout', function () {
slide[0].style.transform = 'translateX(230%)';
slide[0].style.transform = 'none';
})
btn.addEventListener('mouseover', function() {
slide[0].style.transform = 'translateX(80%)';
}, true)
})
Unless you have to compute a value in JavaScript (like the height of an element).
Use CSS classes as modifiers (is-hidden, is-folded, is-collapsed, ...).
Using JavaScript, only add/remove/toggle the class
yourElement.addEventListener(
"mouseenter",
function (event)
{
yourElement.classList.remove("is-collapsed");
}
);
yourElement.addEventListener(
"mouseleave",
function (event)
{
yourElement.classList.add("is-collapsed");
}
);
is-collapsed is only an exemple, name it according to your class naming standard.
You're probably going to need a bit more code than what you're showing, as you have two mutually exclusive CSS things you want to do: transition that background across the "button" on mouseenter/mouseout, which is animated, and then reset the background to its start position, which should absolutely not be animated. So you need to not just toggle the background, you also need to toggle whether or not to animation those changes.
function setupAnimation(container) {
const fg = container.querySelector('.label');
const bg = container.querySelector('.slide-bg');
const stop = evt => evt.stopPropagation();
// step one: make label text inert. This is critical.
fg.addEventListener('mouseenter', stop);
fg.addEventListener('mouseout', stop);
// mouse enter: start the slide in animation
container.addEventListener('mouseenter', evt => {
bg.classList.add('animate');
bg.classList.add('slide-in');
});
// mouse out: start the slide-out animation
container.addEventListener('mouseout', evt => {
bg.classList.remove('slide-in');
bg.classList.add('slide-out');
});
// when the slide-out transition is done,
// reset the CSS with animations _turned off_
bg.addEventListener('transitionend', evt => {
if (bg.classList.contains('slide-out')) {
bg.classList.remove('animate');
bg.classList.remove('slide-out');
}
});
}
setupAnimation(document.querySelector('.slide'));
.slide {
position: relative;
overflow: hidden;
width: 12rem;
height: 1.25rem;
cursor: pointer;
border: 1px solid black;
text-align: center;
}
.slide span {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.slide-bg {
background-color: rgba(0,0,0,.1);
transform: translate(-100%, 0);
transition: none;
z-index: 0;
}
.slide-bg.animate {
transition: transform 0.5s ease-in-out;
}
.slide-bg.slide-in {
transform: translate(0%, 0);
}
.slide-bg.slide-out {
transform: translate(100%, 0);
}
<div class="slide">
<span class="label">View Pricing</span>
<span class="slide-bg"></span>
</div>
And thanks to browsers being finicky with rapid succession mouseenter/mouseout events, depending on how fast you move the cursor this may not even be enough: you might very well still need a "step" tracker so that your JS knows which part of your total animation is currently active, and not trigger the mouseout code if, by the time the slide-in transition ends, the cursor is in fact (still) over the top container (or, again).
I advice you use the .on event listener
$('').on("mouseentre","elem",function(){$('').toggleclass('.classname')})
$('').on("mouseleave","elem",function(){$('').toggleclass('.classname')})
Then you can toggle css classes to your element in the function
toggle class adds the css of a class to your jquery selection, you can do it multiple times and have keyframes for animation in the css class
Keyframes are great way to implement animation and are supported on every browers

Hover over an element covered by another element's padding

So I have a map composed by tiles that are svg elements.
In the image, the tile itself is the blue area, but it has a buffer area to allow geometries that span outside the tile to render whole. The problem is that this buffer area (in green), is covering the geometries from other tiles that are below it. This buffer zone is set in CSS as the following:
padding: 128px;
margin: -128px;
Is there a way to hover/click "through" the buffer area, or is there a better approach in CSS to achieve this?
Padding is part of a element, therefore will react like it was content of your tag.
See here. If you absolutely need to have that spacing to be padding, you can't click anything behind neither content nor padding.
You might consider changing your layering, using z-index, but for further advise on this, you'll have to provide further code, your HTML Markup and CSS code.
If that blue thingy is the only element that needs this treatment, then I suggest creating a key bind to move that element to the back with z-index, and be done with it.
If you require this functionality on all of these red balls, the thing to do would probably be to move, the one you click on, to the back with z-index ( again ).
Both of these require you to use JavaScript most likely, unless you want to move that big blue element to the back on hover.
To always move the clicked element to the back you could just keep track of what was the last assigned z-index, and decrease it by one every time you assign it to a new object.
Something like this would probably do:
#box1 { position: absolute; background-color: #123; width: 100px; height: 100px; top: 200px; left: 300px; opacity: 0.9; }
#box2 { position: absolute; background-color: #ABC; width: 100px; height: 100px; top: 250px; left: 350px; opacity: 0.8; }
<div class="box" id="box1"></div>
<div class="box" id="box2"></div>
<script type="text/javascript">
var boxes = document.getElementsByClassName("box");
var length = boxes.length;
var index = 0;
function moveToBack(event)
{
var element = this;
this.style.zIndex = index;
index--;
return false;
}
for(var i = 0; i < length; i++)
{
var box = boxes[i];
box.addEventListener("click", moveToBack, false);
}
</scirpt>
Does that do the job ? or did you mean something else entirely ?
Only way I can seem to get it to work is to add a child inner element and give pointer-events:none to the wrapper and pointer-events:auto to the inner child element. It's not ideal as support for pointer-events is limited and there's no telling if all browsers will respect a child of pointer-events:none element having a different value than its parent. It will need tested. Well, in any case, here is the code:
$('.tile').on('mouseenter', function(){
$('.info', this).find('.tile-info').remove();
var ts = new Date().getTime();
$('.info', this).append('<div class="tile-info">mouseenter tile '+ts+'</div>');
});
$('.tile').on('mouseleave', function(){
$('.info', this).find('.tile-info').remove();
var ts = new Date().getTime();
$('.info', this).append('<div class="tile-info">mouseleave tile '+ts+'</div>');
});
$('.inner').on('mouseenter', function(){
$('.info', this).find('.inner-info').remove();
var ts = new Date().getTime();
$('.info', this).append('<div class="inner-info">mouseenter inner '+ts+'</div>');
});
$('.inner').on('mouseleave', function(){
$('.info', this).find('.inner-info').remove();
var ts = new Date().getTime();
$('.info', this).append('<div class="inner-info">mouseleave inner '+ts+'</div>');
});
.tile {
width: 200px;
height: 200px;
background: rgba(100,100,200,0.2);
padding: 50px;
margin: -50px;
position: absolute;
pointer-events:none;
}
.tile:nth-of-type(1) {left: 300px;top: 20px;}
.tile:nth-of-type(2) {left: 90px;top: 210px;}
.tile:nth-of-type(3) {left: 0px;top: 0px;}
.tile:nth-of-type(4) {left: 360px;top: 240px;}
.tile .inner {
width: 100%;
height: 100%;
background: rgba(200,100,100,0.2);
pointer-events:auto;
overflow: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="tile">
<div class="inner">
<div class="info"></div>
</div>
</div>
<div class="tile">
<div class="inner">
<div class="info"></div>
</div>
</div>
<div class="tile">
<div class="inner">
<div class="info"></div>
</div>
</div>
<div class="tile">
<div class="inner">
<div class="info"></div>
</div>
</div>

How to get top level object under the mouse function?

Title says it all. I've got child div's with absolute positions inside a relative parent div, and would like to know whether the mouse is over a child or a parent div at a "random" point in time.
Hypothetically, I'd like to call the .mouseover method and perform a .hasclass test on the highest level object to see if it has the child class or not. However, .mouseover is an event handler, thus not something I could just call to get the relevant information.
Example HTML below:
$(document).ready(function() {
$(".child").draggable();
setTimeout(doSomething, 31415);
});
var doSomething = function() {
// Edit content based on what is underneath the mouse
}
.parent {
width: 100%;
height: 1000px;
position: relative;
background: #f0f0f0;
}
.child {
width: 300px;
height: 100px;
position: absolute;
background: #cccccc;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.4/themes/ui-lightness/jquery-ui.css">
<script src="http://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
</div>
Getting an element from a position is what the document.elementFromPoint function was designed to do:
document.elementFromPoint(mousePosition.x, mousePosition.y);
To get the current mouse position, attach a listener to mousemove (as far as I know there is no native method to extract mouse coordinates without a mouse event). Here's an example fiddle showing this: https://jsfiddle.net/xsLwt8Ld/
If I understood correctly, you want to know if at any given time, the mouse is over the child or directly over the parent. You could achieve it by using the :hover pseudoclass
Create a function that checks if there is any .child that has the :hover class:
If there is, that means that the mouse is over a .child (and you have the element) and there's no need to check the parent.
If there isn't, then check if there is any .parent element that also has the class that you created:
If there is: the mouse is over a .parent but not over a .child;
If there is not: the mouse i not over a .parent or a .child.
The code to achieve this is simple:
function checkMouseOver() {
if ($(".child:hover").length) {
// mouse over a .child
} else if ($(".parent:hover").length) {
// mouse over a .parent (but not over .child)
} else {
// mouse not over a .parent or .child;
}
}
A simple working demo:
$(".child").draggable();
// Edit content based on what is underneath the mouse
function checkMouseOver() {
if ($(".child:hover").length) {
alert("You were over " + $(".child:hover").text());
} else if ($(".parent:hover").length) {
alert("You were over " + $(".parent:hover").attr("id"));
} else {
alert("You are not over a .parent or .child");
}
}
.parent {
width: 100%;
height: 1000px;
position: relative;
background: #f0f0f0;
}
.child {
width: 300px;
height: 100px;
position: absolute;
background: #cccccc;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.4/themes/ui-lightness/jquery-ui.css">
<script src="http://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
<button onclick="checkMouseOver()">Check where the mouse is</button>
<div class="parent" id="parent1">
<div class="child">Child 1</div>
<div class="child">Child 2</div>
</div>
<div class="parent" id="parent2">
<div class="child">Child 3</div>
<div class="child">Child 4</div>
</div>
(Click on the page and press tab until you get into the button, then mouse over the different elements and press Enter to trigger the funtion)

css+js: div click (and elements inside it)

I'm trying to build a (semi-transparent) overlay that covers all on a web page.
Similar to this: http://www.jankoatwarpspeed.com/use-jquery-to-turn-off-the-lights-while-watching-videos
<div id="overlaySplash" onclick="clickHandler(this)">
<div id="insideBox">
[ label elements here with onclick="dosomething()" ]
</div>
</div>
css
#overlaySplash {
position: absolute;
top: 0;
left: 0;
bottom:0;
right:0;
background: rgb(50, 50, 50);
background: rgba(0, 0, 0, .50);
z-index: 50;
}
#insidebox {
position: absolute;
z-index: 100;
left: 15%;
right: 15%;
bottom: 5%;
line-height: 50px;
text-align: left;
}
The problem is that I'm not using jquery and the overlay div will have some clicable contents on int. I have this function below but when I click on elements inside the main overlay div JS will return e.id as being the overlay div itself, not the clicked element. This way I can't tell where the user really clicked.
function clickHandler(e){ //hide the div overlaySplash
e = e || event;
alert(e.id);
}
THE BOTTOM LINE:
user clicked an label inside the div: dosomething();
user clicked the background (the DIV itself): closeOverlaySplash();
I don't think you need to completely stop propagation as it may serve some purpose later on. It might be easiest to create a separate js & css files that encompass this functionality for ease of use.
The issue you have is basically the event is bubbling up to the parent when it isn't currently needed. You can easily check the event.target.id to see if the parent was clicked or not. With this you can make sure the overlay was clicked vs the content.
eg:
if (event.target.id == "overlay") {
//hide the overlay
}
JSFiddler
Like this:
HTML
<div id="overlaySplash" onclick="clickHandler(event);">
<div id="insideBox" onclick="clickHandlerInner(event);">Inner</div>
</div>
JS
function clickHandler(event) {
alert("no");
}
function clickHandlerInner(event) {
if (!event) var event = window.event;
event.cancelBubble = true; //IE
if (event.stopPropagation) event.stopPropagation()
alert("yes")
}
jsFiddle

Categories

Resources