onmouseover event misbehaving - javascript

I was trying to make somewhat of a curtain type effect and applied onmouseover to a parent div id. I am not able to understand why is the effect taking place when I mouse over on the child div, other than the parent div.
var left = document.getElementById("left");
var right = document.getElementById("right");
function curtain() {
left.style.transform = "rotate(30deg)";
right.style.transform = "rotate(-30deg)";
}
function back() {
left.style.transform = "rotate(0deg)";
right.style.transform = "rotate(0deg)";
}
CSS:
#animate {
width: 400px;
margin: auto;
position: relative;
}
img {
width: 100%;
}
#left {
position: absolute;
top: 0;
right: 50%;
padding: 0;
margin: 0;
width: 50%;
transform-origin: 0 0;
}
#right {
position: absolute;
top: 0;
right: 0%;
padding: 0;
margin: 0;
width: 50%;
transform-origin: 100% 0;
}
HTML:
<div id="animate" onmouseover="curtain()" onmouseout="back()">
<div id="left">
<img src="http://www.uwphotographyguide.com/images/Articles/image-overlay-3107.jpg">
</div>
<div id="right">
<img src="http://s3.freefoto.com/images/15/78/15_78_19_web.jpg">
</div>
</div>

I think the answer is that you made the position of the childs absolute. With that they are not longer "part" of the parent div. In your case the outer div has no height because the children are put absolute on the page.
The mouseover property delegates down. So the parent listens and all it's children. Read up on this one for more information how a event listener works https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

This was happen because your outer div height was small compare to image size. so when you move mouse outside outer div back event is called. just give some minimum height to outer div. here is working code
var left = document.getElementById("left");
var right = document.getElementById("right");
function curtain() {
left.style.transform = "rotate(30deg)";
right.style.transform = "rotate(-30deg)";
}
function back() {
left.style.transform = "rotate(0deg)";
right.style.transform = "rotate(0deg)";
}
#animate {
width: 250px;
margin: auto;
position: relative;
height: 225px;
border: 1px solid;
}
img {
width: 100%;
}
#left {
position: absolute;
top: 0;
right: 50%;
padding: 0;
margin: 0;
width: 50%;
transform-origin: 0 0;
}
#right {
position: absolute;
top: 0;
right: 0%;
padding: 0;
margin: 0;
width: 50%;
transform-origin: 100% 0;
}
<div id="animate" onmouseover="curtain()" onmouseout="back()">
<div id="left">
<img src="http://www.uwphotographyguide.com/images/Articles/image-overlay-3107.jpg">
</div>
<div id="right">
<img src="http://s3.freefoto.com/images/15/78/15_78_19_web.jpg">
</div>
</div>

As per my understanding, I think you want that if you mouse over on child div no effects will be on child div, It should happens only when you mouse over on parent div.
This is happening because child div is inside the scope of parent div.
So you have to manage these div. Adjust the height of parent div and it will works fine.

Related

Safari selects wrong drag element when elements use transforms instead of left and top

When I have 3 draggable divs using position: absolute and positioning them using left and top the items are correctly draggable on Safari (and other browsers).
But when I set left and top to 0 and instead position them using transform.translateX and transform.translateY and try to drag them, Safari always selects the first item regardless of the mouse position.
The HTML code below shows this problem. Initially the items are positioned using left and top and you can drag any of the divs. When you click the button it moves them all to 0 (left and top) and repositions them using transform.translateX and transform.translateY so visually it looks the same, but when you try to drag them things get strange. It appears it's always selecting the topmost item at the origin (regardless of the cursor position) and it clips some of the content.
Is there a workaround for this, or can items not be dragged on Safari when they have transformations applied?
document.querySelector("button").addEventListener("click", (e) => {
document.querySelectorAll("div[draggable").forEach(elem => {
const style = getComputedStyle(elem);
const left = style.getPropertyValue("left");
const top = style.getPropertyValue("top");
elem.style.left = 0;
elem.style.top = 0;
elem.style.transform = `translateX(${left}) translateY(${top})`;
});
document.querySelector("p:nth-of-type(1)").style.display = "none";
document.querySelector("p:nth-of-type(2)").style.display = "";
e.target.style.display = "none";
});
body {
background-color: #444;
width: 720px;
height: 720px;
}
div[draggable] {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
color: #000;
border-radius: 16px;
width: 200px;
height: 200px;
}
div[draggable]:nth-child(1) {
background-color: #f80;
left: 20px;
top: 50px;
}
div[draggable]:nth-child(2) {
background-color: #8f0;
left: 240px;
top: 50px;
}
div[draggable]:nth-child(3) {
background-color: #08f;
left: 460px;
top: 50px;
}
button {
position: absolute;
left: 20px;
top: 10px;
padding: 4px 12px;
}
p {
color: white;
position: absolute;
left: 200px;
top: 0;
}
<div draggable="true">Item 1</div>
<div draggable="true">Item 2</div>
<div draggable="true">Item 3</div>
<p>Items use 'position: absolute' and are positioned using 'left' and 'top' properties.</p>
<p style="display: none">Items use 'position: absolute' and are positioned using 'transform.translateX' and 'transform.translateY' properties.</p>
<button>Position -> Transform</button>

Is there a way to apply CSS-transform to an element which is inside an element that is being CSS-transformed on mousemove?

Basically I have a parallax scene using parallax.js library.
Inside the scene I have a couple of divs with unique parallax settings data tags.
And inside one of these divs I have an element which I want apply tilt effect to(when its getting mouseover'ed). But it doesnt work, the transformations from tilt lib arent being applied if an element is inside the scene however it works if I move it out of the parallax scene.
I think the problem lies somewhere around the management of OnMouseMove events or maybe it cannot work that way(when transform is being applied to an already transformed element's child).
Chrome EventListeners tab shows that both parallax and tilt mousemove listeners exist.
I would appreciate any help. If you need any code snippets I can provide it, since right now I actually don't know what particular parts to show and dont want to copy paste the whole libs.
UPD.
here's a snippet of what im trying to do:
$(document).ready(function() {
var scene = $('.prlx-scene').get(0);
var parallaxInstance = new Parallax(scene, {
relativeInput: true,
invertX: false,
invertY: false
});
});
.fulld,
.prlx-scene {
position: relative;
height: 100%
}
.prlx-scene {
width: 80%;
margin-right: auto;
margin-left: auto
}
.fulld {
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 12;
display: block;
width: 100%;
background-color: #000fff;
background-position: 50% 50%;
background-size: cover
}
.platonic-left-front-img {
position: absolute;
display: block;
}
.platonic-left-front {
z-index: 40;
}
.platonic-left-front-img {
left: 20%;
max-width: 100%;
max-height: 100%;
width: 50%;
top: 40%
}
.pc-text1 {
top: 50%;
left: 10%;
display: block;
position: fixed;
width: 15%;
height: 15%;
background-color: #00ffff;
}
.pc-text {
top: 50%;
left: 30%;
display: block;
position: fixed;
width: 15%;
height: 15%;
background-color: #00ffff;
}
img {
max-width: 100%;
vertical-align: middle
}
.scene-block {
width: 100%;
top: 0;
left: 0;
right: 0;
height: 100%;
bottom: 0;
margin-top: 0
}
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/parallax/3.1.0/parallax.min.js"></script>
</head>
<body style="height:100%;position:absolute;width:100%;">
<div class="pc-text1" data-tilt data-tilt-max="40" data-tilt-speed="200" data-tilt-perspective="500" data-tilt-reverse="true" style="z-index:9999;transform-style: preserve-3d;">
<p style="transform: translateZ(50px);">TEXT</p>
</div>
<div class="fulld">
<div class="prlx-scene">
<div class="scene-block" data-depth="0.8"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" class="platonic-left-front-img"></div>
<div class="scene-block" data-depth="0.85">
<div class="pc-text" data-tilt data-tilt-max="90" data-tilt-speed="400" data-tilt-perspective="500" data-tilt-reverse="true" style="transform-style: preserve-3d;">
<p style="transform: translateZ(50px);">TEXT</p>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vanilla-tilt#1.6.1/lib/vanilla-tilt.min.js"></script>
</body>
Found out that parallax scene disables pointer events.
So in order for that to work I needed to add style="pointer-events: all;" to an element that is being tilted.

How do I make div disappear on click and show what is under it

I have a div with class hide. Its essentially a div with a black background. This div is suppose to hide the food div. So, when you click on the black background, aka if you click on the hide div, it is suppose to make all the hide div's disappear and show the food div. However, my issue is that this is not happening. The hide div is not showing up. Below is my current code:
function make_disappear(){
document.getElementByClassName('hide').style.display = 'none';
}
.main {
position: relative;
display: inline-block;
width: 120px;
height: 120px
margin: 5px;
background: red;
}
.hide {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background: url(https://i.imgur.com/Y8B7LsB.jpg);
}
.food {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
}
<div class="main">
<div onclick="make_disappear()" class="hide"></div>
<div class="food"><img alt="Food!!" src="https://i.imgur.com/1lbkAaY.jpg"></div>
</div>
<div class="main">
<div onclick="make_disappear()" class="hide"></div>
<div class="food"><img alt="Food!!" src="https://i.imgur.com/1lbkAaY.jpg"></div>
</div>
I like using a delegate pattern when animating/transitioning a bunch of elements of the same type or class. After adding z-index: 1 to the .hide elements to get them "on top" of the images in the stacking order, I add a class of disappear to the body when clicking any of the .hide elements. The CSS handles the rest, fading out all the .hide elements at once. This approach could be used on a wrapper element as well, if body feels too high up in the DOM.
const hide = document.querySelectorAll('.hide');
function handleClick() {
document.body.classList.add('disappear');
}
hide.forEach(el => {
el.addEventListener('click', handleClick);
});
.main {
position: relative;
display: inline-block;
width: 120px;
height: 120px margin: 5px;
background: red;
}
.hide {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background: url(https://i.imgur.com/Y8B7LsB.jpg);
z-index: 1;
transition: .5s opacity;
}
.disappear .hide {
opacity: 0;
}
.food {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
}
<div class="main">
<div class="hide"></div>
<div class="food"><img alt="Food!!" src="https://i.imgur.com/1lbkAaY.jpg"></div>
</div>
<div class="main">
<div class="hide"></div>
<div class="food"><img alt="Food!!" src="https://i.imgur.com/1lbkAaY.jpg"></div>
</div>
https://jsfiddle.net/2rvte9y3/1/
Your problem is just how the divs are being rendered, the hide divs are being rendered first than the food divs over top of them since they're both have style of position: absolute. Just rearrange their html like this:
<div class="main">
<div class="food"><img alt="Food!!" src="https://i.imgur.com/1lbkAaY.jpg"></div>
<div onclick="make_disappear()" class="hide"></div>
</div>
Or change the z-index of the hide divs to be higher than those of the food divs:
.hide {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background: url(https://i.imgur.com/Y8B7LsB.jpg);
z-index: 100; /* here */
}
Your JS also won't work, because, as #wentjun has pointed out, document.getElementsByClassName returns a node list. You just need to loop through the list to change each display style like so:
function make_disappear(){
var hideDivs = document.getElementsByClassName('hide')
for (var i = 0 ; i < hideDivs.length; i++) {
hideDivs[i].style.display = "none"
}
}
The getElementsByClassName() returns a NodeList collection of all elements in the document with the specified class name, and in your case, .hide.
Doing this document.getElementByClassName('hide').style without specifying the index will not work as it is a list.
You will need to iterate through the NodeList, or specifically select the index in the nodelist to target it
document.getElementsByClassName('hide')[0].style.display = 'none';
To hide all elements in the NodeList, you may do this:
[...document.getElementByClassName('hide')].map(node => node.style.display = 'none')
One solution is to add a greater z-index to hide elements. Also, note that getElementsByClassName() will return a HTMLCollection, not just one element as you was expecting. So you will have to loop over the collection to apply tthe style to each element.
function make_disappear()
{
var elems = document.getElementsByClassName('hide');
for (let e of elems)
{
e.style.display = "none";
}
}
.main {
position: relative;
display: inline-block;
width: 120px;
height: 120px
margin: 5px;
background: red;
}
.hide {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
z-index: 99;
background: url(https://i.imgur.com/Y8B7LsB.jpg);
}
.food {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
}
<div class="main">
<div onclick="make_disappear()" class="hide"></div>
<div class="food"><img alt="Food!!" src="https://i.imgur.com/1lbkAaY.jpg"></div>
</div>
<div class="main">
<div onclick="make_disappear()" class="hide"></div>
<div class="food"><img alt="Food!!" src="https://i.imgur.com/1lbkAaY.jpg"></div>
</div>

Change position top/left of children independently of the parent scrollTop position

I have several divs positioned absolutely within its parent.
The parent overflows the screen and can be scrolled.
On click those divs are supposed to be positioned occupying the height of the screen (as if they had position fixed). So each div is assigned a certain width and height and a top value. The problem is this only works as expected if the parent is not scrolled (scrollTop = 0).
I want this to be done smoothly with CSS transitions. I could assign a top value related to the scroll position of the parent in the click moment. But I am looking for a CSS way to do this. I thought of changing position fixed to the divs but this doesn't transition.
Is there any way I could make it work using CSS?
Edit: I am asking if anyone has some suggestion on how to achieve this using CSS, or some thought on how to approach it differently.
Edit2: This GIF includes just the position change (the width or height is no changing) as it is where I am having the issue. This is the desired solution:
JSFiddle.
var $container = $('#container');
var $elements = $container.find('.element');
$container
.height(function() {
return ($elements.eq(-1).position().top - $elements.eq(0).position().top + $elements.eq(0).outerHeight());
})
.on('click', function() {
$elements.add($container).toggleClass('on');
});
#container {
width: 100%;
background: grey;
position: absolute;
}
.element {
border: 1px solid black;
position: absolute;
width: 100px;
height: 100px;
background: white;
left: 100px;
transition: all 1s ease;
}
.element:nth-child(2) {
top: 130px;
}
.element:nth-child(3) {
top: 340px;
}
.element:nth-child(4) {
top: 550px;
}
.element:nth-child(5) {
top: 660px;
}
.on.element {
left: 0;
height: 20vh;
width: 20vh;
}
.on.element:nth-child(2) {
top: 20vh;
}
.on.element:nth-child(3) {
top: 40vh;
}
.on.element:nth-child(4) {
top: 60vh;
}
.on.element:nth-child(5) {
top: 80vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
</div>
As you've already mentioned changing position doesn't transition. This is because position is a non-animatable property, i.e. animations/transitions don't work for it and so changing position from absolute to relative or vice-versa will appear jumpy. Check here for a list of animatable properties.
You may also remove the position: absolute on .element from the beginning.
Either way, here's what you can do:
On clicking div, add class on on #container only and modify css with position: relative and top: 0 and remove the extra rules for top positions
Add a padding of scrollY on #container, so that Div#1 comes into current viewport if page is scrolled.
Checkout the below fiddles:
Using relative from beginning:
https://jsfiddle.net/4utdxr0t/2/
Switching b/w relative and position:
https://jsfiddle.net/4utdxr0t/1/
Adding a margin-top value equivalent to the scrollTop position, and transitioning it with the same duration and ease as the children did the trick:
JSFiddle
var $container = $('#container');
var $elements = $container.find('.element');
$container
.height(function() {
return ($elements.eq(-1).position().top - $elements.eq(0).position().top + $elements.eq(0).outerHeight());
})
.on('click', function() {
$elements.add($container).toggleClass('on');
$container.css('margin-top', $('body').scrollTop());
});
#container {
width: 100%;
background: grey;
position: absolute;
transition: all 1s ease;
}
.element {
border: 1px solid black;
position: absolute;
width: 100px;
height: 100px;
background: white;
left: 100px;
transition: all 1s ease;
}
.element:nth-child(2) {
top: 130px;
}
.element:nth-child(3) {
top: 340px;
}
.element:nth-child(4) {
top: 550px;
}
.element:nth-child(5) {
top: 660px;
}
.on.element {
left: 0;
height: 20vh;
width: 20vh;
}
.on.element:nth-child(2) {
top: 20vh;
}
.on.element:nth-child(3) {
top: 40vh;
}
.on.element:nth-child(4) {
top: 60vh;
}
.on.element:nth-child(5) {
top: 80vh;
}
.on.element:nth-child(6) {
top: 25vh;
}
body {
margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div id="container">
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
</div>

Move/Animate Div with Javascript

I want to move a div using javascript.
For example, how would I move a div to the left 50px using javascript?
I want the function to start when after I click on a link. Anyone know how this could be done?
Here is something that works:
HTML:
<div class="container">
<span id="box"></span>
</div>
Move test element.
CSS:
.container {
position: relative;
width: 100%;
height: 50px;
padding: 10px;
background-color: beige;
margin-bottom: 20px;
}
#box {
position: absolute;
top: 10px;
left: 100px;
height: 30px;
width: 30px;
background-color: blue;
}
Javascript:Please put this within the document ready function.
$("#move-test").on('click', function (e) {
e.preventDefault();
moveBox();
});
var moveBox = function(){
var $box = $("#box");
var left = $box.position().left;
left = left - 20;
$box.css('left', left + 'px');
};
Here is a jsfiddle with it moving continuously:
https://jsfiddle.net/5ncb6szg/
You should use position: relative as negative margins are frowned upon. Set the default to left: -100% which means that the element should be moved left the same amount as it is wide. For the javascript we assign a click handler to the link in the script instead of inline. Assign the variable open=false because it is closed on first load, then when we open it assign the variable to open=true. This gives state to the element without querying the dom.
var hidden = true;
var box = document.getElementById("box");
document.getElementById('toggle').onclick = function() {
if(hidden) hidden = false, box.style.left = 0;
else hidden = true, box.style.left = '-100%';
}
#box {
background: red;
height: 50px;
width: 50px;
position: relative;
left: -100%;
}
<a href="javascript:void(0)" id="toggle">
<h3 id="contact">Contact</h3>
</a>
<div id="box"></div>

Categories