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).
Related
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.
I want to find the simplest barebones (that is, no libraries if possible; this is a learning exercise) way to draw a simple line between components. The elements are divs representing cards always stacked vertically potentially forever. Cards can be different heights. The line will exit the left hand side of any given element (card a), turn 90 degrees and go up, turning 90 degrees back into another (card b).
I've tried a few things. I haven't got any fully working yet and they're looking like they all need some serious time dedicated to figuring them out. What I want to know is what's the right/preferred way to do this so that I spend time on the right thing and it's future proof with the view:
I can add as many connecting lines as I need between any two boxes, not just consecutive ones
These lines obey resizing and scrolling down and up the cards
Some cards may not have an end point and will instead terminate top left of page, waiting for their card to scroll into view or be created.
Attempts
My first thought was a <canvas> in a full column component on the left but aligning canvas' and the drawings in them to my divs was a pain, as well as having an infinite scrolling canvas. Couldn't make it work.
Next I tried <div>s. Like McBrackets has done here. Colouring the top, bottom and outer edge of the div and aligning it with the two cards in question but while I can position it relative to card a, I can't figure out how to then stop it at card b.
Lastly I tried <SVG>s. Just .getElementById() then add an SVG path that follows the instructions above. i.e.
const connectingPath =
"M " + aRect.left + " " + aRect.top + " " +
"H " + (aRect.left - 50) +
"V " + (bRect.top) +
"H " + (bRect.left);
Nothing seems to line up, it's proving pretty difficult to debug and it's looking like a much more complex solution as I need to take into account resizing and whatnot.
You might be able to apply something like this by taking a few measurements from the boxes you want to connect; offsetTop and clientHeight.
Update Added some logic for undrawn cards requirement.
While this doesn't fully simulate dynamic populating of cards, I made an update to show how to handle a scenario where only one card is drawn.
Click connect using the default values (1 and 5). This will show an open connector starting from box 1.
Click "Add box 5". This will add the missing box and update the connector.
The remaining work here is to create an event listener on scroll to check the list of connectors. From there you can check if both boxes appear or not in the DOM (see checkConnectors function). If they appear, then pass values to addConnector which will connect them fully.
class Container {
constructor(element) {
this.connectors = new Map();
this.element = element;
}
addConnector(topBox, bottomBox, displayHalf = false) {
if (!topBox && !bottomBox) throw new Error("Invalid params");
const connector = new Connector(topBox, bottomBox, displayHalf);
const connectorId = `${topBox.id}:${bottomBox.id}`;
this.element.appendChild(connector.element);
if (this.connectors.has(connectorId)) {
connector.element.style.borderColor = this.connectors.get(connectorId).element.style.borderColor;
} else {
connector.element.style.borderColor = "#" + Math.floor(Math.random() * 16777215).toString(16);
}
this.connectors.set(connectorId, connector);
}
checkConnectors() {
this.connectors.forEach((connector) => {
if (connector.displayHalf) {
connector.firstBox.updateElement();
connector.secondBox.updateElement();
if (connector.firstBox.element && connector.secondBox.element) {
this.addConnector(connector.firstBox, connector.secondBox);
}
}
});
}
}
class Box {
constructor(id) {
this.id = id;
this.updateElement();
}
getMidpoint() {
return this.element.offsetTop + this.element.clientHeight / 2;
}
updateElement() {
this.element ??= document.getElementById(`box${this.id}`);
}
static sortTopDown(firstBox, secondBox) {
return [firstBox, secondBox].sort((a,b) => a.element.offsetTop - b.element.offsetTop);
}
}
class Connector {
constructor(firstBox, secondBox, displayHalf) {
this.firstBox = firstBox;
this.secondBox = secondBox;
this.displayHalf = displayHalf;
const firstBoxHeight = this.firstBox.getMidpoint();
this.element = document.createElement("div");
this.element.classList.add("connector");
this.element.style.top = firstBoxHeight + "px";
let secondBoxHeight;
if (this.displayHalf) {
secondBoxHeight = this.firstBox.element.parentElement.clientHeight;
this.element.style.borderBottom = "unset";
} else {
secondBoxHeight = this.secondBox.getMidpoint();
}
this.element.style.height = Math.abs(secondBoxHeight - firstBoxHeight) + "px";
}
}
const connectButton = document.getElementById("connect");
const error = document.getElementById("error");
const addBoxButton = document.getElementById("addBox");
const container = new Container(document.getElementById("container"));
connectButton.addEventListener("click", () => {
const firstBoxId = document.getElementById("selectFirstBox").value;
const secondBoxId = document.getElementById("selectSecondBox").value;
if (firstBoxId === "" || secondBoxId === "") return;
error.style.display = firstBoxId === secondBoxId ? "block" : "none";
const firstBox = new Box(firstBoxId);
const secondBox = new Box(secondBoxId);
// Check for undrawn cards
if (!!firstBox.element ^ !!secondBox.element) {
return container.addConnector(firstBox, secondBox, true);
}
const [topBox, bottomBox] = Box.sortTopDown(firstBox, secondBox);
container.addConnector(topBox, bottomBox);
});
window.addEventListener("resize", () => container.checkConnectors());
addBoxButton.addEventListener("click", () => {
const box = document.createElement("div");
box.innerText = 5;
box.id = "box5";
box.classList.add("box");
container.element.appendChild(box);
addBoxButton.style.display = 'none';
container.checkConnectors();
});
.box {
border: solid 1px;
width: 60px;
margin-left: 30px;
margin-bottom: 5px;
text-align: center;
}
#inputs {
margin-top: 20px;
}
#inputs input {
width: 150px;
}
.connector {
position: absolute;
border-top: solid 1px;
border-left: solid 1px;
border-bottom: solid 1px;
width: 29px;
}
#error {
display: none;
color: red;
}
<div id="container">
<div id="box1" class="box">1</div>
<div id="box2" class="box">2</div>
<div id="box3" class="box">3</div>
<div id="box4" class="box">4</div>
</div>
<div id="inputs">
<input id="selectFirstBox" type="number" placeholder="Provide first box id" min="1" value="1" max="5" />
<input id="selectSecondBox" type="number" placeholder="Provide second box id" min="1" value="5" max="5" />
<div id="error">Please select different boxes to connect.</div>
</div>
<button id="connect">Connect</button>
<button id="addBox">Add box 5</button>
I haven't been able to make this code work. From bottom to top, it seems to work perfectly fine - that is, I drag a div element and then drop it on top of another, and it does what it is intended to do. However, when I try to swap an element with another below it, no swap occurs, and that's what's been baffling me most. Could you please shed some light on why it's not working?
let thing1 = document.getElementById("thing1");
let thing2 = document.getElementById("thing2");
let thing3 = document.getElementById("thing3");
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("id", ev.target.id);
console.log(ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var thing = document.getElementById(ev.dataTransfer.getData("id"));
var thingParent = thing.parentElement;
var tgt = ev.currentTarget;
console.log(thing)
console.log(tgt)
thingParent.replaceChild(thing, tgt);
thingParent.appendChild(tgt);
}
thing1.addEventListener("drop", drop);
thing2.addEventListener("drop", drop);
thing3.addEventListener("drop", drop);
thing1.addEventListener("dragover", allowDrop);
thing2.addEventListener("dragover", allowDrop);
thing3.addEventListener("dragover", allowDrop);
thing1.addEventListener("dragstart", drag);
thing2.addEventListener("dragstart", drag);
thing3.addEventListener("dragstart", drag);
.thing {
width: 100%;
height: ;
background: red;
}
.drag {
background: red;
height: 50px;
margin: 10px;
text-align: center;
}
#thing1 {
background: yellow;
}
#thing2 {
background: blue;
}
<div id="thing">
<div id="thing1" class="drag" draggable="true">1</div>
<div id ="thing2" class="drag" draggable="true">2</div>
<div id ="thing3" class="drag" draggable="true">3</div>
</div>
You are always appending the div to be replaced at the bottom of the page, which means you can never actually replace the div at the bottom. I added timeouts to your code to show you what is happening here. (Note that I added a removeChild call, but this happens automatically before the operation replaceChild, so I just made it explicit to enhance your ability to visualize what is happening.)
The solution isn't simple, because whether the choice to insert before or after an element is going to depend on whether you are coming from below or above. One easy solution is swap the two elements out for each other. This can be done by cloning one, as shown below:
function drop(ev) {
ev.preventDefault();
var thing = document.getElementById(ev.dataTransfer.getData("id"));
var tgt = ev.currentTarget;
if (thing && tgt) {
var thingParent = thing.parentElement;
var newTgt = tgt.cloneNode(true);
addListeners(newTgt);
thingParent.replaceChild(newTgt, thing)
thingParent.replaceChild(thing, tgt);
}
}
function addListeners(el) {
el.addEventListener("drop", drop);
el.addEventListener("dragover", allowDrop);
el.addEventListener("dragstart", drag);
}
Even if you decide you actually want a different behavior, knowing how to swap two elements should give you the tools to get whatever behavior you want.
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
})
So I have a div with a set width and height, and I want the content inside to slide automatically (without hover or interaction from the user) after like so many seconds to reveal different content. What my actual goal is, I have boxes with an audio player and the notes of the generated content, and I want it to slide up down or left right whatever, to reveal the artist blabhalbalh. You can see what I'm talking about at here.
Oh, silly me. A good example would be Windows Phone 7 live tiles, thats actually what I'm basing it off of.
var swap = false;
var w = 309;
var h = 261;
var i = 0;
var x = 0;
var q = 1;
var t;
window.onload=function(){
i = initPortfolio();
setTimeout("portfolioRotate()",2000);
}
function initPortfolio(){
$('.portfolio-item').each(function(index){i = i+1;});
for(var n=1;n<=i;n=n+1){
lpos = w * n - w;
$('#item-'+n).css('left',lpos);
}
$('.offs').css('display','block');
x = i * w - w;
return i;
}
function portfolioRotate(){
swap = false;
$('.portfolio-item').animate({left:'-='+w},6000,function(){
nn = this.id.split("-")[1];
if(q==nn && swap==false){
$('#item-'+q).css('left',x);
if(q<i){q=q+1}else{q=1};
swap = true;
u=setTimeout("portfolioRotate()",2000);
}
});
}
<div id="portfolio-container">
<div class="portfolio-item" id="item-1"></div>
<div class="portfolio-item" id="item-2"></div>
<div class="portfolio-item" id="item-3"></div>
<div class="portfolio-item offs" id="item-4"></div>
</div>
.offs{
display:none;
}
#portfolio-container{
width:927px;
height:318px;
margin-top:30px;
position:relative;
overflow:hidden;
}
.portfolio-item{
width:309;
padding-top:20px;
position:absolute;
height:100%;
}
This might be terrible, but I just pulled it from something I was working on recently. Let me know if it isn't sliding, or needs explanation. using jQuery. there is probably a way better way to position the components initially with css.