I'm looking to append divs from the bottom. At a certain point, the vertical scroll should kick in so you can view divs that were appended earlier on. I'm trying to replicate a typical chat application and how messages come from the bottom. Here's the codepen...
http://codepen.io/jareko999/pen/yaQmgk
Before I put the code, I'll explain a couple of workarounds I've tried thus far. The pen currently has the container absolutely positioned with a bottom of 0. The problem, which is a pain, is that once the height goes beyond the height of the viewport, it won't scroll. This is the problem with the absolute positioning workaround.
Another workaround I've tried is doing a height of 100vh and display of flex with justify-content flex-end so the columns start at the bottom. The problem with this is that the scroll will always start from the top. I believe the solution is a scroll function that I've created to scroll to the bottom every time a new div is added. Would this be the best method? The key here is that I want to be able to scroll up to the older divs but have the newer divs start from the bottom. Think of a typical chat application like slack or messages or similar.
HTML
<button onclick="myFunction()">Hey here's a box</button>
<div id="container">
</div>
CSS
body {
margin: 0;
width: 100%;
}
button {
position: fixed;
z-index: 10;
}
#container {
position: absolute;
bottom: 0;
width: 100%;
}
#box {
width: 100%;
background: tomato;
opacity: 0;
height: 100px;
transition: .2s;
}
#box:last-child {
opacity: 1;
height: 0;
animation: .2s height linear forwards;
}
#keyframes height {
to {
height: 100px;
}
}
#box:nth-last-child(2) {
opacity: .8;
}
#box:nth-last-child(3) {
opacity: .6;
}
#box:nth-last-child(4) {
opacity: .4;
}
#box:nth-last-child(5) {
opacity: .2;
}
JS
function myFunction() {
var box = document.createElement("div");
box.setAttribute("id", "box");
var container = document.getElementById('container');
container.appendChild(box);
// window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
}
Is there a better solution than the function I've created to scroll to the bottom? Much appreciated.
Ok, so after messing around with some JS, I figured it out. I love when that happens...
Here's the codepen...
http://codepen.io/jareko999/pen/yaQmgk
I created a setInterval function for scrolling to the bottom.
var myVar = setInterval(function(){
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
}, .1);
However, since this interval runs every .1 seconds, I need to kill it in order to scroll around the divs above (like old chat messages), but I want the animation (of the new div coming in) to finish. So, I created a setTimeout function to kill the setInterval function at 200 ms.
setTimeout(function(){
clearInterval(myVar);
}, 200);
Related
I am sitting with a project in need of an overlay which fades out when hovered upon and goes to display: none (not visibility: hidden, it does need to be display: none).
The setup is a big confusing, but I will try to explain it:
The overlay comes up when I hover a menu point under my mega menu. When I move the cursor to the overlay it should naturally dissapear and the menu close.
This works very well with this code:
var element = document.getElementById("overlayed");
function mouseOver() {
element.classList.add("mystyle");
setTimeout(function() {
element.classList.remove("mystyle");
}, 500);
}
push {
position: absolute;
top: 50%;
}
.overlayerstwo {
position: fixed;
width: 100%;
top: 30%;
left: 0;
right: 0;
bottom: 0;
background: #111;
opacity: 0.5;
z-index: 2;
display: block;
visibility: visible;
}
.mystyle {
display: none;
animation-name: fadeOut;
animation-duration: .5s;
}
#keyframes fadeOut {
0% {
opacity: .5
}
100% {
opacity: 0;
}
}
.mystyler {
display: none;
}
<h1>Here is something. Overlay comes back when hovering me!</h1>
<div class="overlayerstwo" id="overlayed" onmouseover="mouseOver()"></div>
<div class="push">
<p>Here is an item being overlayed</p>
</div>
With this setup the overlay dissapears right away. I am trying to merge it with the fadeOut keyframe animation before it goes black. I have tried different tactics, like adding a second timeout event but all it does is loop through and end up showing the overlay permanently after.
So the order I want to achieve is as follows:
Add a class that fires the keyframe animation fadeOut for .5 sec
Remove keyframe animation class
Add display: block class
Remove display: block class (essentially resetting it, so you can get the overlay up again by hovering its triggerpoint)
So my question is, how do I get all of these to fire every time I hover over the overlay?
One of the things I tried was this:
var element = document.getElementById("overlayed");
element.classList.add("mystyle");
setTimeout(function(){
var element = document.getElementById("overlayed");
element.classList.remove("mystyle");
}, 500);
setTimeout(function(){
var element = document.getElementById("overlayed");
element.classList.add("mystyletwo");
}, 500);
setTimeout(function(){
var element = document.getElementById("overlayed");
element.classList.remove("mystyletwo");
}, 510);
With the css
.mystyle{
animation-name: fadeOut;
animation-duration: .5s;
}
.mystyletwo{
display: block;
}
Which did not work. I hope someone can help me figure out how to get it to work!
if the timeline will be like this: visible -> hover -> animation -> opacity to 0 -> display: none
using CSS with JS logic:
element.addEventListener("mouseover", function() {
element.style.opacity = "0";
element.style.transition = "all 0.3s";
// when finish the animation then call display none
setTimeout(function() {
element.style.display = "none";
}, 300); // put the same number (milliseconds) of duration of transition (or more, not less)
});
using this method you don't need to complex your code...
the trick really is because we use element.style
that is only put the CSS, but technically...
if there is a transition Javascript don't know it,
so it will run the setTimeout() directly after adding styles,
so now CSS will do the animation but javascript will quietly continue the code (which in our case, says that after 300 seconds add display: none;)
I am trying to make the footer of a website im creating change color DURING scrolling and revert back to its original color after the scroll is done.
Is there any way to detect when a scroll event ends?
I am looking for answers in vanilla js. Or if there are any specific recoomendation available in for example jQuery im more than willing to look into it per your kind suggestion.
So you're probably looking for scroll event handeler here
And to check if you're done scrolling to the bottom, you need to check if the scroll height is left or not? This might help with that!
Using code I got from detecting when a visitor has stopped scrolling I derived this:
// Setup isScrolling variable
var isScrolling;
// Listen for scroll events
window.addEventListener('scroll', function ( event ) {
// Clear our timeout throughout the scroll
window.clearTimeout( isScrolling );
// Set a timeout to run after scrolling ends
isScrolling = setTimeout(function() {
// Run the callback
if(document.querySelector('#mydiv').style.background=='red')
document.querySelector('#mydiv').style.background='blue';
else if(document.querySelector('#mydiv').style.background=='blue')
document.querySelector('#mydiv').style.background='yellow'
else
document.querySelector('#mydiv').style.background='red'
}, 66);
}, false);
div{
width:100%;
height:400vh;
}
<body>
<h1>scroll down this(document should change colour when you stop:</h1>
<div id="mydiv"></div>
</body>
You just set a timeout to wait until the user is done scrolling. If the user continues to scroll the timeout is cleared and reset before it activates(meaning the code won't run until he stops).
You can get the window height and the height of your footer element and check the scroll pageYOffset in relation to the window height => window.innerHeight and the footers top position in relation to the viewport => footer.getBoundingClientRect().top.
Use a set of conditionals to check the scroll position. Set it anywhere you'd like for the initial color change, I used the initial window.innerHeight to start the BG color change.
For the bg-color change, I use a class and add it to the body with classList.add('bgColor'). Then we further check if the pageYOffset is greater than the window.innerHeight - footer.getBoundingClientRect().top, meaning the footer will just be breaking the bottom fold of the page and coming into view, if this happens we remove the class that adds the bg-color and return the document to its initial bg-color.
const height = 3000;
document.documentElement.style.setProperty("--height", `${height}px`)
const display = document.getElementById('display')
const footer = document.querySelector('.footer')
const rect = footer.getBoundingClientRect()
const screenHeight = window.innerHeight
function setBgOnScroll(e) {
let offset = window.pageYOffset
display.textContent = `scroll offset: ${offset} footer top position: ${rect.top - screenHeight}`
offset > screenHeight ?
document.body.classList.add('bgColor') : document.body.classList.remove('bgColor')
offset > rect.top - screenHeight ?
document.body.classList.remove('bgColor') : null
}
window.addEventListener('scroll',setBgOnScroll)
:root {
--body-bg-color: white;
}
body {
background-color: var(--body-bg-color);
transition: background-color ease-in-out 500ms;
}
.cont {
height: var(--height);
}
#display {
position: fixed;
top: 20px;
right: 220px;
}
.footer {
position: absolute;
top: calc(3000px - 100px);
left: 0;
width: 100%;
background-color: green;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
.bgColor {
background-color: lightblue;
transition: background-color ease-in-out 500ms;
}
<div class="cont">
<div id="display"></div>
Page content, scroll down...
<div class="footer">Footer</div>
</div>
While you can react to the scroll event with Javascript you'll need to define some timings to decide when the user has stopped scrolling.
As you can't sense a 'not-scrolling' event this will have to be with something like a setTimeout.
<style>
div {
height: 300vh;
}
footer {
position: fixed;
bottom: 0;
width: 100vw;
height: 20vh;
background-color: magenta;
}
</style>
<body>
<div>scroll down</div>
<footer>Footer</footer>
<script>
let timeout = false;
const footer = document.querySelector('footer');
document.addEventListener('scroll', function () {
if (timeout) {clearTimeout(timeout); timeout = false;}
footer.style.backgroundColor = 'blue';
timeout = setTimeout( function () {
footer.style.backgroundColor = 'red';
}, 1000);
});
</script>
</body>
How do I enlarge a div while scrolling from a size of 20% width and height in the center to 100% width and height?
I'm currently trying at my first website and I'm almost there. All that is missing is animations and improvements in CSS. One of my ideas is that you have a div with a background inside and while scrolling the picture gets bigger up to the whole viewpoint. I would be very grateful if someone could help me.
You can use transform scale to do it.
CSS part will set the element to take 100% of width and height (i use viewport units), and set it position to fixed (so you will see what happen when you scroll).
Since we gonna change it's scale while scroll, set it initial scale to be 20% of it's original size.
JS part will listen to scroll event and scale the div that it won't be less then 20% but also won't be larger then 100%.
Play with the numbers on the condition to get what you need:
const demoDiv = document.querySelector("#demo");
window.addEventListener('scroll', function() {
if (pageYOffset*0.0001 > 1 || pageYOffset*0.0001 < 0.2) { return; }
else { demo.setAttribute('style', 'transform: scale('+pageYOffset*0.0001+');'); }
});
body {height: 40000px; margin: 0; padding: 0;}
p {position: fixed; top: 0; left: 0; font-size: 40px;}
#demo {
text-align: center;
font-size: 10vw;
position: fixed; top: 0; left: 0;
width: 100vw;
height: 100vh;
background-color: black;
color: white;
transform: scale(0.2); /* since you ask for 20% */
}
<p style="">Scroll to see it grow.</p>
<div id="demo">My minumum width and height are 20% and i will stop grow when i get to 100%</div>
Firstly, Congratulations on your first website. Good luck on your coding journey.
You can do it by using CSS & JavaScript. There is many way, but I'm writing one here. I hope it will be some good.
Let us call the div with an CSS ID animatedDiv.
<div id="animatedDiv"></div>
Now, lets style it with CSS
#animatedDiv
{
margin-top: 200px;
background: #dc143c;
min-height: 350px;
min-width: 20%;
position: absolute;
}
Here, I gave the div a background color, Absolute type of position, and margin-top of 200px. You can change it according to your needs. I used min-height and min-width property because these value will not be any fixed value, they will change on scroll.
Now, lets write some JavaScript
var aDiv = document.getElementById("animatedDiv");
function changeWidth()
{
var scrollVal = window.pageYOffset;
var scrollSlow = (scrollVal / 4);
//Changing CSS Width
aDiv.style.width = Math.min(Math.max(scrollSlow, 20), 100) + "%";
}
window.addEventListener('scroll', function()
{
requestAnimationFrame(changeWidth);
}, false);
Here, on a user define function, I catch the div with it's ID and assign into aDiv variable. Then I catch the page offset on Y axis (How much pixel the page was scrolled) and store it into a variable scrollVal, Next I divide the value by four (you can use 5, 10 20). It will slow the changing effect.
I've use Math methods (min and max) to assign a value between 20 to 100%.
To make the function work on scroll, window.addEventListener is used, and the window.requestAnimationFrame() method will tell the browser that we wish to perform it as an animation.
I hope it will be some help to you. I don't know did I explain well the process to you or not. English is not my mother language, so please don't mind if I made any grammatically mistake.
Wish you all the best.
How can I detect changes made by CSS3 animations or web animations (Element.animate)??
(Sorry for my bad English! this is my first question in Stackoverflow)
I know about MutationObserver, it responds only if I change the inline style or if I use requestAnimationFrame (since I change the inline style using it). But if I use CSS3 animations or Web animations, MutationObserver doesn't respond since they don't change the inline style.
See this... There are two divs here... div1, div2. div1's position will change when div2's position changes. But this happens only if I use requestAnimationFrame as I said before.
My question is how can I do this for css3 animations and web animations (Element.animate)?
const div1 = document.getElementById('div1');
const div2 = document.getElementById('div2');
/***** Add mutation observer to detect change *****/
const mutation = new MutationObserver(mutations => {
div1.style.left = div2.style.left;
});
mutation.observe(div2, {
attributes: true
});
/***** Animation with css *****/
function cssAnimation() {
div2.style.animation = 'anim 1.5s linear';
}
/***** Animation with web animations *****/
function webAnimation() {
div2.animate({
left: [0, '500px']
}, {
duration: 1500,
easing: 'linear'
});
}
/*****Animation with requestAnimationFrame ******/
// Current left position of div2
const left = 0;
function requestAnimation() {
// Increase left position 5px per keyframe
div2.style.left = `${(left += 5)}px`;
// Increase left position until it reaches to 500px
if (left < 500) {
requestAnimationFrame(requestAnimation);
}
}
function clearAnimations() {
left = 0;
div2.style.left = 0;
div2.style.animation = 'unset';
}
#keyframes anim {
from {
left: 0;
}
to {
left: 500px;
}
}
#div1 {
background: orange;
width: 100px;
height: 100px;
position: absolute;
top: 200px;
}
#div2 {
background: lightgreen;
width: 100px;
height: 100px;
position: absolute;
top: 100px;
}
<div id="buttons">
<h3>Animate with...</h3>
<button onclick='cssAnimation()'>Css3</button>
<button onclick="requestAnimation()">request animation frame</button>
<button onclick="webAnimation()">web animations api</button>
<button id="clear" onclick="clearAnimations()">Clear</button>
</div>
<div id="div1">
Div1
</div>
<div id="div2">
div2
</div>
Both CSS Animations and Web Animations are based on the principle that you delegate the playback of the animation to the browser. That allows the browser to run the animation on a separate thread or process when possible, so that it runs smoothly. Updating style from JavaScript on each frame is best avoided where possible.
In your example, can you simply run the animation on both elements at the same time? Web Animations, at least, allows synchronizing the animations.
When the remainder of the Web Animations API is shipped, it will be much easier to duplicate animations from one element and apply them to another but for now you would need to call animate twice.
As others have pointed out, it is possible to observe significant moments in the playback of animations (when they start, finish, repeat etc.) but there is no event that runs on each frame. If you want to perform and action on each frame you need to use requestAnimationFrame.
If you pass div2 to getComputedStyle in requestAnimationFrame you will get the animated style for that frame which you can then apply to div1. (That is, reading div2.style.left will only give you the style specified via the style attribute but getComputedStyle(div2).left will give you the animated style including style changes from CSS animations and Web Animations). But, again, that will lead to poor performance and the two animations will not necessarily be synchronized since the CSS animation or Web animation may run on a different thread or process.
You can use requestAnimationFrame and window.getComputedStyle() to get current animated styles during the animation, note, included fill:"forward" at Element.animate() call
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
/***** Add mutation observer to detect change *****/
var mutation = new MutationObserver(function(mutations) {
div1.style.left = div2.style.left;
});
mutation.observe(div2, {
attributes: true
});
/***** Animation with css *****/
function cssAnimation() {
div2.style.animation = "anim 1.5s linear forwards";
let animationFrame;
function getCurrentStyles() {
console.log(window.getComputedStyle(div2).left);
animationFrame = requestAnimationFrame(getCurrentStyles)
}
getCurrentStyles();
div2.addEventListener("animationend", () => cancelAnimationFrame(animationFrame));
}
/***** Animation with web animations *****/
function webAnimation() {
let animationFrame;
function getCurrentStyles() {
console.log(window.getComputedStyle(div2).left);
animationFrame = requestAnimationFrame(getCurrentStyles)
}
getCurrentStyles();
div2.animate({
left: [0, "500px"]
}, {
duration: 1500,
fill: "forwards",
easing: "linear"
}).onfinish = function() {
cancelAnimationFrame(animationFrame);
console.log(window.getComputedStyle(div2).left);
};
}
/*****Animation with requestAnimationFrame ******/
//Current left position of div2
var left = 0;
function requestAnimation() {
//Increase left position 5px per keyframe
div2.style.left = `${(left += 5)}px`;
console.log(window.getComputedStyle(div2).left);
//Increase left position until it reaches to 500px
if (left < 500) {
requestAnimationFrame(requestAnimation);
}
}
function clearAnimations() {
left = 0;
div2.style.left = 0;
div2.style.animation = "unset";
}
#keyframes anim {
from {
left: 0;
}
to {
left: 500px;
}
}
#div1 {
background: orange;
width: 100px;
height: 100px;
position: absolute;
top: 200px;
}
#div2 {
background: lightgreen;
width: 100px;
height: 100px;
position: absolute;
top: 100px;
}
<div id="buttons">
<h3>Animate with...</h3>
<button onclick='cssAnimation()'>Css3</button>
<button onclick="requestAnimation()">request animation frame</button>
<button onclick="webAnimation()">web animations api</button>
<button id="clear" onclick="clearAnimations()">Clear</button>
</div>
<div id="div1">
Div1
</div>
<div id="div2">
div2
</div>
For css animations, you have the animationstart and animationend events that could help you with what you are trying to achieve. However, there is no animationchange or animationupdate event, and it is this way, as far as I know, by design. Without events during the animation happening, it is possible to reach full hardware acceleration, with the interpolation computations done directly in the GPU. So, be aware that while you might be able to mimic what an animationchange event would do via animationstart, animationend and requestAnimationFrame, this is probably going to involve a performance penalty.
You're over complicating things. But to listen for animation changes you can listen on these events instead of the mutation observer.
animationstart //will fire has soon as the animation starts
animationiteration //will fire if the same animation is looped can be infinity or more then 2
animationend // will fire when the animations have ended
also please use translate instead of animating the left property.
I have two navigation in my website. Both the navigation bars are fixed. Basically when I scroll up, I would like to use the animate() and show both the navigation bar in the page. How do I get the scroll up event and use that to animate the divs, like the Google search widget. I would really appreciate your help. Thank you.
html:
<div id="navbar_header">
some link
</div>
<div id="main_content">
<p>Some content...</p>
</div>
<div id="navbar_footer">
some link
</div>
css:
#navbar_header {
background: #22313F;
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 40px;
}
#navbar_footer {
background: #22313F;
position: fixed;
bottom: 0px;
left: 0px;
width: 100%;
height: 40px;
}
Normally using the window for the scroll event should be sufficient, as it's big enough and the one element, that's being scrolled. If jQuery is loaded correctly, you could try something like this:
$(document).ready(function(){
var lastTopPosition = 0;
$(window).scroll(function(){
var topPosition = $(window).scrollTop();
if (topPosition > lastTopPosition ){
$("#navbar_header").stop(true).animate({'top':'-40px'}, 200);
$("#navbar_footer").stop(true).animate({'bottom':'-40px'}, 200);
} else {
$("#navbar_header").stop(true).animate({'top':'0px'}, 200);
$("#navbar_footer").stop(true).animate({'bottom':'0px'}, 200);
}
lastTopPosition = topPosition;
}
});
This piece of code gets the current position from the top everytime you scroll. If the distance gets bigger (scroll down) the two bars fadeout. If it's getting smaller (scroll up) it fades in. You can replace the FadeOut/In methods here with you animate() call too. A check, if the elements are displayed would be good here too, but I guess you can figure that one out ;-)
If I understood this right, something along the lines of:
$("#main_content").scroll(function(){
$('#navbar_header').show(300);
$('#navbar_footer').show(300);
});
Where show(300) will basically do a 300ms showing animation of your divs.