Animate width from auto to 100%? - html

I have this div
<div id="view_album">
View Photo
</div>
And a css
#view_album {
text-align: right;
width: auto;
display: inline-block;
background: #005cb9;
color: white;
box-sizing: border-box;
padding: 10px;
-webkit-transition: all .5s ease-in-out;
-moz-transition: all .5s ease-in-out;
-o-transition: all .5s ease-in-out;
-ms-transition: all .5s ease-in-out;
transition: all .5s ease-in-out;
}
#view_album:hover {
width: 100%;
}
Is it possible to animate the width from auto to something? A final width of 100% isn't working but if I set the initial width to a specific value like 100px and so, it works. But I need it auto since the text inside the div, 'View Photo', can be other text, too like something longer than that.
On this site I'm working on:
As you can see, that div with 'View Album' has a specific width of 115px. Then animate to 100%. But if I change the text inside like, 'Abc', which is obviously shorter than View Album and does not need a 115px of width.
That's why I need it auto.

First, change ids to classes, since you are repeating same id for all album covers.
You can use auto width and after site is loaded, run jQuery script and add current width to css:
$(document).ready(function(){
$.each($('.view_album'), function(){
$(this).css('width', $(this).width() + 'px');
}
});

I don't think CSS can animate/do transitioning in the way you want. You have to use script to set the initial width explicitly. However we have to know the width which is calculated by the browser (from the style width:auto after the element is rendered), this can be done easily using getComputedStyle or simply by the css method in jQuery. The important thing here is you have to modify the style via the CSSRule, not via the style property of the element. I'm not sure why it is but looks like that's how CSS works. Here is the script to find the rule #view_album and set the width to a specific value so that the hovering can make it transition well:
var ss = document.styleSheets;
for(var i = 0; i < ss.length; i++){
var rules = ss[i].cssRules || ss[i].rules;
var br = false;
for(var j = 0; j < rules.length; j++) {
if(rules[j].selectorText == '#view_album'){
rules[j].style.width = $('#view_album').css('width');
br = true;
break;
}
}
if(br) break;
}
Demo.
NOTE: Doing so won't set the width:auto on your element any more, so you should not change the text of that element dynamically (I think this is not your use case). If you have to change it dynamically and the width should expand/collapse automatically, you can try setting width:auto on it first before setting the text/innerHTML.
Here is Another version allowing you to change the font-size of the #view_album so that the width is adjusted accordingly.

Related

How to set transition animation for content to appear?

there is an example with content dropdown: https://codesandbox.io/s/expand-content-4pc09c?fil...
But the appearance of content on the button is quite fast. I need to set smoothness using the transition property, but it didn't work.
As far as I understand, you need to add some kind of appearance effect with the help of visability, but here there is a link to useState also tried to set a property for the content: transition: all 0.5s ease-out; but the animation is not happening
I also tried change styles like that:
const styles = {
height: expand ? "auto" : "0px",
maxHeight: expand ? "auto" : "0px",
overflow: expand ? "visible" : "hidden"
};
but it turns out that i have the same result
You have to keep on mind various things to make it works. In your codepen there's a typo in visibility animation in styles.css. I attached you a working snippet but I'm explaining you some important topics about how it works.
Explanation
First of all if you're toggling styles with you styles variable it'll no respect transition as it will be rendered as inline styles, so the approach I use was create a new .wrapper__content.expanded class and the class is the one that'll be toggled with the state.
Second about CSS properties:
visibility cannot be transitioned as it doesn't have 'in-between' values, when the time of the transition (let's suppose 300ms) had passed it will change to visible or hidden with no intermediate values, that's why it cannot be 'animated'. But if you still want to add this property for accessibility purposes you can add a transition-delay to this property timing to trigger the change when height transition had finish.
height property is a difficult one to animate because it needs explicit values to work and I recommend you not to change the height: auto that comes by default, as it'll be adapting to its content (avoiding overflow issues). Instead of transitioning this property you should use max-height in collapsed state with a value of 0 and in expanded state with a value that your content will never reach (1000px in my snippet). This will do the trick, don't forget to add overflow:hidden; to hide the content when it's collapsed.
I think this answer will fulfill you requirements, but any questions feel free to ask. Hope it helps.
const App =() => {
const [expand, setExpand] = React.useState(false);
const onToggle = () => {
setExpand(!expand);
};
return (
<div className="wrapper">
<div className="wrapper__expand">
<button onClick={onToggle} className="wrapper__expand-btn">
+
</button>
Expand
</div>
<div className={expand ? 'wrapper__content expanded' : 'wrapper__content'}>
expanded content
</div>
</div>
);
}
ReactDOM.render( < App / > , document.getElementById("root"));
.App {
font-family: sans-serif;
text-align: center;
}
.wrapper {
display: flex;
flex-direction: column;
}
.wrapper__expand {
display: flex;
column-gap: 10px;
}
.wrapper__content {
margin-top: 10px;
overflow: hidden;
max-height: 0;
transition: max-height .5s ease-in-out;
}
.wrapper__content.expanded {
max-height: 1000px;
}
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
We can really check transition using height property and somehow control the distance like that
transition: max-height .5s ease-in-out;
Сomment below solves my problem

Table width css transition because of row delete: does not fire

After a row is deleted from a table, its width may change. However, the table's width transition does not fire, probably because the width css property was never changed directly, only its calculated value.
How do I make a table smoothly animate its width after a row is deleted, even if its css style width has not changed?
Example:
<style>
table {
transition: all 500ms ease-in-out;
}
</style>
<script>
function test(button) {
var el=document.getElementById("to-delete");
el.parentNode.removeChild(el);
button.parentNode.removeChild(button);
}
</script>
<table>
<tr><td>id</td><td>type</td></tr>
<tr id="to-delete"><td>165495</td><td>user</td></tr>
<tr><td>12</td><td>user</td></tr>
</table>
<button onclick="test(this)">Test</test>
The same moment the row is deleted, the table width abruptly changes its width to the final value, despite the css transition:
table {
transition: all 500ms ease-in-out;
}
Is there any way to make the table obey the rule in this case?
It sounds like you want to animate the width of every column, and the height of the deleted row, to get a smooth effect all-around. CSS doesn't have an explicit command for this purpose, but it does have...
Animating the font size
If you can just get all the contents of the row to shrink away smoothly, then the browser's regular table handling will do what you're looking for.
<style>
td {
transition: font-size 500ms ease-in-out, padding 500ms ease-in-out;
}
.deleting > td {
font-size: 0;
padding: 0;
border-width: 0;
color: transparent;
}
</style>
<script>
function test(button) {
var el=document.getElementById("to-delete");
el.className = "deleting";
window.setTimeout(function() {
el.parentNode.removeChild(el);
}, 1000);
button.parentNode.removeChild(button);
}
</script>
<table>
<tr><td>id</td><td>type</td></tr>
<tr id="to-delete"><td>165495</td><td>user</td></tr>
<tr><td>12</td><td>user</td></tr>
</table>
<button onclick="test(this)">Test</test>
For the simple example in the question, animating the font size gets very close, and animating the padding gets the rest of the way. If you have more complex contents in your table cells (that aren't sized entirely in terms of ems), you may have to do something more complicated, such as replacing the contents with a div of the same size and then shrinking that div to nothing via the height/width properties.

The "hover" not work properly

I have a problem with div "transition"\"hover" at my project :
Comverese.
At the section "SUCCESS STORIES" . when you hover the div's , it go up this o.k ,but not stay up when you move the mouse in that div.
What is the problem?
.story1{
position: absolute;
width: 22rem;
height: 12rem;
z-index: 9;
background: white;
padding: 1rem;
-webkit-transition:transform .6s ease-out;
-moz-transition:transform .6s ease-out;
-ms-transition:transform .6s ease-out;
-o-transition:transform .6s ease-out;
transition:transform .6s ease-out;
}
.story1:hover{
-webkit-transform:translate(0px,-200px);
-moz-transform:translate(0px,-200px);
-ms-transform:translate(0px,-200px);
-o-transform:translate(0px,-200px);
transform:translate(0px,-200px);
}
The problem is that your div is not hovered anymore after it translates. The div moved up, and you're now hovering the div below (namely the .quote div) -> the story div comes back down.
One way to fix this is to use a bit of javascript instead of solely relying upon CSS. Adding a specific class on mouseenter and removing it when you're leaving the div below.
Something like the following (note that the JavaScript is a bit simple and should be improved upon to work for every story).
CSS:
.story1.translated {
-webkit-transform:translate(0px,-200px);
-moz-transform:translate(0px,-200px);
-ms-transform:translate(0px,-200px);
-o-transform:translate(0px,-200px);
transform:translate(0px,-200px);
}
JS :
var story1 = document.getElementsByClassName("story1")[0];
var blockquote = document.getElementsByClassName("quote")[0];
story1.addEventListener("mouseenter", function () {
story1.className += " translated";
}
blockquote.addEventListener("mouseleave", function () {
story1.className = story1.className.replace(" translated", "");
}
Edit
A pure CSS solution would be to move the :hover part to .story and style .story1 accordingly when it occurs. As follow:
.story:hover .story1{
-webkit-transform:translate(0px,-200px);
-moz-transform:translate(0px,-200px);
-ms-transform:translate(0px,-200px);
-o-transform:translate(0px,-200px);
transform:translate(0px,-200px);
}
This seems to work just fine when I change it locally to try it on your site.
And the same change can be applied to .float-icon:hover as well. Changing it to .story-icon:hover .float-icon works wonders

CSS Transitions On Computed Styles

Is it possible to transition css on what the computed style becomes, not what that style is explicity set to?
Example
I have the following CSS/HTML
.grow {
transition: height 1s ease-in-out;
}
.hidden {
display: none;
}
<ul>
<li>
<div class="grow">
<ul class="hidden">...</ul>
</div>
</li>
</ul>
Then if I use javascript to remove the hidden class the div will logically grow to fit the newly displayed content, however since the height property did not change, the transition does not take effect. Is there a way to overcome this behavior? If it is possible, I'd like to keep the solution to css.
You are talking about two different CSS properties: height and display. Your transition only applies to height property and, even with display: none; the element keeps the same height.
What you can do, actually, is something like this:
setTimeout(function() {
document.getElementById("hid").className = "";
}, 1000);
.grow div {
transition: height 3s ease-in-out;
display: block;
height: 20px;
overflow: hidden;
}
.grow .hidden {
height: 0;
}
<ul>
<li>
<div class="grow">
<div class="hidden" id="hid">.AaA..</div>
</div>
</li>
</ul>
Hope it helps you.
As Rolyataylor2 mentioned, you cannot animate automatic dimensions. I've encounted this problem before and have a relatively simple JS-based solution.
Check out this Codepen. It uses a small jQuery plugin that I wrote to set the height of the variable-height container to its calculated height based on the heights of its children. It actually clones the element, measures the height, destroys the clone, and explicitly sets the calculated height on the original element.
This is the full code for the plugin and here's the Gist. It's written in Coffeescript but I can convert it to JS if needed. Cheers!
$.fn.extend
setContentHeight: ->
return #each ->
$(#).css('height', $(#).getContentHeight())
getContentHeight: ->
elem = $(#).clone().css(
"height":"auto"
"display":"block"
).appendTo($(#).parent())
height = elem.css("height")
elem.remove()
return height

In pure CSS, is it possible to set margin equal to height of current element?

I'm have a vertical stack of items to which the user can append one by clicking a button, roughly like this.
<ol>
<li><textarea></textarea></li>
<li><textarea></textarea></li>
</ol>
<a data-action="additem">Add another</a>
I'm trying to write a CSS animation so that when the new li is inserted, the "Add another" smoothly slides down to its new resting place. Fixed height on the li tags is not an option, and I'm trying to avoid using the max-height animation hack because it can have weird layout effects.
I figured out that I could animate margin-bottom from something to 0 and have the desired effect, but I can't figure out how in CSS to express that I want the current height of the element to which this rule is applied. Percentages are measured relative to the width of the element, which isn't what I need here, and I can't think of a clever trick using calc or the like to express what I want to the browser.
Suggestions?
EDIT
I'm using a template with a repeat binding to add the items to the list. The JS only pushes another object into an observable array, and the framework handles the actual DOM insertion. The li tag has on it the following CSS to get it to enter smoothly:
animation: append forwards .5s;
And append is defined as:
#keyframes append {
from {
transform: translateX(10%);
opacity: 0;
margin-bottom: _____;
}
to {
transform: none;
opacity: 1;
margin-bottom: 0;
}
}
Not currently...
I've come up against this frustrating issue a number of times, always trying to either animate a non-numeric value, access a specific property of the current element as an animation value, or animate an unspecified value to a specified one. Generally I always have to fall back to either some form of not-quite-perfect max-height animation (like you've already mentioned) or use a mixture of CSS and JavaScript/jQuery.
For your issue there are a few options, but none are exactly what you're after.
css only version (using duplicated markup and another animation)
http://jsfiddle.net/7m8F9/2/
http://jsfiddle.net/7m8F9/3/ <-- improved version using bottom and position:relative
http://jsfiddle.net/7m8F9/5/ <-- even better version, going back to translateY
One trick often used with CSS-only hacks, is to duplicate markup — in this instance, the link iteself — and place it within parent wrappers that will be turned on or off by different means. The downsides to this method are that you get a rather ugly markup, and in this particular instance a bullet-number that appears jarringly (because of having to move the opacity animation from the li to the textarea).
The benefits of this method however are that by moving the link inside the li you can use -100% on the y-axis with a translate, or another offset method. Oddly though I can't work out what translateY(-100%) is calculating based upon... it doesn't seem to be the parent height, perhaps it is the height of itself. For this reason I've updated the fiddle to use bottom and relative positioning instead, although in Firefox (on mac) this glitches briefly.
It does seem to be that translateY is calculating percentage based on it's own height, so in order to get around this problem I've had to make use of position absolute and force the the link layer to assume the same dimensions as the li... annoying, as it involves z-indexing the textarea above the link, and an internal span to offset the link text, but at least it does work.
The following code works in the latest Firefox, and would work in other modern browsers if all the different browser-prefixes were correctly used to define the animation keyframes, I don't have time to set them all up right now however.
markup:
<ol class="list">
<li><textarea></textarea><a class="add" href="#"><span>Add another</span></a></li>
<li><textarea></textarea><a class="add" href="#"><span>Add another</span></a></li>
</ol>
css:
ol li {
position: relative;
}
ol li .add {
display: none;
}
ol li:last-child .add {
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
width: 100%;
height: 100%;
display: block;
animation-duration: 1s;
animation-name: slide;
}
ol li:last-child .add span {
position: absolute;
bottom: -20px;
}
.list li textarea {
position: relative;
animation-duration: 1s;
animation-name: append;
z-index: 1;
}
#keyframes append {
from {
transform: translateX(10%);
opacity: 0;
}
to {
transform: none;
opacity: 1;
}
}
#keyframes slide {
from {
transform: translateY(-100%);
}
to {
transform: none;
}
}
javascript version (code triggered translations)
http://jsfiddle.net/7m8F9/1/
The following obviously doesn't take into account the fact that you are using a template engine to power your DOM manipulations, but all the code needs to work properly is a before and after height of the list (to calculate the difference in height), and an event to trigger at the point where the new list item is added.
Sadly it is not yet possible to do this all in pure CSS, at least not as far as I have seen, perhaps once calc has leveled up...? Or perhaps if some way is introduced to reference the current elements dimensions, not just it's offset parent.
It should be noted I didn't have Internet Explorer around to test this with, but all other modern browsers seem happy.
markup:
<ol class="list">
<li><textarea></textarea></li>
<li><textarea></textarea></li>
</ol>
<div class="add">
Add another
</div>
javascript (with jQuery):
function prefix(){
for ( var a = ['Webkit','Moz','O','ms'], i=0, l = a.length; i<l; i++ ) {
if ( document.body.style[a[i]+'AnimationName'] !== undefined ) {
return { js: a[i], css: '-' + a[i].toLowerCase() + '-' };
}
}
return { css:'', js:'' };
}
$(function(){
$('.add a').click(function(e){
e.preventDefault();
var pref = prefix(),
link = $(this).parent(),
list = $('.list'),
lihi = list.height(),
liad = $('<li><textarea></textarea></li>').appendTo(list),
lihd = lihi - list.height();
link.css(pref.css + 'transform', 'translateY(' + lihd + 'px)');
setTimeout(function(){link.addClass('translate-zero transition-all');},0);
setTimeout(function(){
link.css(pref.css + 'transform', '');
link.removeClass('translate-zero transition-all');
},500);
});
});
css:
.transition-all {
-webkit-transition: all 0.5s;
-moz-transition: all 0.5s;
-ms-transition: all 0.5s;
-o-transition: all 0.5s;
transition: all 0.5s;
}
.translate-zero {
-webkit-transform: translateY(0) !important;
-moz-transform: translateY(0) !important;
-ms-transform: translateY(0) !important;
-o-transform: translateY(0) !important;
transform: translateY(0) !important;
}
.list li {
animation-duration: 1s;
animation-name: append;
}
#keyframes append {
from {
transform: translateX(10%);
opacity: 0;
}
to {
transform: none;
opacity: 1;
}
}
redesign version
A number of times I have hit a similar issue, only to find a redesign helps do away with the problem and can often actually improve usability. In your case it may be best to place the "add link" above the list (or top right), or integrate the button as a floating icon somewhere... where-ever you put it, it is best to try and keep it in a static location, moving interaction points can be annoying for users, especially if they wish to add more than one item in quick succession.
The simplest solution that i could think of is this.
When you add a new li element, just append it in the dom.
liMarkup = '<li><textarea></textarea></li>'
$('ol').append(liMarkup);
$('ol').find('li').last().css('display','none');
$('ol').find('li').last().show('fast');
This would work as per your requirement :) I hope it helps.
Working Jsfiddle
EDIT: Its easy and better to do it in JS.