Send mouseover event to all overlaid elements [duplicate] - javascript

Is it possible to make two overlapping divs, both clickable?
I've appended divs to two containers, #container and #container2. Their styles are exactly the same only except one is flex-direction: column; and one is flex-direction: column;. Both position:absolute with #container2 on top. I made each of the appended child clickable to fill its background color. Only the div on top is clickable so far, is there a way to make both clickable? or is there another way to have the bottom div react to my clicks?
window.addEventListener('load', init);
function init() {
calculateGrid();
//calculate grid
function calculateGrid() {
var w = window.innerWidth;
var h = window.innerHeight;
var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);
function randomInRange(from, to) {
let x = Math.random() * (to - from);
return x + from;
};
for (var i = 0; i < totalNum; i++) {
var div = document.createElement('div');
div.setAttribute('class', 'grid');
div.style.width = randomInRange(3, 10) + 'vw';
div.style.height = randomInRange(5, 10) + 'vh';
document.getElementById('container').appendChild(div);
document.getElementById('container2').appendChild(div.cloneNode(true));
}
};
$(".grid").click(function() {
$(this).toggleClass('selected');
});
};
#container {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: column;
overflow: hidden;
}
#container .grid {
border: 1px solid blue;
}
#container2 {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: row;
overflow: hidden;
}
#container2 .grid {
border: 1px solid red;
}
.grid {
font-size: 10px;
color: white;
}
#container .selected {
background-color: blue;
}
#container2 .selected {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="wrapper">
<div id="container"></div>
<div id="container2"></div>
</div>
View on CodePen

One method is to use Document.elementsFromPoint() to return "an array of all elements at the specified coordinates". Iterate through that array, adding the "selected" class to "grid" elements.
window.addEventListener('load', init);
function init() {
// build grid
function calculateGrid() {
var w = window.innerWidth;
var h = window.innerHeight;
var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);
function randomInRange(from, to) {
let x = Math.random() * (to - from);
return x + from;
};
for (var i = 0; i < totalNum; i++) {
var div = document.createElement('div');
div.setAttribute('class', 'grid');
div.style.width = randomInRange(3, 10) + 'vw';
div.style.height = randomInRange(5, 10) + 'vh';
document.getElementById('container1').appendChild(div);
document.getElementById('container2').appendChild(div.cloneNode(true));
}
};
// handle grid clicks
function handleGridClick(e) {
let elms = document.elementsFromPoint(e.clientX, e.clientY);
Array.from(elms).forEach(elm => {
if (elm.classList.contains('grid'))
elm.classList.add('selected');
});
}
// initialize grid and click handler
calculateGrid();
document.addEventListener('click', handleGridClick);
};
.container {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
overflow: hidden;
}
#container1 {
flex-direction: column;
}
#container1 .grid {
border: 1px solid blue;
}
#container1 .grid.selected {
background-color: blue;
}
#container2 .grid {
border: 1px solid red;
}
#container2 .grid.selected {
background-color: red;
}
<div id="wrapper">
<div id="container1" class="container"></div>
<div id="container2" class="container"></div>
</div>

You can't actually hover two items at the same time in plain 'ol HTML/CSS - for that you will need JavaScript as explained in the accepted solution. However, there's a CSS-only solution to allow hovering over the different layers, which was fun to figure out at the very least.
So the idea is that you have these invisible boxes on top of the visible ones. The invisible boxes only have borders such that any time your mouse hits a border, some clever z-index swapping takes place to make the visible containers change their stacking order.
For every .grid item you need to create a corresponding .box item: https://jsfiddle.net/ryanwheale/01v5yz86/93/

Related

Move DIV using CSS Grid

I created a CSS Grid layout with global variables:
#app{
--width-l: 0.5fr;
--width-c: 0.5fr;
}
So I have a bar in the middle of my screen. On the other hand, in JavaScript I have two events that observe if the mouse is pressed (mousedown) and in motion (mousemove) that move the div. It has a problem, the movement is above the mouse position and at the height of the #app. So it works in parts, when I'm near the top, the div#bar doesn't go up anymore, and near the bottom, the same thing happens but at a greater distance.
I'm looking for a solution to make the transition smoothly, using the grid positions.
This is the code I created to try:
var split = document.querySelector(".split");
var app = document.querySelector("#app");
var position = app.getBoundingClientRect();
var isMouseMove = false;
split.addEventListener("mousedown", (e) => {
isMouseMove = true;
this.addEventListener("mouseup", (e) => {
isMouseMove = false;
});
});
split.addEventListener("mousemove", (e) => {
if (isMouseMove) {
let fullSize = app.offsetHeight;
let average = (100 * (e.y - position.top)) / fullSize;
let up = (average / 100).toFixed(4);
let down = (1 - average / 100).toFixed(4);
app.style.setProperty("--width-l", `${up}fr`);
app.style.setProperty("--width-c", `${down}fr`);
}
});
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
header {
background: aquamarine;
height: 50px;
}
footer {
background: aqua;
height: 50px;
}
#app {
--width-l: 0.5fr;
--width-c: 0.5fr;
height: calc(100vh - 100px);
display: grid;
grid-template: "aside up" var(--width-l) "aside split" 50px "aside down" var(
--width-c
) / 100px auto;
}
#app .aside {
grid-area: aside;
background: blue;
}
#app .up {
grid-area: up;
background: yellow;
resize: horizontal;
}
#app .split {
grid-area: split;
background: floralwhite;
display: flex;
justify-content: center;
align-items: center;
font-weight: 900;
color: tomato;
user-select: none;
}
#app .down {
grid-area: down;
background: green;
}
#app .split:active {
cursor: move;
}
<header></header>
<div id="app">
<div class="aside"></div>
<div class="up"></div>
<div class="split"> CLICK AND MOVE </div>
<div class="down"></div>
</div>
<footer></footer>

How can I repair moving square - javascript

I want to ask you for help. I'm creating a moving square that have to work in this way:
I'm grabbing it
It follows my cursor
It stays in place where I moused up
But I have a problem with the second point because the square is lagging when I'm using offsetX / Y. I discovered that the reason is the cursor on the square, because then the offset stops counting the axes X/Y. What can I do with it? When I move square to left or top, then it's ok.
HTML CODE:
<div class="container">
<div class="square">
</div>
</div>
CSS:
body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
background: bisque;
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 80vw;
height: 80vh;
border: 1px solid rgb(255, 122, 122);
position: relative;
overflow: hidden;
}
.square {
width: 100px;
height: 100px;
background: blueviolet;
cursor: pointer;
position: relative;
}
JAVASCRIPT:
const square = document.querySelector('.square')
const container = document.querySelector('.container')
square.addEventListener('mousedown', createClass);
square.addEventListener('mouseup', removeClass);
container.addEventListener('mousemove', movingSquare);
// ADDING CLASS 'ACTIVE'
function createClass(e) {
e.target.className = "square active";
}
// MOVING SQUARE
function movingSquare (e) {
if (square.classList.contains('active')) {
square.style.transform = `translate(${e.offsetX}px, ${e.offsetY}px)`;
// square.style.left = e.offsetX + 'px';
// square.style.top = e.offsetY + 'px';
}
}
// REMOVING CLASS 'ACTIVE'
function removeClass(e) {
e.target.className = "square"
}
I can see a possible advantage of of using .offset values but I prefer to use clientX and clientY values. It does require adjustment to allow for the positions of the parent element but is not difficult.
In my snippet, instead of adding and removing classes, I access the .left and .top style properties of the element. A class name could be added if you want to change other aspects.
Like in your code I use event listeners for mousedown, mousemove, and mouseup.
In the mousedown event I set a boolean flag true if the target element is the moveable box (this equates to your class addition). I also set .client cursor positions for the start, and update the initial left and top values (relative to its parent container by subtraction) for the moveable element.
This allows the mousemove listener to update (provided the target flag was set true) the style left and top properties by an amount equal to the movement of the cursor coordinates since the mousedown was made.
const box = document.getElementsByClassName('square')[0];
let downInBox=false;
let initialLeft = 0;
let initialTop = 0;
let deltaX = 0;
let deltaY = 0;
let startX = 0;
let startY = 0;
document.addEventListener('mousedown', event => {
if (event.target == box ) {
downInBox=true;
initialLeft = parseInt(box.getBoundingClientRect().left - box.parentElement.getBoundingClientRect().left);
initialTop = parseInt(box.getBoundingClientRect().top - box.parentElement.getBoundingClientRect().top);
startX = event.clientX;
startY = event.clientY;
} // end if
}); // end mousedown;
document.addEventListener('mouseup', event => {
downInBox=false;
}); // end mouseup;
document.addEventListener('mousemove', event => {
if (downInBox) {
deltaX = startX-event.clientX;
deltaY = startY-event.clientY;
box.style.left = `${initialLeft-deltaX}px`;
box.style.top = `${initialTop-deltaY}px`;
} // end if;
}); // end mousemove;
body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
background: bisque;
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 80vw;
height: 80vh;
border: 1px solid rgb(255, 122, 122);
position: relative;
overflow: hidden;
}
.square {
width: 100px;
height: 100px;
background: blueviolet;
cursor: pointer;
position: relative;
left: 0px;
top: 0px;
}
<div class="container">
<div class="square">
</div>
</div>
The mouseup listener simply resets the flag false (equivalent to you removing the class).

Is it possible to see through a div to the one below around cursor?

Hi this is a bit of an odd question, I've seen similar effects to whats I'm going for but not exactly the same not sure if what I want to do is possible.
I want to have two divs stacked with the contents of the div below revealed only in a specific area (around the cursor), is there a way to make only part of a div transparent? Or is there any other way to achieve this effect?
Instead of having the element you want to show in the background you can put it in front and only show part of it via a clip-path;
For the coordinates I use CSS variables though you could also overwrite the style directly.
// Get element from the DOM
const container = document.querySelector('.container');
// Apply event listener
container.addEventListener('mousemove', updateCoords, false);
function updateCoords(event) {
// Get X and Y coordinates
const { offsetX, offsetY } = event;
// Update coordinates
container.style.setProperty('--x', offsetX + 'px');
container.style.setProperty('--y', offsetY + 'px');
}
.container {
border: 1px solid #000;
width: 300px;
height: 300px;
}
/* Show child when hovering the container */
.container:hover .child {
display: block;
}
.child {
clip-path: ellipse(30px 30px at var(--x) var(--y));
display: none;
}
<div class="container">
<img class="child" src="//picsum.photos/300" width="300" height="300" />
</div>
You can use requestAnimationFrame to make the circle move more smoothly
// Get element from the DOM
const container = document.querySelector('.container');
// Apply event listener
container.addEventListener('mousemove', updateCoords, false);
function updateCoords(event) {
// Get X and Y coordinates
const { offsetX, offsetY } = event;
// Update coordinates
requestAnimationFrame(() => {
container.style.setProperty('--x', offsetX + 'px');
container.style.setProperty('--y', offsetY + 'px');
});
}
.container {
border: 1px solid #000;
width: 300px;
height: 300px;
}
/* Show child when hovering the container */
.container:hover .child {
display: block;
}
.child {
clip-path: ellipse(30px 30px at var(--x) var(--y));
display: none;
}
<div class="container">
<img class="child" src="//picsum.photos/300" width="300" height="300" />
</div>
Example with text
// Get element from the DOM
const container = document.querySelector('.container');
// Apply event listener
container.addEventListener('mousemove', updateCoords, false);
function updateCoords(event) {
// Get X and Y coordinates
const {offsetX, offsetY} = event;
// Update coordinates
requestAnimationFrame(() => {
container.style.setProperty('--x', offsetX + 'px');
container.style.setProperty('--y', offsetY + 'px');
});
}
.container {
min-height: 100vh;
min-width: 100vh;
overflow: hidden;
}
.container:hover .code {
display: flex;
}
.display,
.code {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
background-color: rgb(49, 49, 49);
color: rgb(240, 191, 29);
pointer-events: none;
}
.code {
clip-path: ellipse(100px 100px at var(--x) var(--y));
display: none;
background-color: rgb(3, 3, 3);
color: rgb(101, 253, 101);
}
<div class="container">
<div class="display">
<h1>Header</h1>
</div>
<div class="code">
<h3><h1>Header</h1></h3>
</div>
</div>

Problems with moving a div by cursor if I move it too fast

to specify my question I wrote an standalone example of my problem. I want to precisely move a div inside a wrapping container (only in x-direction), like a trackbar. The wrapping div should specify the space for the slider.
My script works, if I slowly move the cursor. But if I move the cursor too fast I kind of loose the slider div somewhere inside the container. Especially in the right and left corner.
How can I improve the code to have a stable solution, without the need of librarys? I know that there is a kind of simple solution with jQuery, but I would be very happy if we could find a way in plain javascript.
var x_mouse_position;
var x_offset;
var isDown = false;
var new_slider_left_position;
var container = document.getElementById("container");
var slider = document.getElementById("slider");
slider.addEventListener('mousedown', function (e) {
isDown = true;
x_offset = slider.offsetLeft - e.clientX;
}, true);
document.addEventListener('mouseup', function () {
isDown = false;
}, true);
document.addEventListener('mousemove', function (event) {
if (isDown) {
x_mouse_position = event.clientX;
new_slider_left_position = x_mouse_position + x_offset;
if (new_slider_left_position >= 0 && new_slider_left_position <= container.offsetWidth - slider.offsetWidth) {
slider.style.left = new_slider_left_position + 'px';
}
}
}, true);
html,
body {
height: 100%;
width: 100%;
}
body {
background-color: antiquewhite;
display: flex;
justify-content: center;
align-items: center;
}
#container {
position: relative;
width: 400px;
height: 30px;
background-color: cornflowerblue;
border-radius: 5px;
overflow: hidden;
}
#slider {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
border: 1px solid black;
width: 50px;
height: 100%;
border-radius: 5px;
background-color: rgba(0, 0, 0, 0.2);
cursor: move;
}
<div id="container">
<div id="slider"></div>
</div>

Container div doesn't take 100% height no matter what [duplicate]

This question already has answers here:
Position absolute but relative to parent
(5 answers)
Closed 2 years ago.
There are at least 2000 questions about this but in my particular scenario nothing I've tried worked.
My page consists of 2 nested divs (external, contents)
contents is clickable and should cover 100% of the document
there's also a bunch of absolutely positioned divs inside contents: those are draggable by a user and can be in any position.
If there are enough of these divs, scrollbar appears. And my contents is limited by the current browser window height, so when I start to scroll it's cut:
Here's a codepen: https://codepen.io/sergey-kritskiy/pen/qBbqQJv
I've tried...
setting min-height of everything one by one and all together;
adding flex on everything; float like this, float like that;
use %, vh, vmax;
When the scrollbar is made by a bunch of 'normal' divs, min-height works fine, but with these absolute guys I'm not in luck. I probably miss something obvious and I'd appreciate solutions.
Update:
There was an answer from someone that suggested to add position: relative; overflow-y: auto to contents and it worked in my case! But the answer was removed before I was able to ask why exactly that worked.
Solution 1
wrap items in another div called sidebar.
i am appending items in .sidebar instead of #content.
you can use grid in #external like i have used in css
var target = document.querySelector(".sidebar");
for (var i = 1; i <= 30; i++)
{
(function(i)
{
var div = document.createElement('div');
target.appendChild(div);
div.innerHTML = 'el ' + i;
div.className = 'item';
div.style.left = '5px';
div.style.top = 5 + ((i-1) * 105) + 'px';
div.addEventListener('click', function (evt) {
console.log("clicked el " + i);
evt.stopPropagation()
});
})(i);
}
target.addEventListener('click', function () {
console.log("content click");
});
body,
html {
width: 100%;
height: 100%;
margin-right: auto;
margin-left: auto;
vertical-align: middle;
margin: 0px;
background-color: white;
}
.item {
position: absolute;
background-color: blue;
width: 200px;
height: 100px;
border: 1px solid black;
}
#external {
background-color: #5b6f59;
display: grid;
grid-template-columns: 230px 1fr;
}
#content {
background-color: #5b6f59;
position: relative;
}
#external,
#content {
width: 100%;
height: 100%;
}
/** Added CSS **/
.sidebar {
height: 100vh;
overflow-y: auto;
position: relative;
}
<div id="external">
<div class="sidebar"></div>
<div id="content"> </div>
</div>
Solution #2
var target = document.querySelector("#content");
for (var i = 1; i <= 30; i++)
{
(function(i)
{
var div = document.createElement('div');
target.appendChild(div);
div.innerHTML = 'el ' + i;
div.className = 'item';
div.style.left = (Math.random()*100) + 'px';
div.style.top = 5 + ((i-1) * 105 + Math.random()*100 - 50) + 'px';
div.addEventListener('click', function (evt) {
console.log("clicked el " + i);
evt.stopPropagation()
});
})(i);
}
target.addEventListener('click', function () {
console.log("content click");
});
body,
html {
width: 100%;
height: 100%;
margin-right: auto;
margin-left: auto;
vertical-align: middle;
margin: 0px;
background-color: white;
}
.item {
position: absolute;
background-color: blue;
width: 200px;
height: 100px;
border: 1px solid black;
}
#external {
background-color: red;
}
#content {
background-color: #5b6f59;
height: 100vh;
overflow-y: auto;
position: relative;
}
#external {
width: 100%;
height: 100%;
}
<div id="external">
<div id="content">
</div>
</div>

Categories

Resources