Animating height property :: HTML + CSS + JavaScript - javascript

I have noticed this 'issue' lately when trying some stuff.
Say I want to create a drop-down menu or an accordion.
This is my HTML:
<div class="wrapper" onclick="toggle()">
I want to be animated!
<div class="content">
Was I revealed in a timely fashion?
</div>
</div>
Stylesheets:
.wrapper {
background: red;
color: white;
height: auto;
padding: 12px;
transition: 2s height;
}
.content {
display: none;
}
.content.visible {
display: block;
}
JavaScript:
function toggle () {
var content = document.getElementsByClassName('content')[0];
var test = content.classList.contains('visible');
test ? content.classList.remove('visible') :
content.classList.add('visible');
}
I am trying to achieve a nice, smooth animation when we toggle the state of the content. Obviously this does not work. Anyone can explain to me why it does not work and how to fix it? Many thanks.
Link to the JSFiddle.

First things first, some CSS properties CANNOT be transitioned, display is one of them, additionally only discrete values can be transitioned, so height: auto cannot as well.
In your case the problem is with height: auto, while there are a few hacks for doing this, if you are just showing and hiding stuff, why not add, and use jQuery's toggle instead?
$(".content").toggle("slow");
jsFiddle
--EDIT (without jQuery)--
Because it's the auto that is giving us problems, we can use javascript to replace auto with a value in pixels and then use the css transition normally, if your content doesn't have a scroll, we can easily take that value from the scrollHeight property:
function toggle () {
var content = document.getElementsByClassName('content')[0];
var test = content.classList.contains('visible');
console.log(test);
if (test) {
content.classList.remove('visible')
content.style.height = "0px";
} else {
content.classList.add('visible');
content.style.height = content.scrollHeight + "px";
}
}
Css
.wrapper {
background: red;
color: white;
height: auto;
padding: 12px;
transition: 2s height;
}
.content {
height: 0px;
display: block;
transition: 2s height;
overflow: hidden;
} /* totally removed .content.visible */
jsFiddle

Related

How can I restart a CSS transition as soon as it ends using standard JavaScript?

I have built a kind of password generator that should display a new password whenever the countdown expires. Unfortunately, I have only managed to figure out how to run my code once. The countdown consists of a simple CSS transition, which I would like to keep, because it is much smoother than my other attempts, wherein i tried to repeatedly update the width using JavaScript.
var dictionary = {
"adverbs": [
"always",
"usually",
"probably"
],
"adjectives": [
"useful",
"popular",
"accurate"
],
"nouns": [
"computer",
"software",
"terminal"
]
};
function capitalizeFirst(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function randomIndex(object) {
return object[Math.floor(Math.random() * object.length)];
}
function generatePassword() {
var category = ["adverbs", "adjectives", "nouns"];
var password = [];
for (i = 0; i < category.length; i++) {
password.push(capitalizeFirst(randomIndex(dictionary[category[i]])));
}
password.push(Math.floor(Math.random() * 8999) + 1000);
return password.join("");
}
function updatePassword() {
document.getElementById("countdown-fill").style.width = 100 + '%';
document.getElementById("text-field").value = generatePassword();
document.getElementById("countdown-fill").style.width = 0 + '%';
}
setInterval(updatePassword, 5000);
#import url('https://fonts.googleapis.com/css?family=Nunito&display=swap');
body {
margin: 0;
width: 100%;
height: 100%;
background-color: #f8f8f8;
}
.container {
max-width: 400px;
margin: 0 auto;
}
#text-field {
font-size: 15px;
font-weight: 400;
font-family: 'Nunito', sans-serif;
margin-top: 100px;
margin-bottom: 10px;
width: 100%;
height: 30px;
padding: 10px;
box-sizing: border-box;
border: 1px solid #e5e5e5;
background-color: #ffffff;
}
#countdown-background {
width: 100%;
height: 10px;
box-sizing: border-box;
border: 1px solid #e5e5e5;
background-color: #ffffff;
}
#countdown-fill {
width: 100%;
height: 100%;
transition: width 5s;
transition-timing-function: linear;
background-color: #1e87f0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Password Generator</title>
</head>
<body>
<div class="container">
<input id="text-field" type="text" spellcheck="false">
<div id="countdown-background">
<div id="countdown-fill"></div>
</div>
</div>
</body>
</html>
Currently, I have two apparent issues with my code:
The transition becomes delayed due to setInterval. This is not the case if I simply call updatePassword on its own.
The CSS transition only animates once. I would like to reset the animation every time i call updatePassword.
I came across a few jQuery solutions for my problem, but I am not very interested in those, as I want to rely on standard JavaScript as much as possible. However, I am okay with alternative CSS tools like keyframes, which seem to work well:
#countdown-fill {
width: 100%;
height: 100%;
animation: refresh 5s infinite;
background-color: #1e87f0;
}
#keyframes refresh {
from {
width: 100%;
}
to {
width: 0;
}
}
Although, I do worry about synchronization issues as the animation is not coupled with updatePassword in any way.
Question: Is there a way to have updatePassword reset the animation each time I call the function, and remove the initial delay?
JSFiddle: https://jsfiddle.net/MajesticPixel/fxkng013/
I've modified your JSFiddle, here's the explanation.
#countdown-fill {
width: 100%;
height: 100%;
transform-origin: left;
background-color: #1e87f0;
}
.reset {
transition: transform 5s linear;
transform: scaleX(0);
}
The trick is to bind the transition to a class, and when you want to reset it you just remove the class (reset the transition to the initial status) and add it again (restart it).
But there are a few gotchas: the most important is that instantly removing and adding the class will be optimized by the browser, which will just merge the actions and no transition at all will happen. The trick is to wrap the calls in a nested rAF call, which will force the browser to execute, render, and then execute again.
window.requestAnimationFrame(function() {
document.getElementById("countdown-fill").classList.remove('reset');
window.requestAnimationFrame(function() {
document.getElementById("countdown-fill").classList.add('reset');
});
});
The second is related to transitions: to optimize browser rendering, avoid transitioning properties like width or height, and try to limit to transforms and opacity. I've changed your width transition into a transform transition: same effect, more performance.
I second what NevNein has posted, and would also like to add that if you want to couple the transition with updatePassword so that they have a linked relationship and not just matched timeouts, you should replace setInterval(updatePassword, 5000) with:
updatePassword();
document.getElementById('countdown-fill').addEventListener("transitionend", updatePassword)
The countdown and password change will now run at any speed you set in the CSS.

How do I prevent scroll back up with JavaScript or jQuery?

I have a webpage where there is a full height intro image. Underneath this image is the main body of the site with a regular site header at the top, I'm trying to create an effect where once the user scrolls down to the site header, they cannot scroll back up to view the intro image.
CSS Classes:
Main Intro Image: .cq-fullscreen-intro
Site Header: .nav-down
I had a poke around on StackOverflow but I can't find anything that addresses this circumstance, can anyone point me in the right direction to achieve this using jQuery?
you can use JQuery scrollTop function like this
$(function() {
$(window).scroll(function() {
var scroll = $(window).scrollTop();
// set the height in pixels
if (scroll >= 200) {
// after the scroll is greater than height then you can remove it or hide it
$(".intro-image").hide();
}
});
});
So instead of scrolling, I personally think it would be better to have it be actionable. Forcing the user to manually do the transition (and all in between states) is a bad idea. If the user scrolls half way, and see's something actionable (menu, button, input field) is it usable? If it is, what happens if they submit... very awkward. If it isn't usable, how do they know when it is? How do they know it's because they haven't scrolled all the way. It's very poor user experience.
In the following example, I've created a pseudo-screenport for you to see what's actually going on. The .body container in your real site would be the body element.
Code Pen Example
$(document).ready(function(){
$('.splash-screen').on('click', function(){
$('.splash-screen').addClass("is-hidden");
});
})
html, body{
background: #eee;
margin: 0;
padding: 0;
}
.flex-root {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
.web-container {
width: 640px;
height: 480px;
background: #fff;
}
.body {
font-size: 0; // this is only to prevent spacing between img placholders
position: relative;
}
.splash-screen{
position: absolute;
transition: transform 1s ease-in-out;
}
.splash-screen .fa {
position: absolute;
z-index: 1;
font-size: 24px;
color: #fff;
left: 50%;
bottom: 15px;
}
.splash-screen.is-hidden {
transform: translateY(-110%);
transition: transform 1s ease-in-out;
}
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="flex-root">
<div class="web-container">
<div class="body">
<div class="splash-screen">
<img src="https://via.placeholder.com/640x480?text=Splash+Screen"/>
<i class="fa fa-chevron-circle-up"></i>
</div>
<img src="https://via.placeholder.com/640x60/cbcbcb?text=Menu"/>
<img src="https://via.placeholder.com/640x420/dddddd?text=Site Body"/>
<div>
</div>
</div>
While its not direclty preventing you from scrolling up and its not jQuery, I would suggest to remove/hide the element once its out of view.
You could get the current scroll position, relative to the top of the page, and check if its greater than the elements height:
const target = document.getElementById('my-target')
const targetHeight = target.getBoundingClientRect().height
const scrollEventListener = () => {
if (
document.body.scrollTop > targetHeight ||
document.documentElement.scrollTop > targetHeight
) {
target.remove()
window.removeEventListener('scroll', scrollEventListener)
document.documentElement.scrollTop = 0
}
}
window.addEventListener('scroll', scrollEventListener)
Here is a codepen https://codepen.io/bluebrown/full/aboagov

JavaScript on scroll to shrink logo AND menu bar

I'm working on a WordPress site, I have used the following JavaScript code to shrink the logo on scroll:
logo has id #logoid
CSS
.logoclass {width:100%;
transition: width 0.5s linear;}
.scroll {margin-top:-10px;
width:55%;
transition: width 0.5s linear;}
Javascript
<script type="text/javascript">
window.onscroll = () => {
const nav = document.querySelector('#logoid');
if(this.scrollY <= 250) nav.className = 'logoclass'; else nav.className =
'scroll';};
</script>
Now this works fine to simply shrink the image and restore size.
Now I have two problems:
Since I'm using WordPress plugins, there are many attributes applied
to the logo internally and are not in my .logoclass or in the
.scroll so these attributes get removed once I scroll and do not
get applied again. Is there a way to :
a) On scroll down ONLY change size while keeping other attributes
intact
b) On scroll up revert to initial settings completely (remove new
class)
My second question is, I want to also modify the menu bar size on scroll, but I cannot use the same code twice because it seems to only accept the code written last. Possibly because windows.onscroll gets added twice. Any way to incorporate both?
For #1, you should use the classList property to add or remove classes.
For #2, you should be able to add whatever changes you want in the same if statement.
window.onscroll = () => {
const nav = document.querySelector('#logoid');
const menu = document.querySelector('#menubar');
if (this.scrollY <= 250) {
nav.classList.remove('scroll');
menu.classList.remove('someclass');
} else {
nav.classList.add('scroll');
menu.classList.add('someclass');
}
};
.logoclass {
width: 100%;
transition: width 0.5s linear;
}
.scroll {
margin-top: -10px;
width: 55%;
transition: width 0.5s linear;
}
body {
height: 1000px;
}
#logoid {
position: absolute;
top: 0px;
background: red;
height: 25px;
}
<div id="logoid" class="logoclass"></div>
<div id="menubar" class="menuclass"></div>
If you add a global scroll class to your body tag you won't have to change your JavaScript if you want to change more things on scroll, only your CSS.
window.onscroll = () => {
const body = document.querySelector('body');
if (this.scrollY <= 250) {
body.classList.remove('scroll');
} else {
body.classList.add('scroll');
}
};
.logoclass {
width: 100%;
transition: width 0.5s linear;
}
.scroll .logoclass, .scroll .menuclass {
margin-top: -10px;
width: 55%;
transition: width 0.5s linear;
}
body {
height: 1000px;
}
#logoid {
position: absolute;
top: 0px;
background: red;
height: 25px;
}
<div id="logoid" class="logoclass"></div>
<div id="menubar" class="menuclass"></div>
If you wan't to run multiple functions on scroll you should use addEventListener.
window.addEventListener('scroll', function() {
doSomething();
});
window.addEventListener('scroll', function() {
doSomeOtherThing();
});

Animation not easing

i have some problem with my transitioning. here is the javascript/jquery
function moveProgressBar(v, a) {
var getPercent = v / 100;
var getProgressWrapWidth = $('.progress-wrap').width();
var progressTotal = getPercent * getProgressWrapWidth;
var animationLength = a;
$('.progress-bar').stop().animate({
left: progressTotal
}, animationLength, function(){
if (getPercent === 1) {
$('.progress').css('height','auto');
$('.progress_checkout').text('Proceed to checkout!');
} else {
$('.progress').css('height','2rem');
$('.progress_checkout').text('');
}
});
}
.progress_checkout{
text-align: center;
margin: auto 0;
display: block;
color: #fff;
font-weight: bold;
padding: 2rem 0;
transition: ease-in-out 0.6s;
font-size: 200%;
}
.progress_checkout:hover{
background-color: white;
color: #C6DA80;
cursor: pointer;
}
.progress {
width: 100%;
height: 2rem;
}
.progress-wrap {
background: #C6DA80;
margin: 20px 0;
overflow: hidden;
position: relative;
}
.progress-bar {
background: #F5F5F5;
left: 0;
position: absolute;
top: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="progress-wrap progress" data-progress-percent="0">
<a class="progress progress_checkout"></a>
<div class="progress-bar progress"></div>
</div>
What i want to do is that when this progress bar is full display the text and make the bar bigger. It does that but the animation is instant instead of over 0.5s or so. I have tried it with addClass and removeClass and it does exactly the same. I've even added transition on ever element that it has possible contact with and it will still be instant.
NOTE: If something seems missing please let me know because i might
have not pasted all the code. Though as far as I'm concerned this
should be everything related too the animations
jQuery's animate uses it's own easing parameter. Unfortunately, only swing and linear are available
The only easing implementations in the jQuery library are the default, called swing, and one that progresses at a constant pace, called linear. More easing functions are available with the use of plug-ins, most notably the jQuery UI suite.
Documentation.
You have two options.
The first is CSS3 Animations, with which you can time and combine multiple animations. So I would suggest switching back to classes and using CSS.
The second is using jQuery UI, which has the following list of easing options:
linear
swing
_default
easeInQuad
easeOutQuad
easeInOutQuad
easeInCubic
easeOutCubic
easeInOutCubic
easeInQuart
easeOutQuart
easeInOutQuart
easeInQuint
easeOutQuint
easeInOutQuint
easeInExpo
easeOutExpo
easeInOutExpo
easeInSine
easeOutSine
easeInOutSine
easeInCirc
easeOutCirc
easeInOutCirc
easeInElastic
easeOutElastic
easeInOutElastic
easeInBack
easeOutBack
easeInOutBack
easeInBounce
easeOutBounce
easeInOutBounce
What you choose or prefer is up to you.
Thanks for the help but this ended up being my fix. Using opacity and having the a tag contain " " avoided the sudden jump from the text insert making the transition smooth.
if (getPercent === 1) {
$('.progress').animate({height: "4rem"}, 1000);
$('.progress_checkout').text('Proceed to checkout!');
$('.progress_checkout').animate({opacity: 1}, 800);
} else {
$('.progress').animate({height: "2rem"}, 1000);
$('.progress_checkout').text(' ');
$('.progress_checkout').animate({opacity: 0}, 800);
}

Overflow hidden for text in css

I have an element with image and text,
Fiddle. Note: Resize preview enough to make grid big enough.
Here is my CSS:
.gridster .gs-w .item{
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
.gridster .gs-w .item .obj{
background-color: #00A9EC;
}
.gridster .gs-w .item .itemIcon {
height: 100%;
width: 100%;
float:left;
overflow: hidden;
z-index: 10;
}
.gridster .gs-w .item .itemIcon {
background-image: url(http://icons.iconarchive.com/icons/dakirby309/windows-8-metro/256/Apps-Calendar-Metro-icon.png);
background-repeat:no-repeat;
background-size:contain;
align-content: center;
}
.gridster .gs-w .item .itemText{
display: block;
width: 100%;
position: relative;
margin-right: 0px;
right: 0px;
text-align: right;
z-index: 9;
}
.gridster .gs-w .item .itemText a{
vertical-align: center;
text-align:right;
color:white;
padding-right: 10%;
font-size: 14px;
font-weight: 600;
text-decoration:none;
font-family: 'Segoe UI';
}
I want to show text when element is expanded, and hide when element is collapsed, I think I can achieve it by CSS, but it's not yet clear what is wrong.
and here it is collapsed
advise some CSS code, in case if possible to make in CSS.
You can hook into resize.resize.
By checking data attribute data-sizex you get how many columns the cell spans. By this you can expand the init function to the following:
Sample fiddle.
public.init = function (elem) {
container = elem;
// Initialize gridster and get API reference.
gridster = $(SELECTOR, elem).gridster({
shift_larger_widgets_down: true,
resize: {
enabled: true,
resize: function (e, ui, $widget) {
var cap = $widget.find('.itemText');
// Hide itemText if cell-span is 1
if ($widget.attr('data-sizex') == 1) {
cap.hide();
} else {
cap.show();
}
}
}
}).data('gridster');
hookWidgetResizer();
}
Or cleaner, and likely preferable. Split it out to own function and say something like:
resize: capHide
Sample fiddle.
If you rather go for the solution proposed by your updated images, one way is to tweak the CSS on resize, using your resize_widget_dimensions function. Sure this can be done better, but as a starter you can have this:
Sample fiddle.
this.$widgets.each($.proxy(function (i, widget) {
var $widget = $(widget);
var data = serializedGrid[i];
this.resize_widget($widget, data.size_x, data.size_y);
// Find itemText
var $it = $widget.find('.itemText');
// Set CSS values.
$it.css({width:this.min_widget_width, left:this.min_widget_width});
}, this));
Challenge is that the gridster is a very fluid cake where a lot of the dimensions and positioning is done by JavaScript rather then pure CSS. Anyhow, the above should give a direction on how to tweak it, and might even be good enough ;)
As a final treat you can resize the font according to cell size. I'm not sure how to best find the size you want as you divide the space between icon/image and text. But something like this:
Sample fiddle.
Where you have a hidden span to measure text:
<span id="font_sizer"></span>
With CSS:
#font_sizer {
position: absolute;
font-family:'Segoe UI';
visibility: hidden;
}
And font measure by:
function szFont(w, t) {
var s = 1, $fz = $('#font_sizer');
$fz.text(t);
$fz.css('fontSize', s + 'px');
while ($fz.width() < w - 2)
$fz.css('fontSize', ++s + 'px');
return s;
}
You can set font size as:
var fontSize = szFont(this.min_widget_width - 10, 'Objects');
Where this.min_widget_width - 10 is the part where you set size available for text. Then you can say:
var $it = $widget.find('.itemText');
$it.css({fontSize: fontSize + 'px', width:this.min_widget_width, left:this.min_widget_width});
Other notes:
You have a typo in:
var container,
grister, // <<-- Missing 'd' in gridster
resizeTimer;
In extensions you have
var data = serializedGrid[i];
this.resize_widget($widget, data.sizex, data.sizey);
however a console.log of data show:
data.size_x
data.size_y
not sure how this fits in. The data attribute uses sizex / y but data property from serialize, (on object), it uses size_x / y with underscore.
I think you are looking for media query:
#media all and (max-width: 760px) {
.gridster .gs-w .item .itemText {
display: none;
}
}
Example
You can hide text by using below type of CSS
.gridster .gs-w .item .itemText a.hide-text {
text-align: left;
text-indent: -99999px;
display: inline-block;
}
now whenever you want to hide text you need to add this class i.e. hide-text on anchor element Objects and vice versa to show text remove class
basically you need to try and figure out best possible solution to fit all requirements Good luck

Categories