detect event on 2 overlapping sibling elements? - javascript

I have 2 overlapping siblings like this:
document.querySelector("#item1").addEventListener('mouseup',()=>{
console.log("Mouseup item 1!");
});
document.querySelector("#item2").addEventListener('mouseup',()=>{
console.log("Mouseup item 2!")
},true);
#item1{
height:10rem;
width:10rem;
background-color:red;
position:absolute;
z-index:-2;
}
#item2{
height:10rem;
width:10rem;
background-color:green;
position:relative;
top:2rem;
}
<div id="item1"></div>
<div id="item2"></div>
whenever I mouseup on item2 I want item1 mouseup event to also be triggered if mouse is within item1
( Basically I want mouseup to be triggered on whatever divs the mouse is within regardless of which is overlapping what )
How do I achieve this?

Using elementsFromPoint as 'pointed out' by #Teemu in the comments I came up with this:
document.querySelector("#wrapper").addEventListener('mouseup', (event) => {
let els = document.elementsFromPoint(event.clientX, event.clientY);
els.forEach(el => {
if (el.id != '' && el.id != 'wrapper')
console.log(el.id)
})
});
#item1 {
height: 10rem;
width: 10rem;
background-color: red;
position: absolute;
}
#item2 {
height: 10rem;
width: 10rem;
background-color: green;
position: relative;
top: 2rem;
}
#wrapper {
background-color: blue;
height: 20rem;
width: 20rem;
}
<div id="wrapper">
<div id="item1"></div>
<div id="item2"></div>
</div>

Related

Is the child element visible in the parent

I am making a comment system, there is a certain block in which there are others (messages) how, when scrolling through these messages, to find out whether the user sees one particular one (for example, with the identifier x) or not,
HTML:
<div class="parent">
<div class="msg" id="a"></div>
<div class="msg" id="b"></div>
<div class="msg" id="c"></div>
<div class="msg" id="x"></div>
</div>
CSS:
.parent {
width: 100%;
height: 100%;
height: 89%;
overflow: scroll;
padding-top: 10px;
padding-bottom: 70px;
}
.msg {
width: 98%;
height: 500px;
padding: 10px;
position: relative;
margin: 0 auto;
border: 2px hsl(174deg 72% 41%) solid;
color: hsl(174deg 72% 41%);
border-radius: 20px
px
;
margin-top: 10px;
}
JS:
document.querySelector(".parent").onscroll = () => {
//what to write here?
}
That is: if the message has become visible in the general block, then paint it in yellow
I tried different options: getComputedStyle, and getBoundingClientRect, and offset, but none of this helped me, they constantly say that the message is visible
BUT:
getBoundingClientRect doesn't work, I don't need to check if it's visible in the whole window, I need to check if it's ONLY visible in a div element
WHEN SCROLLING A PARENT ELEMENT
As suggested in the comments by other user, what you are looking for is the Intersection Observer API
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
Here's a demo trying to apply the concept to your scenario. It's not very well factored but clearly shows a document containing both your .msg container (.parent) styled as overflow-y: scroll; and other elements before and after taking space on the viewport.
All .msg elements will be observed for intersection in the visible space of their parent so that every time each one of them will be visible, its id will be printed on console. Plus there's an added trigger callback that will be invoked in the event described above, that will check for a condition (for example if the element currently became visible has id == 'c') to perform an action.
//this will be called everytime a target element being observer became visible,
//and will check a condition before performing an action (if the element.id === c for example)
const trigger = (element)=>{
if (element.id === 'c')
console.log('condition met! element with id == c was reached.');
}
const observerOptions = {
root: null,
rootMargin: '0px',
threshold: 0
};
//gets called everytime any of the targets appears on the viewport
const observerCallback = (entries, observer) => {
//for each of the observed entries
entries.forEach(entry => {
//console logs the element id currently intersecting the viewport
if (entry.isIntersecting){
console.log(entry.target.id);
trigger(entry.target);
}
});
};
//sets up an observer...
//calling the observerCallback when the observed targets will be intersecting the viewport
const observer = new IntersectionObserver(observerCallback, observerOptions);
//the targets the observer will be observing for (each .msg children inside the .parent element)
const observerTargets = document.querySelectorAll('.parent > .msg');
observerTargets.forEach(target => observer.observe(target));
.other-content{
display: block;
outline: dashed 3px gray;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
font-family: arial;
color: gray;
}
.parent {
display: block;
margin: 10px 10px;
height: 120px;
overflow-y: scroll;
outline: solid 1px gray;
}
.msg {
position: relative;
display: block;
width: 100%;
height: 200px;
padding: 0;
margin: 10px 0;
box-sizing: border-box;
border: solid 2px hsl(174deg 72% 41%);
border-radius: 10px;
}
.msg::before {
position: absolute;
content: 'id: ' attr(id);
top: 50%;
left: 50%;
border-radius: 1rem;
padding: 1rem 1rem;
background: gray;
font-size: 2rem;
font-weight: 600;
color: white;
}
<div class="other-content">
before..
</div>
<div class="parent">
<div class="msg" id="a"></div>
<div class="msg" id="b"></div>
<div class="msg" id="c"></div>
<div class="msg" id="x"></div>
</div>
<div class="other-content">
..after
</div>

Show/hide div or image when scrolling

Is there a way to show an image or a div when scrolling down a web page and hide it when not scrolling and vice versa?
So in the code below the red div would be displayed when not scrolling, and the green div would be displayed only when scrolling.
.square {
position: fixed;
width: 100px;
height: 100px;
}
.green {
background: green;
display: none;
}
.red {
background: red;
}
.container {
width: 100%;
height: 3000px;
}
<div class="container">
<div class="square green"></div>
<div class="square red"></div>
</div>
The end goal is to achieve something like this: https://mailchimp.com/annual-report/ where the character appears to be walking when the user scrolls, and stands still when the user stops. Is this easily achievable?
You just need an eventListener that listen to a scroll event. However this has the issue that it only recoginze when you scroll but not when you stop scrolling. For that you can use this answer that explains how to listen for a "scroll-stop"
To make the code shorter and easier, I removed your display: none from the green box. I added a new class d-none that contains this proeprty now instead. By default it is added to the green box.
With classList.toggle('d-none') I can toggle class within both boxes which makes it easier then to address and then add or remove the class for every box on its own.
var timer = null;
var box = document.querySelectorAll('.square');
window.addEventListener('scroll', function() {
if (timer !== null) {
clearTimeout(timer);
} else {
box.forEach(el => el.classList.toggle('d-none'));
}
timer = setTimeout(function() {
box.forEach(el => el.classList.toggle('d-none'));
}, 150);
}, false);
.d-none {
display: none;
}
.square {
position: fixed;
width: 100px;
height: 100px;
}
.green {
background: green;
/* display: none; */
/* removed */
}
.red {
background: red;
}
.container {
width: 100%;
height: 3000px;
}
<div class="container">
<div class="square green d-none"></div>
<div class="square red"></div>
</div>
You just need a setTimeout function:
(function($) {
$(function() {
$(window).scroll(function() {
$('.square.red').show()
$('.square.green').hide()
clearTimeout($.data(this));
$.data(this, setTimeout(function() {
$('.square.red').hide()
$('.square.green').show()
}, 250));
});
});
})(jQuery);
.square {
position: fixed;
width: 100px;
height: 100px;
}
.green {
background: green;
}
.red {
background: red;
display: none;
}
.container {
width: 100%;
height: 3000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="square green"></div>
<div class="square red"></div>
</div>

Opening a div 'popup' from clicking a parent div

When clicking the 'test' div, it opens up the div price-list-test in a popup way like this:
However, I am stuck on trying to get the div to pop up on the 'right' alignment of the element rather than left, like this:
How can I achieve this?
$(document).ready(function() {
$(".test").each(function() {
$(this).click(function() {
var leftpos = $(this).offset() - window.screen.width;
$(this).children().css("left", leftpos.left);
$(this).children().css("display", "block");
});
})
});
.price-list-test {
width: 100px;
height: 100px;
background-color: red;
color: white;
display: none;
position: absolute;
}
.test {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test">
Open
<div class="price-list-test">
Test
</div>
</div>
Make 'test' class position relative, after that set 'price-list-test' left : 0%
.test {
postion: relative;
}
.price-list-test {
left: 0 %;
width: 100px;
height: 100px;
background-color: red;
color: white;
display: none;
position: absolute;
}

How to keep user's scrolling place when resizing div

I wanted to do a cool menu effect for a website I'm working on. I'm having a div act as the the section for the main content. When the user opens the menu, the main content div will resize and move out of the way, revealing the menu. However, when I do this with the code I have written, it always loses my scrolling place on the page. Is there any way to keep my place on the page when it shrinks and also when it expands back again? Below is what I have. Thank you in advance!
function shrinkPage() {
var element = document.getElementById("mock-body");
element.classList.toggle("mock-body-on-burger");
var z = document.getElementById("mock-body-container");
z.classList.toggle("mock-body-container-on-burger");
var x = document.getElementById("body");
x.classList.toggle("body-on-burger");
};
body {
margin: 0;
background:#000;
}
.body-on-burger {
max-width: 100%;
overflow-x:hidden;
}
.mock-body-container{
height:100vh;
}
.mock-body-container-on-burger {
height:100vh;
transform: scale(0.4) translate(130%);
overflow: hidden;
}
.mock-body-size-change{
overflow: scroll;
}
.mock-body {
position:relative;
background: #fff;
margin-left: 50px;
}
.container {
position: fixed;
height:50px;
width:50px;
cursor: pointer;
}
.container #icon {
width: 16px;
height: 8px;
position: relative;
margin: 0px auto 0;
top: 40%;
}
.container #icon .bars {
height: 1px;
background: #fff;
}
.myDiv {
height:500px;
}
.one {
background:red;
}
.two {
background:green;
}
.three {
background:blue;
}
<body id="body">
<div class="menu-activator" onclick="shrinkPage()">
<div class="container usd">
<div id="icon">
<div class="bars first"></div>
</div>
</div>
</div>
<div id="mock-body-container" class="mock-body-container">
<div id="mock-body" class="mock-body">
<div class="myDiv one"></div>
<div class="myDiv two"></div>
<div class="myDiv three"></div>
</div>
</div>
</body>
Please take a look at the snippet below. Notice how the overflow property is used.
You have to scroll mock-body-container to keep its scrolling position.
You're scrolling body instead, so when you scale mock-body-container there is nothing to scroll in body and you loose the scrolling position.
function shrinkPage() {
var element = document.getElementById("mock-body");
element.classList.toggle("mock-body-on-burger");
var z = document.getElementById("mock-body-container");
z.classList.toggle("mock-body-container-on-burger");
var x = document.getElementById("body");
x.classList.toggle("body-on-burger");
};
body {
margin: 0;
background:#000;
}
.body-on-burger {
max-width: 100%;
overflow-x:hidden;
}
.mock-body-container{
height:100vh;
overflow:auto;
}
.mock-body-container-on-burger {
height:100vh;
transform: scale(0.4) translate(130%);
}
.mock-body-size-change{
overflow: scroll;
}
.mock-body {
position:relative;
background: #fff;
margin-left: 50px;
}
.container {
position: fixed;
height:50px;
width:50px;
cursor: pointer;
}
.container #icon {
width: 16px;
height: 8px;
position: relative;
margin: 0px auto 0;
top: 40%;
}
.container #icon .bars {
height: 1px;
background: #fff;
}
.myDiv {
height:500px;
}
.one {
background:red;
}
.two {
background:green;
}
.three {
background:blue;
}
<body id="body">
<div class="menu-activator" onclick="shrinkPage()">
<div class="container usd">
<div id="icon">
<div class="bars first"></div>
</div>
</div>
</div>
<div id="mock-body-container" class="mock-body-container">
<div id="mock-body" class="mock-body">
<div class="myDiv one"></div>
<div class="myDiv two"></div>
<div class="myDiv three"></div>
</div>
</div>
</body>
Once you know the element that was in focus it should be relatively easy. If you need to find which element was last in focus, you can do that with a scroll function. If you need this as well let me know and I will update my answer.
If you know that #mock-body is the last element in focus, just scroll back to it after the resize.
In this example I've used jQuery as it makes this interaction easier, but this can be done (albeit more verbosely) with vanilla JS as well.
$('html, body').animate({
scrollTop: $('#mock-body').offset().top
}, 0); // If you want the animation to be smoother you can increase 0 to a higher number
A simple way to do it is to remember the position of the document scroll and reapply it when you getting back to "normal" view:
let savedScroll;
function shrinkPage() {
let _s = (el) => document.querySelector(el),
s_ = (d) => !d.classList.contains('body-on-burger'),
x = _s('#body'),
element = _s('#mock-body'),
z = _s('#mock-body-container');
if (s_(x)) {
savedScroll = document.documentElement.scrollTop;
}
element.classList.toggle("mock-body-on-burger");
z.classList.toggle("mock-body-container-on-burger");
x.classList.toggle("body-on-burger");
if (s_(x)) {
document.documentElement.scrollTop = savedScroll;
}
};
Check it out:
let savedScroll;
function shrinkPage() {
let _s = (el) => document.querySelector(el),
s_ = (d) => !d.classList.contains('body-on-burger'),
x = _s('#body'),
element = _s('#mock-body'),
z = _s('#mock-body-container');
if (s_(x)) {
savedScroll = document.documentElement.scrollTop;
}
element.classList.toggle("mock-body-on-burger");
z.classList.toggle("mock-body-container-on-burger");
x.classList.toggle("body-on-burger");
if (s_(x)) {
document.documentElement.scrollTop = savedScroll;
}
};
body {
margin: 0;
background: #000;
}
.body-on-burger {
max-width: 100%;
overflow-x: hidden;
}
.mock-body-container {
height: 100vh;
}
.mock-body-container-on-burger {
height: 100vh;
transform: scale(0.4) translate(130%);
overflow: hidden;
}
.mock-body-size-change {
overflow: scroll;
}
.mock-body {
position: relative;
background: #fff;
margin-left: 50px;
}
.container {
position: fixed;
height: 50px;
width: 50px;
cursor: pointer;
}
.container #icon {
width: 16px;
height: 8px;
position: relative;
margin: 0px auto 0;
top: 40%;
}
.container #icon .bars {
height: 1px;
background: #fff;
}
.myDiv {
height: 500px;
}
.one {
background: red;
}
.two {
background: green;
}
.three {
background: blue;
}
<body id="body">
<div class="menu-activator" onclick="shrinkPage()">
<div class="container usd">
<div id="icon">
<div class="bars first"></div>
</div>
</div>
</div>
<div id="mock-body-container" class="mock-body-container">
<div id="mock-body" class="mock-body">
<div class="myDiv one"></div>
<div class="myDiv two"></div>
<div class="myDiv three"></div>
</div>
</div>
</body>
Legend: _s(el) returns first match of el and s_(d) checks if d has class body-on-burger.
The simple way to do this is to determine the change in height during the resize, and scroll that much.
const heightChange = newHeight - initialHeight;
scrollableDiv.scrollTop = scrollableDiv.scrollTop - heightChange;
In my case I am using a resize method I wrote, so I do this work inside of a window.addEventListener("mousemove", handleResize); when I know the div in actively being resized by the user.
This will still work fine with native html resizable elements, you just need to figure out how/when to listen for resize/drag events accordingly.

How to remove hover state when the element moves

I have this example:
<div class="container">
<div class="bp"></div>
</div>
http://jsfiddle.net/VKwjD/20/
if you hover the same square, his color changes; if you click, the parent size changes so the square move.
My problem is: If you don't move your mouse after the click, the square stays in the hover state... Why ?
It's possible to remove this state after the click? without moving the mouse...
Thank you for your help
HTML:
<div class="container">
<div class="bp" id="bp"></div>
</div>
CSS:
.container {
background: #FF0000;
width: 200px;
height: 200px;
position: relative;
}
.container.hide {
width: 50px;
}
.bp {
background: #00FFFF;
width: 30px;
height:30px;
top:0px;
right:0px;
position:absolute;
cursor: pointer;
}
.bp:hover{
background: #0000FF!important;
}
JavaScript:
$(document).ready(function() {
$('.bp').click(function() {
if($('.container').hasClass('hide')) {
$('.container').removeClass('hide');
var l1 = document.getElementById('bp');
l1.style.background = '#00FFFF';
}
else {
$('.container').addClass('hide');
var l1 = document.getElementById('bp');
l1.style.background = '#0000FF';
}
});
});

Categories

Resources