how do i trigger onMouseEnter for elements behind other elements - javascript

I'm trying to trigger mouseEnter event when mouse is on top of multiple elements.
I want both mouseEnter events to trigger when the mouse is at the center, and preferably for both to turn yellow.
Run the code snippet below for an example:
<!DOCTYPE html>
<html>
<head>
<style>
div:hover {
background-color: yellow;
}
div {
width: 100px;
height:100px;
background:green;
border: 2px solid black;
}
.second {
transform:translateX(50%) translateY(-50%);
}
</style>
<script>
function onhover(){console.log('hovered')}
</script>
</head>
<body>
<div onmouseenter=onhover()></div>
<div onmouseenter=onhover() class='second'></div>
</body>
</html>

According to MDN, the mouseenter event does not bubble, whereas the mouseover event does. However, even if it DID bubble, your elements currently have no relation to one another, thus the mouse events are captured by the upper element.
One possible way around this is with the amazing elementsFromPoint function in JavaScript, which makes quick work of solving your issue:
// Only the IDs of the elments you are interested in
const elems = ["1", "2"];
// Modified from https://stackoverflow.com/a/71268477/6456163
window.onload = function() {
this.addEventListener("mousemove", checkMousePosition);
};
function checkMousePosition(e) {
// All the elements the mouse is currently overlapping with
const _overlapped = document.elementsFromPoint(e.pageX, e.pageY);
// Check to see if any element id matches an id in elems
const _included = _overlapped.filter((el) => elems.includes(el.id));
const ids = _included.map((el) => el.id);
for (const index in elems) {
const id = elems[index];
const elem = document.getElementById(id);
if (ids.includes(id)) {
elem.style.background = "yellow";
} else {
elem.style.background = "green";
}
}
}
div {
width: 100px;
height: 100px;
background: green;
border: 2px solid black;
}
.second {
transform: translateX(50%) translateY(-50%);
}
<div id="1"></div>
<div id="2" class="second"></div>

I think that you can not without javascript, and with it it's a bit tricky, you have to check on every mousemove if the coordinates of the mouse are in de bounding box of the element, this fill fail with elements with border radius but for the others it's ok
<script>
var hovered=[]
function addHover(element){hovered.push(element)}
function onhover(element){console.log("hovered",element)}
function onCustomHover(e){
hovered.forEach((el,i)=>{
let bounds=el.getBoundingClientRect()
if (e.pageX > bounds.left && e.pageX < bounds.bottom &&
e.pageY > bounds.top && e.pageY < bounds.right ) {
onhover(i);
}
})
}
</script>
</head>
<body>
<div id="div1"></div>
<div id="div2" class='second'></div>
<script>
document.body.addEventListener('mousemove', onCustomHover, true);//{capture :false});
addHover(document.getElementById("div1"))
addHover(document.getElementById("div2"));
</script>
I would appreciate if you could rate the answer if that was usefull to you because I can not make comments yet <3

It will be easier to change your code a little bit.
ex. Add to your div elements class box.
Add to your styles class with name hovered which will look like:
.hovered {
background-color: yellow;
}
Into JS(between script tag) add event listeners (code not tested, but idea is shown), also move script to place before closing body tag:
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
box.addEventListener('mouseover', () => {
boxes.forEach(b => b.classList.add('hovered'));
});
box.addEventListener('mouseout', () => {
boxes.forEach(b => b.classList.remove('hovered'));
});
});

The problem is that elements are blocking the mouse such that elements in the background do not receive the event. With the exception that events bubble to the parent.
Given that you could change your markup slightly to get this effect.
First add a class to your boxes so we can easily find them in JavaScript:
<div class="box"></div>
<div class="box second"></div>
Then adapt the CSS such that this background change is toggled with a class instead:
.box.hovered {
background-color: yellow;
}
And then the JavaScript:
// Get all box elements
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
// For each box attach a listener to when the mouse moves
box.addEventListener('mousemove', (ev) => {
// Get the position of the mouse
const { x, y } = ev;
boxes.forEach(b => {
// for each box get it's dimension and location
const rect = b.getBoundingClientRect();
// check if the pointed is in the box
const flag = x > rect.left && x < rect.right && y > rect.top && y < rect.bottom;
// toggle the class
b.classList.toggle('hovered', flag);
});
});
});
This can be improved a lot, especially if you have more boxes by getting the rectangles beforehand and then using the index in the forEach to link the box to it's rectangle:
const boxes = document.querySelectorAll('.box');
const rects = [...boxes].map(box => box.getBoundingClientRect());
Another improvement is to use the fact that events bubble to the parent, that means you could wrap all boxes in one parent and only add a listener to this parent.

Related

How to wait for the end of the element transform property?

In this example, after the button is clicked, I first want to transform the box and then show an alert. But here, it first show the alert and then do the transform
const clickme = document.getElementById('clickme');
const box = document.getElementById('box');
clickme.addEventListener("click", () => {
box.style.transform = `rotate(90deg)`;
const value = parseFloat(box.style.transform.split('rotate(')[1]);
if(value >= 90) {
alert('Hello Rotate')
}
})
#box {
width: 100px;
height: 100px;
background: tomato;
transition: .200s ease;
}
<div id="box">
</div>
<button id="clickme">
rotate
</button>
That's the limit for transform for you.
To achieve best performance, a reliable result and actually use the right tools for the right purpose i'd suggest learning CSS animations with keyframes.
It's not that hard and it will allow you to unload some of the tasks from the main thread to the gpu(better SEO and performance).
Else there are plenty of libraries that you could use:
For example: https://animejs.com/
In your particular case, the problem is not of the transform property or not having keyframes as Filas' answer pointed out, but instead the problem lies in the nature of Javascript.
const clickme = document.getElementById('clickme');
const box = document.getElementById('box');
clickme.addEventListener("click", () => {
box.style.transform = `rotate(90deg)`;
const value = parseFloat(box.style.transform.split('rotate(')[1]);
if(value >= 90) {
alert('Hello Rotate')
}
})
The browser pauses the rendering of the HTML, which is responsible for box.style.transform = 'rotate(90deg)';, and instead prioritizes showing the alert(). Here's a thread that talks about it more.
I assume that in your actual code, you would be using some other function, instead of the alert(). In that case, your code should run as you intended, with the transform occurring first, followed by the subsequent commands.
const clickme = document.getElementById('clickme');
const box = document.getElementById('box');
clickme.addEventListener("click", () => {
box.style.transform = 'rotate(90deg)';
const value = parseFloat(box.style.transform.split('rotate(')[1]);
if(value >= 90) {
box.style.backgroundColor="green"; //code that isn't an alert()
}
})
#box {
width: 100px;
height: 100px;
background: tomato;
transition: .200s ease;
}
<div id="box">
</div>
<button id="clickme">
rotate
</button>
Combine this with a transitionend event, or a setTimeout() function, if you want to sequence the changes in a way that the individual changes/steps are clearly visible to the user.

Let link follow clippath

I am trying to figure out how to display a clickable link only inside the area of the existing clip-path. And also utilize the existing OffsetX value.
<style>
.mouse {
background-color: aqua;
}
.img {
background-color: black;
}
</style>
<div class="wrap">
<div class="img"></div>
<div class="mouse">
<p id="anchor"></p>
</div>
</div>
<script>
let main = document.querySelector('.wrap');
let mouse = document.querySelector('.mouse');
let text = "Text link";
let result = text.link("www.stackoverflow.com");
document.getElementById("anchor").innerHTML = result;
main.addEventListener('mousemove', (e) => {
mouse.style.clipPath = `circle(15em at ${e.offsetX}px`;
});
</script>
If I understand you correctly, you want the link to move along with the clip path.
I would do it by so:
mouse.style.clipPath = `circle(5em at ${(e.clientX - main.getBoundingClientRect().x)}px`;
document.getElementById("anchor").style = "margin-left: " + (e.clientX - main.getBoundingClientRect().x) + "px";
This does not utilize the offsetX, but as you move the link, the offsetX would also move along (so it would stay the same), unless you disable pointer events for the link (which might not be intented).

How can I detect the scrollTop of an element using Vanilla Javascript?

I want to add a class to an element when the user scrolls more than 100px from the top of the element but I seem to be triggering this as soon as the page loads. This is the code that I have at the moment
const content = document.getElementById("content");
document.addEventListener("scroll", () => {
content.classList.add(
'curtain-in',
content.scrollTop > 100
);
});
Also with your answer can you please explain where I've gone wrong.
Thank you in advance
Maybe what is happening is that content.scrollTop is always returning 0 and your condition is never fulfilled. I've struggled myself with that problem trying to make a fiddle to test your case.
To check if the scroll has passed the beginning of the element plus 100px we need to know where the element starts and the new position of the scroll, we can get both values like this:
var position = content.offsetTop;
var scrolled = document.scrollingElement.scrollTop;
With these, you can do something like this in your event function:
const content = document.getElementById("content");
document.addEventListener("scroll", (e) => {
var scrolled = document.scrollingElement.scrollTop;
var position = content.offsetTop;
if(scrolled > position + 100){
content.classList.add(
'curtain-in');
}
});
Here's the fiddle: https://jsfiddle.net/cvmw3L1o/1/
I want to add a class to an element when the user scrolls more than
100px from the top of the element
You should add addEventListener to content not document
const content = document.getElementById("content");
content.addEventListener("scroll", () => {
console.log('class added');
content.classList.add(
'curtain-in',
content.scrollTop >= 100
);
});
#content {
height: 200px;
width: 200px;
overflow: scroll;
}
p {
height: 1000px;
}
<div id="content">
<p></p>
</div>

How can I find current element on mouseover using jQuery?

How can I get the class name of the current element that is on mouseover? For example
When a mouse is over from div to a, I want to get the class name of a div element. How can I get it using jQuery?
you can give a try to this:
window.onmouseover=function(e) {
console.log(e.target.className);
};
This is my version:
function handler(ev) {
var target = $(ev.target);
var elId = target.attr('id');
if( target.is(".el") ) {
alert('The mouse was over'+ elId );
}
}
$(".el").mouseleave(handler);
Working fiddle: http://jsfiddle.net/roXon/dJgf4/
function handler(ev) {
var target = $(ev.target);
var elId = target.attr('id');
if( target.is(".el") ) {
alert('The mouse was over'+ elId );
}
}
$(".el").mouseleave(handler);
.el{
width:200px;
height:200px;
margin:1px;
position:relative;
background:#ccc;
float:left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Hover an element and refresh the page, than move your mouse away.</p>
<div id="element1" class="el"></div>
<div id="element2" class="el"></div>
<div id="element3" class="el"></div>
<div id="element4" class="el"></div>
<div id="element5" class="el"></div>
<div id="element6" class="el"></div>
<div id="element7" class="el"></div>
<div id="element8" class="el"></div>
<div id="element9" class="el"></div>
Do you want the class name of the div on which the mouseover event occurs?
If that is the case then refer this,
HTML
<div class="a">aaaaaaaa</div>
<div class="b">bbbbbbbbb</div>
jQuery
$(document).on('mouseover', 'div', function(e) {
console.log($(e.target).attr('class'));
});
jsFiddle
I have used mouseover event with target
e.target gives the element on which that event occurs
If you want to get the class name of div after leaving the mouse from it
then use "mouseleave" event instaed of "mouseover"
What most people have neglected is this request from the OP:
When mouse over div from a
Meaning you need to know you've hovered from a specific type of element, not just from any element.
I made a global var, changing to true on the mouseleave of specific elements, in your case an a element. Then, inside the hover function you need to check that it's true.
Here's a Demo
Edit: Updated fiddle demo with edge cases when hovering from a element not directly onto the div.
Get the position of element on mouseover and then get the class name
<div id="wrapper">
A<div class="divClass">DIV</div>
</div>
$('#wrapper').mouseover(function(e) {
var x = e.clientX, y = e.clientY,
elementOnMouseOver = document.elementFromPoint(x, y);
elementClass=$(elementOnMouseOver).attr('class');
alert(elementClass);
});
JSFiddle: http://jsfiddle.net/ankur1990/kUyE7/
If you don't want to apply this only on wrapper div but on whole window/document, then you can replace wrapper with window/document
$(window).mouseover(function(e){});
All depending on how you want it. This could also be an option:
»Fiddle 1«
With some more detail. This will only show as true after taking the direct path from a to div. (The tiny white space between a and div.) As in:
a -> div TRUE
a -> div -> white space in between -> div FALSE
»Fiddle 2«
Might hold up. This will also show as true if one go to the tiny white space between a and div, and then go back to div. As in:
a -> div -> white space in between -> div TRUE
var mode = 0;
$(window).on("mousemove", function(e) {
if (e.target.className === "d1") {
mode = 1;
} else {
var cc = e.target.className;
if (cc !== "d2" && mode) {
var el = $(".d1"),
d1 = {
x : el.offset().left,
y : el.offset().top,
w : el.width(),
h : el.height()
},
c = {
x : e.pageX,
y : e.pageY
};
if (c.x >= d1.x + d1.w && c.y >= d1.y && c.y <= d1.y + d1.h)
mode = 2;
else
mode = 0;
} else if (cc === "d2" && mode) {
mode = 3;
}
}
$("#status").html("Current: " + (mode == 3 ? "OVER" : "NOT OVER") + " from a" );
});
From jQuery API
<div class="className">
<span class="span">move your mouse</span>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript">
$(".className").mouseover(function() {
var n = $(this).attr("class");
$(".span").html("");
$(".span").html("The class :"+n);
});
</script>
this should work:
define a class in your style sheet:
.detectable-div{
border: white solid 1px;
}
.detectable-div:hover{
border: red solid 1px;
}
then in your js:
$('div.detectable-div:hover').mouseover(function () {
$(this) // this is your object
})

How to check if the cursor is over an element? [duplicate]

This question already has answers here:
pure javascript to check if something has hover (without setting on mouseover/out)
(5 answers)
Closed 3 years ago.
How can I check whether the cursor is over a div on the html page with JQuery/Javascript?
I'm trying to get cursor coordinates to see if they are in the rectangle of my element. Maybe there are predefined methods?
UPD, don't say anything about hover events, etc. I need some method which will return true/false for some element at the page, like:
var result = underElement('#someDiv'); // true/false
I'm not really sure why you wish to avoid hover so badly: consider the following script
$(function(){
$('*').hover(function(){
$(this).data('hover',1); //store in that element that the mouse is over it
},
function(){
$(this).data('hover',0); //store in that element that the mouse is no longer over it
});
window.isHovering = function (selector) {
return $(selector).data('hover')?true:false; //check element for hover property
}
});
Basically the idea is that you use hover to set a flag on the element that the mouse is over it/no longer over it. And then you write a function that checks for that flag.
For the sake of completeness I will add a couple of changes that I believe will help a bit for performance.
Use delegation to bind the event to one element, instead of binding it to all existent elements.
$(document).on({
mouseenter: function(evt) {
$(evt.target).data('hovering', true);
},
mouseleave: function(evt) {
$(evt.target).data('hovering', false);
}
}, "*");
Add a jQuery pseudo-expression :hovering.
jQuery.expr[":"].hovering = function(elem) {
return $(elem).data('hovering') ? true : false;
};
Usage:
var isHovering = $('#someDiv').is(":hovering");
The simplest way would probably be to just track which element the mouse is over at all times. Try something like:
<div id="1" style="border:solid 1px red; width:50px; height:50px;"></div>
<div id="2" style="border:solid 1px blue; width:50px; height:50px;"></div>
<div id="3" style="border:solid 1px green; width:50px; height:50px;"></div>
<input type="hidden" id="mouseTracker" />
​$(document).ready(function() {
$('*').hover(function() {
$('#mouseTracker').val(this.id);
});
});
and then your function is simply
function mouseIsOverElement(elemId) {
return elemId === $('#mouseTracker').val();
}
Can't you just check $(select).is(':hover') ?
I did this with custom function:
$(document).mouseup(function(e) {
if(UnderElement("#myelement",e)) {
alert("click inside element");
}
});
function UnderElement(elem,e) {
var elemWidth = $(elem).width();
var elemHeight = $(elem).height();
var elemPosition = $(elem).offset();
var elemPosition2 = new Object;
elemPosition2.top = elemPosition.top + elemHeight;
elemPosition2.left = elemPosition.left + elemWidth;
return ((e.pageX > elemPosition.left && e.pageX < elemPosition2.left) && (e.pageY > elemPosition.top && e.pageY < elemPosition2.top))
}

Categories