Scroll position when prepending elements - css

I have a list of messages, when the user scroll to the top of the list, the app loads more messages. This part is working fine.
But the scroll position is kept unchanged, it's not scrolling to the previously shown element, the scroll position is always on top (so we could load more messages again).
This behavior happens only when user has scrolled to the very top. If we load more message but user has scroll down even a very little bit, it keeps the same position.
Is there a css solution ?
Or should I programmatically scroll down user to the previous message ?
Or should I programmatically scroll down a very little bit juste before adding messages to the list ?
function addElement(id) {
const $element = $($('#TemplateItem').html().replace('{id}', id));
$('.list').prepend($element);
}
var loadCount = 0;
function loadMore() {
loadCount++;
for(let i=0; i < 5; i++) {
addElement(loadCount + '-' + (i+1));
}
}
$('button').click(() => {
loadMore();
});
loadMore();
.list {
width: 50%;
height: 20rem;
background: yellow;
overflow-y: auto;
}
.item {
background: lime;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.2/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<button class="btn btn-primary mt-3" type="button">
Load more
</button>
<div class="list mt-3 p-2"></div>
<template id="TemplateItem">
<div class="item mt-1 p-2">I am fantastic item #{id} !</div>
</template>
</div>

Refering to this answer, I have created my own solution.
Once the nodes has been added, update the scroll top of the container to the last node that existed before adding the new nodes.
Working example.
function addElement(id) {
const $element = $($('#TemplateItem').html().replace('{id}', id));
$element[0].id = `list-node-${id}`;
$('.list').prepend($element);
}
var loadCount = 0;
var incrementer = 5;
function loadMore() {
loadCount++;
for (let i = 0; i < incrementer; i++) {
addElement(loadCount + '-' + (i + 1));
}
if (loadCount > 1) {
initialNode = $(`#list-node-${loadCount - 1}-${incrementer}`);
$('#wrapper').animate({
scrollTop: initialNode.offset().top - $('#wrapper').offset().top + $('#wrapper').scrollTop()
}, 1000);
}
}
$('button').click(() => {
loadMore();
});
loadMore();
.list {
width: 50%;
height: 20rem;
background: yellow;
overflow-y: auto;
}
.item {
background: lime;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.2/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<button class="btn btn-primary mt-3" type="button">
Load more
</button>
<div class="list mt-3 p-2" id="wrapper"></div>
<template id="TemplateItem">
<div class="item mt-1 p-2">I am fantastic item #{id} !</div>
</template>
</div>

Related

Lottie Preloader only show once per session

I have a working preloader that shows a lottie animation when the page is loaded. But I want it to show up only one per session. So that it doesn't show up every time someone opens or loads the page.
Current code:
<style>
.hidden {
display: none;
}
.visuallyhidden {
opacity: 0;
}
</style>
<div id="preloader" class="preloader-container">
<div class="animation">
<div class="player">
<script src="https://unpkg.com/#lottiefiles/lottie-player#latest/dist/lottie-player.js"></script>
<lottie-player src="https://lottie.host/9fd6c66b-4984-4b99-a7d6-b44856d4c4de/1ZusfoGYH2.json" background="transparent" speed="1" style="width: 300px; height: 300px;" loop autoplay></lottie-player>
</div>
<a id="skip">Skip</a>
</div>
</div>
<script>
let box = document.querySelector("#preloader"),
btn = document.querySelector("#skip");
function fadeOut() {
box.classList.add("visuallyhidden");
box.addEventListener(
"transitionend",
function (e) {
box.classList.add("hidden");
},
{
capture: false,
once: true,
passive: false
}
);
}
btn.addEventListener("click", fadeOut, false);
setTimeout(fadeOut, 6000);
</script>

When rendering modal/component i want background to have a dark color

Hello so i use a portal Component that looks like this that is used to create "modals"
export const ModalWindow = (props: RenderableProps<Props>) => {
if (!props.display) {
return null;
}
return (
<Portal parentNode={props.parentNode} renderOnMount={true}>
<div id='modal' className={props.largeModal ? 'modal-large' : 'modal-small'}>
{props.children}
</div>
</Portal>
)
}
Then i have this component that i then render inside that modal
export const ShowInformation = (props: RenderableProps<Props>) => {
return (
<div className='blur-out-menu'>
<div className='information-content'>
<span className='information-title'>Show Information?</span>
<span className='information-text'>Click check button to show information</span>
<div className='button-section'>
<div className='decline-button' onClick={() => props.onShow(false)}>
<CancelButton />
</div>
<div className='check-button' onClick={() => props.onShow(true)}>
<CheckButton />
</div>
</div>
</div>
</div>
);
};
what is important here is the blur-out-menu that i just want to use to blur out everything except the modal but i cant figure it out either it just covers the same area as the modal or if i put position fixed it messes it up totally and nothing inside the modal keeps in place. is there any nice way of doing this?
here is the css for the blur-out-menu
.blur-out-menu {
background-color: rgba(0, 0, 0, 0.25);
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 4;
}
the modal got z-index 5 so it is above this blur out
here is the modal css as well
.modal-small {
background-color: #1E2933;
top: 25%;
left: 5%;
width: 90%;
height: 30%;
border-radius: 10px;
z-index: 5;
}
In .blur-out-menu change height property to 100vh and width property to 100vw
This should to the trick.

CSS3 animate or transition elements into position after removal of an element above

What is the best way of achieving this without any visible flickering and any wierdness?
The fiddle to start out: http://jsfiddle.net/35qec14b/2/
$('.element').on('click', function(e){
this.remove();
});
.element {
position:relative;
width: 200px;
margin:5px;
padding:20px;
cursor:pointer;
background: rgb(150,200,250);
transition:1s linear;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
(click to remove)
<div class="element">Element 1</div>
<div class="element">Element 2<br>Second line</div>
<div class="element">Element 3</div>
<div class="element">Element 4<br>Second line</div>
<div class="element">Element 5</div>
Note: the removed element must disappear instantly in this case, as it would appear in another location and we don't want it to be visible in two places simultaneously.
Ideas so far:
transform:translateY for ALL elements below the removed one (probably performance intensive for large lists)
Animate/transform margin of the first element below, from removed element's height to 0 (leveraging chained animations? step-start?)
Replace the removed element with a transparent placeholder and animate it's own height to 0
The best that comes to mind is to hide it, clone it for its new location (not showed here), and then animate its height
When one animate both margins, paddings and height, it becomes not so smooth, so I added an extra inner wrapper for the content so the animation only animates the height
$('.element').on('click', function(e) {
this.style.height = $(this).height()+ 'px';
this.classList.add('hide-me');
(function(el) {
setTimeout(function() {
el.remove();
}, 500);
})(this);
});
.element {
position: relative;
width: 200px;
overflow: hidden;
}
.element > div {
margin: 5px;
padding: 20px;
background: rgb(150, 200, 250);
}
.element.hide-me {
animation: hideme .5s forwards;
opacity: 0;
}
#keyframes hideme {
100% {
height: 0;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
(click to remove)
<div class="element">
<div>
Element 1
</div>
</div>
<div class="element">
<div>
Element 2
<br>Second line
</div>
</div>
<div class="element">
<div>
Element 3
</div>
</div>
<div class="element">
<div>
Element 4
<br>Second line
</div>
</div>
<div class="element">
<div>
Element 5
</div>
</div>
Here's the jQuery approach using .animate()
$('.element').on('click', function(e){
var $this = $(this), $next = $this.next();
$next.css({
marginTop: $this.outerHeight(true)
}).animate({
marginTop: 5
}, 200);
$this.remove();
});
.element {
position:relative;
width: 200px;
margin:5px;
padding:20px;
cursor:pointer;
background: rgb(150,200,250);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
(click to remove)
<div class="element">Element 1</div>
<div class="element">Element 2<br>Second line</div>
<div class="element">Element 3</div>
<div class="element">Element 4<br>Second line</div>
<div class="element">Element 5</div>
And here is a CSS transition approach
$('.element').on('click', function(e){
var $this = $(this), $next = $this.next();
$next.css({
marginTop: $this.outerHeight(true)
});
setTimeout(()=>{
$next.addClass('hide');
setTimeout(()=>{
$next.css({marginTop: ''}).removeClass('hide');
}, 250)
}, 20);
$this.remove();
});
.element {
position:relative;
width: 200px;
margin:5px;
padding:20px;
cursor:pointer;
background: rgb(150,200,250);
}
.element.hide {
transition: margin-top 0.25s linear;
margin-top: 5px !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
(click to remove)
<div class="element">Element 1</div>
<div class="element">Element 2<br>Second line</div>
<div class="element">Element 3</div>
<div class="element">Element 4<br>Second line</div>
<div class="element">Element 5</div>
Even though you are concerned that using transforms could have a negative performance, I think that the opposite is true.
Bear in mind that other solutions involve massive reflows, that probably are more performance intensive on the CPU (transforms most probably are handled by the GPU).
However, solving this with this with transforms is a little bit hard to code. Specially changing the amount of pixels that need to be moved, and injecting them into the style.
See a posible solution. I have used just JS to make it more portable.
If you are concern about performance, the result of findKeyframesRule could be assigned to a variable and reused.
document.addEventListener('DOMContentLoaded', setEvent, false);
function setEvent() {
var elements = document.getElementsByClassName('element');
for (var n = 0; n < elements.length; n++) {
elements[n].addEventListener('click', remove, false);
}
}
function remove(event) {
var current = event.currentTarget;
var elements = document.getElementsByClassName('move');
for (var n = 0; n < elements.length; n++) {
elements[n].classList.remove('move');
}
window.setTimeout(function() {
remove2(current);
}, 0);
}
function remove2(current) {
var next = current.nextElementSibling;
if (!next) {
return;
}
var top1 = current.offsetTop;
var top2 = next.offsetTop;
var diff = top2 - top1;
var newTransform = 'translateY(' + diff + 'px)';
var rule = findKeyframesRule('move');
var style = rule.cssRules[0].style;
style.transform = newTransform;
next.classList.add('move');
current.style.height = '0px';
}
function findKeyframesRule(rule) {
// gather all stylesheets into an array
var ss = document.styleSheets;
// loop through the stylesheets
for (var i = 0; i < ss.length; i++) {
var ss1 = ss[i];
// loop through all the rules
if (!ss1.cssRules) {
alert('you are using Chrome in local files');
return null;
}
for (var j = 0; j < ss1.cssRules.length; j++) {
// find the keyframe rule whose name matches our passed parameter
if (ss1.cssRules[j].type == window.CSSRule.KEYFRAMES_RULE && ss1.cssRules[j].name == rule)
return ss1.cssRules[j];
}
}
return null;
}
.element {
position: relative;
width: 200px;
overflow: hidden;
}
.element>div {
margin: 5px;
padding: 20px;
background: rgb(150, 200, 250);
}
.move,
.move~.element {
animation: move 2s;
}
#keyframes move {
from {
transform: translateY(60px);
}
to {
transform: translateY( 0px);
}
}
(click to remove)
<div class="element">
<div>
Element 1
</div>
</div>
<div class="element">
<div>
Element 2
<br>Second line
</div>
</div>
<div class="element">
<div>
Element 3
</div>
</div>
<div class="element">
<div>
Element 4
<br>Second line
</div>
</div>
<div class="element">
<div>
Element 5
</div>
</div>

Angularjs 1.4.5: animation using ng-enter not working

I am new to AngularJS and I am working on creating fadein animation. I added all the required js files in my my header and element.ng-enter and element.ng-enter-ng-enter.active classes in css file as per documents from AngularJS. When I browse the URL, I dont get fadein effects. Here is my JSFiddle link.
HTML code:
<div ng-app="Dashboard">
<div id="dashboard" ng-controller="dashboardCtrl">
<div class="app-nav">
<button onclick="location.href = '/';">Dashboard</button>
</div>
<br>
<div class="app-nav">
<button onclick="location.href = 'Applications';">Healthchecks</button>
</div>
<br>
<div class="app-nav">
<button>Contact US</button>
</div>
<br>
</div>
</div>
JS Script:
angular.module('Dashboard', ['ngAnimate'])
.controller('dashboardCtrl', function ($scope) {
});
CSS:
#dashboard {
margin-top: 50px;
}
.app-nav {
max-width: 350px;
min-height: 75px !important;
margin: 0 auto;
}
.app-nav button:hover {
background-color: #154995;
}
.app-nav button {
min-width: 350px;
min-height: 75px;
}
button.ng-enter {
transition: 3s linear all;
opacity: 0;
}
button.ng-enter.ng-enter-active {
opacity: 2;
}
Do not mix pure javascript in your html file with angular. You should replace onclick with ng-click because then angular can inject correct class to div and show you animation
index.html
<div ng-app="Dashboard">
<div id="dashboard" ng-controller="dashboardCtrl">
<div class="app-nav">
<button ng-click="functionOne()">Dashboard</button>
</div>
<br>
<div class="app-nav">
<button ng-click="functionTwo()">Healthchecks</button>
</div>
<br>
<div class="app-nav">
<button>Contact US</button>
</div>
<br>
</div>
</div>
controller.js
(function () {
'use strict';
angular
.module('Dashboard')
.controller('dashboardCtrl', dashboardCtrl);
dashboardCtrl.$inject = ['$scope', 'ngAnimate'];
function dashboardCtrl($scope, ngAnimate) {
$scope.functionOne = function () {
// your action
};
$scope.functionTwo = function () {
// your action
};
}
}());

Scrolling effect: Slow at first than it goes fast

I'm trying to create a scrolling effect where when the onclick event is triggered, I want that div1 to scroll to dev2. It should initially go slowly and then fast!.
Here's a website using this effect: http://community.saucony.com/kinvara3/
How do I do this?
$.fn.animateScrollDivs = function(sourceDiv, destinationDiv) {
var source = '#' + sourceDiv;
var destination = '#' + destinationDiv;
$('html, body').animate({
scrollTop: $(destination).offset().top
}, 1200, 'easeInExpo');
};
function animateDiv(sourceDiv, destinationDiv) {
$.fn.animateScrollDivs(sourceDiv, destinationDiv);
}
div {
height: 650px;
width: 1000px;
}
button {
background-color: #FE2EF7;
}
.downButton {
margin-top: 500px;
margin-bottom: 40px;
margin-right: 200px;
margin-left: 200px;
}
.upButton {
margin-top: 60px;
margin-bottom: 500px;
margin-right: 200px;
margin-left: 200px;
}
<body>
<div id="div1" style="background-color:red;">
<button class="downButton" onclick="animateDiv('div1','div2');">Go Down</button>
</div>
<div id="div2" style="background-color:yellow;">
<button class="upButton" onclick="animateDiv('div2','div1');">Go Up</button>
<button class="downButton" onclick="animateDiv('div2','div3');">Go Down</button>
</div>
<div id="div3" style="background-color:grey;">
<button class="upButton" onclick="animateDiv('div3','div2');">Go Up</button>
<button class="downButton" onclick="animateDiv('div3','div4');">Go Down</button>
</div>
<div id="div4" style="background-color:#2E9AFE;">
<button class="upButton" onclick="animateDiv( 'div4', 'div1');">GoToTop</button>
</div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js "></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js "></script>
Will this do?? Please adjust the position of button(s) as per your requirement.
I've used jQuery mmin (1.11) and jQuery UI (1.11).
Make use of ScrollTop with offset().top to scroll it to DIV nodes.
HTML
Do add a "active" class on DIV item which you want to show at first on page load. For me, it's the first DIV item.
<button class="giu">Animate To Next available List Item</button>
<div class="product active" id="product1">1</div>
<div class="product" id="product2">2</div>
<div class="product" id="product3">3</div>
<div class="product" id="product4">4</div>
JavaScript
$('.giu').click(function(event) {
event.preventDefault();
var n = $(window).height();
$('div.active').removeClass('active').next('div').addClass('active');
$('body').animate({
scrollTop: $(".product.active").offset().top
}, 500);
});
Fiddle - http://jsfiddle.net/ideaovation/fhg1g974/3/

Resources