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

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>

Related

How can I zoom in on a div element on scroll using javascript?

What I'm trying to achieve here is that when I scroll on a particular div here .ball, it should scale up to 1.5.
but when I'm not scrolling on that ball div it should shrink down to it's original height and width.
Here I'm using window method to do this trick and as soon as I scroll ball scale up which isn't what I'm trying to do. What can I use instead of window method and is there any other approach to do achieve this?
const ball = document.querySelector('.ball');
window.addEventListener('scroll', ()=> {
if (scroll) {
ball.classList.add('active');
} else {
ball.classList.remove('active');
}
});
.ball {
height: 200px;
width: 200px;
border-radius: 100%;
background-color: orange;
}
.ball.active {
transform: scale(1.5);
position: fixed;
}
body {
height: 150vh;
}
<div class="ball"></div>
I would use a setTimeout function to remove the class after a short period after the scroll. Do not forget to clear the timeout otherwise it will lead to weird behaviour. (as suggested by Lakshya when I was answering to the question).
To make the ball smoothly transition, I would add a css transition as shown bellow.
const ball = document.querySelector('.ball');
const container = document.querySelector('.container')
let scrollTimeout;
container.addEventListener('scroll', ()=> {
ball.classList.add('active');
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(()=> ball.classList.remove('active'), 100);
});
.ball {
height: 200px;
width: 200px;
border-radius: 100%;
background-color: orange;
transition: transform 0.3s ease;
position: fixed;
top: 50px;
left: 50px;
}
.ball.active {
transform: scale(1.5);
}
.container{
width: 100%;
background: red;
overflow: scroll;
height: 500px;
}
.inside_container{
position: relative;
width: 100%;
height: 2000px;
}
<div class="container">
<div class="inside_container">
<div class="ball"></div>
</div>
</div>
One of the approaches could be delaying the removal of .active class on ball by 200ms such that each time you try to scroll again, the timer is cleared and a new one starts to do the same. A debounce approach in a nutshell.
const ball = document.querySelector('.ball');
let scrollTimeout;
window.addEventListener('scroll', ()=> {
ball.classList.add('active');
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(()=> ball.classList.remove('active'),200);
});
.ball {
height: 200px;
width: 200px;
border-radius: 100%;
background-color: orange;
}
.ball.active {
transform: scale(1.5);
position: fixed;
}
body {
height: 150vh;
}
<div class="ball"></div>

Fade a Pseudo Element on scroll - JS or jQuery

I have a pseudo element I'd like to fade to opacity:0 on scroll. I can't seem to make head nor tale of how to do it. I've set up a codepen here. http://codepen.io/emilychews/pen/JWyaKr
Normally I'd use Greensock, but I can't on this project. I also have to use a pseudo element, not an absolutely positioned div. The fade needs to happen after 10px scroll from the top and then come back when the user scrolls back to the top (its part of a nav element)
HTML
<div id="mydiv">My Div</div>
CSS
#mydiv {
background: red;
width: 10%;
}
#mydiv:after {
content: '';
position: absolute;
height: 10%;
width: 10%;
top: 30px;
background: black;
}
Any ideas would be awesome. I feel as though I'm either about to cry or eat a bucket of fried chicken in frustration.
Emily
Set a transition for opacity on the pseudo element, and add a class to the main element on scroll that you use in the selector to change opacity on your pseudo element.
var $mydiv = $('#mydiv');
$(window).scroll(function() {
if ($(this).scrollTop() > 10) {
$mydiv.addClass('fade');
} else {
$mydiv.removeClass('fade');
}
})
body {
height: 200vh;
}
#mydiv {
background: red;
width: 10%;
}
#mydiv:after {
content: '';
position: absolute;
height: 10%;
width: 10%;
top: 30px;
background: black;
transition: opacity .25s;
}
#mydiv.fade:after {
opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mydiv">My Div</div>

onmouseover event misbehaving

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.

Make a child of a div's position fixed to the top of the div on scroll and also animate height of child

I have div called box with a header div as a child. when the div scrolls I want that header div to be sticked(fixed) to the top so the users can always see the header. when the user scrolls down,the header's height should decrease to take up less space but to still allow users to see the header content. When the user scrolls back up the header should be unstuck and the height should be like the way it was before any scrolling took place when it was at the top.
I don't like my attempt I'm about to show you because I get the offset of the box relative to the document. I feel like that might not be necessary because there might be a css solution. this one is for the scroll of the whole document. That demo shows using position fixed and it fixes to the top of the page. I cant use position fixed because I believe that is meant for the window and not for divs so what the next best thing?
I dont like how the header's width is over the scrollbars. and the animation is jumpy and it doesn't work.
$(function(){
var btop = $(".box").offset().top;
bwidth = $(".box").innerWidth();
$(".box").on("scroll", function(e){
if($(this).scrollTop() > 50){
$(this).find(".header").css({
"position" : "absolute",
"top" : btop,
"max-width" : bwidth,
}).animate({
"height" :"2em"
})
}else{
$(this).find(".header").css({
"position" : "static",
"top" : btop,
"max-width" : bwidth
}).animate({
"height" :"3em"
})
}
})
})
.box{
margin: 4em auto;
height: 12em;
width: 20em;
background: blue;
overflow-y: scroll;
/*overflow-x: hidden;*/
/*position: relative;*/
}
.header{
background: orange;
width: 100%;
height: 3em;
font-size: 1.3em;
}
.content{
width: 90%;
margin: 0 auto;
background: powderblue;
height: 12em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="box">
<div class="header">This will stick</div>
<div class="content">Other content</div>
<div class="content">Somemore content</div>
</div>
It's just an idea, but in your situation, you can fake a sticky header with a simple trick.
Remove the overflow-y: scroll property of your .box div.
Set a position: absolute property to your .header.
Add a div .content-container who contains your .content div and
set it a position: absolute property too and an overflow-y: scroll.
This way your header is always on top of your .box div and you can scroll your content. Then with your JS code you can change the height of your header on scroll event.
$(document).ready(function(){
$('.content').scroll(function(){
if ($('.content').scrollTop() >= 60){
$('.header').addClass('sticky');
} else {
$('.header').removeClass('sticky');
}
});
});
.box{
margin: 4em auto;
height: 360px;
width: 20em;
background: blue;
position: relative;
}
.header{
position: absolute;
top: 0;
background: orange;
width: 100%;
height: 60px;
font-size: 1.3em;
z-index: 9999;
transition: height 0.3s ease 0s;
-webkit-transition: height 0.3s ease 0s;
-moz-transition: height 0.3s ease 0s;
}
.header.sticky{
height: 30px;
}
.content{
position: absolute;
top: 0;
width: 100%;
height: 300px;
padding-top: 60px;
background: powderblue;
overflow-y: scroll;
}
.content > div{
height: 500px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="box">
<div class="header">This will stick</div>
<div class="content">
<div>Content</div>
<div>Somemore content</div>
</div>
</div>

Show div expanding out from middle out in jquery

I want to show my div when the user generates the trigger. The animation which I want to use showing the div is such that the div is rendered starting from its centre and then gaining its height by expanding in both directions(up and down) gradually. Here is the snippet of what I have tried. The div starts rendering from left. What I want is it starts rendering from middle of its height.
$("#km1").click(function() {
$(".homePopup").animate({
width: "730px",
height: "200px"
}, 800);
})
.homePopup {
position: absolute;
z-index: 100;
width: 400px;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Know more
<div class="homePopup"></div>
You could animate the margin as well to achieve this effect.
Set the initial margin-top and margin-bottom half of the final height; and margin-left and margin-right half of the final width. Then when you increase the width and height, decrease the margin as well.
$("#km1").click(function() {
$(".homePopup").animate({
width: "730px",
height: "200px",
margin: '0'
}, 800);
})
.homePopup {
position: absolute;
z-index: 100;
width: 0px;
margin: 100px 365px;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Know more
<div class="homePopup"></div>
I divided the width and height by four to and added that to the left and top to obtain the center animation requested.
$("#km1").click(function() {
$(".homePopup").animate({
width: "730px",
height: "200px",
left: "0px",
top: "0px"
}, 800);
})
.homePopup {
position: absolute;
z-index: 100;
width: 400px;
background-color: red;
left: 182px;
top: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Know more
<div class="homePopup"></div>
You need to position the element in the middle from the beginning. I'm setting the left absolute position to 50%, then moving the element back -50% of itself so that it is in the middle.
Check out CSS transform:
http://css-tricks.com/almanac/properties/t/transform/
$("#km1").click(function() {
$(".homePopup").animate({
width: "730px",
height: "200px"
}, 800);
})
.homePopup {
position: absolute;
z-index: 100;
width: 0;
background-color: red;
left: 50%;
transform: translateX(-50%);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Know more
<div class="homePopup"></div>
******UPDATE******
Here is the css to run the animation from the middle of the window's height:
.homePopup {
position: absolute;
z-index: 100;
width: 0;
background-color: red;
top: 50%;
transform: translateY(-50%);
}

Categories