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
Related
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
I'm trying to set a transition-delay to the overflow property of body when a div is clicked by adding a class to the body as follows:
$("div").click(function(){
$("body").addClass("no_overflow");
});
div{
background:lime;
height:2000px;
}
.no_overflow{
overflow:hidden;
}
body{
overflow:auto;
transition: overflow 0 2s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>I'm div</div>
However, this doesn't seem to work (there's no delay). Am I doing anything wrong here?
I know this can be achieved by using setTimeout function, but was wondering why can't this be achieved using css transitions? Are there any specific style properties to which css transitions can be applied?
There are many properties that can't be transitioned. overflow is among them; the render engine has no idea how to transition between "hidden" and "shown", because those are binary options, not intervals. This is the same reason why you can't transition between display: none; and display: block; (for example): there are no in-between phases to use as transitions.
You can see a list of properties you can animate here on Mozilla Developer Network.
You can simulate a delay with animation:
$("div").click(function() {
$("body").addClass("no_overflow");
});
div {
background: lime;
height: 2000px;
}
.no_overflow {
overflow: hidden;
/* persist overflow value from animation */
animation: 7s delay-overflow;
}
body {
overflow: auto;
}
#keyframes delay-overflow {
from { overflow: auto; }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>I'm div</div>
You'll have to apply a separate animation to .body if you want a delay on removeClass, and also to take care that the two animations don't overlap or they'll cancel each other out.
overflow isn't CSS animatable property. You can see full list of animatable CSS properties there.
In case someone is looking at the answer, like I was, for a way to animate the cropping of an element which requires overflowing - here is the solution that worked for me: the clip-path css property which is animatable and very versatile.
Here is a cool tool to play around with, in order to get the proper start / end values for an animation: https://bennettfeely.com/clippy/.
Dmitry's answer should be the only accepted answer, as it is a pure CSS solution applying delay to "non-animatable" properties. However it's worth to mention, that the CSS rule applying animation should be "triggerable" each time when it is needed.
For instance, the following code does not work:
#keyframes show-overflow {
from { overflow: hidden; }
}
.hideable, .overlay {
font-size: 36px;
height: 50px;
}
.hideable {
transition: height 2s;
overflow: visible;
animation: show-overflow 2s; /* this line should be in separate "triggerable" CSS rule to work */
}
.hideable.hidden {
height: 0;
overflow: hidden;
}
<button onclick="document.getElementById('hideable').classList.toggle('hidden')">
Clik HERE to hide/show the text below
</button>
<div id='hideable' class='hideable'>
This is the text to hide and show.
</div>
<div class='overlay'>
This is overlaying text
</div>
But after moving the marked property to a separate CSS rule, everything works as expected:
#keyframes show-overflow {
from { overflow: hidden; }
}
.hideable, .overlay {
font-size: 36px;
height: 50px;
}
.hideable {
transition: height 2s;
overflow: visible;
}
.hideable:not(.hidden) {
animation: show-overflow 2s; /* now this works! */
}
.hideable.hidden {
height: 0;
overflow: hidden;
}
<button onclick="document.getElementById('hideable').classList.toggle('hidden')">
Clik HERE to hide/show the text below
</button>
<div id='hideable' class='hideable'>
This is the text to hide and show.
</div>
<div class='overlay'>
This is overlaying text
</div>
It makes sense that you can't transition between binary attributes for example overflow: hidden; and overflow: visible but it would have been really nice if instead of "transitioning" then it would be like (in js pseudo code:
setTimeout("applyOverflowVisible()", transitionTime);
But of course you can do this yourself in JavaScript but then you are splitting the code between places and it can make it difficult to understand by someone else. I guess using things like React helps but even there I would want to avoid mixing css into the js.
I have a scenario in which I have a team page with pictures and some blurb. Under each picture I have social media links much like the following:
These are images that sit within a horizontal list underneath each item using the below base markup.
<ul>
<li>
<a><img src=""/></a>
</li>
<li>
<a><img src=""/></a>
</li>
</ul>
At the moment these are images but I would very much like if when hovered the grey inards of these images turned blue.
I was thinking just have a span with a background image like this:
<a><span class="linkedin"></span></a>
.linkedin{
height:28px;
width:auto;
background-image:url(link/to/the/linkedin/picture)
}
.linkedin:hover{
height:28px;
width:auto;
background-image:url(link/to/the/linkedin/picture-blue-version)
}
However, when I attempted this the space was empty instead of taking the size of the image.
If I enter as content I get a small part of the background image, furthermore giving the class an absolute position takes it out of document flos
Is this the ideal approach?
The problem is if you use a <span> element you need to set it to display: inline-block and you need to set a width for the image to show up. Then it works, here is a demo:
.linkedin {
display: inline-block;
width: 140px;
height:100px;
background-image:url(http://ipsumimage.appspot.com/140x100,ff7700)
}
.linkedin:hover {
background-image:url(http://ipsumimage.appspot.com/140x100,0000FF)
}
<span class="linkedin"></span>
As you see on the first :hover it flickers. Cause it will not load the image bevore you :hover the first time. This is why you should consider another solution. Like mentioned in some comments you could use http://fontawesome.io/icons/ and then just change the color.
To prevent flickering you could do the same with using <img> tags then the source will be loaded and ready to be shown on :hover. But it works best with also setting positions, demo like so:
a.special {
position: relative;
}
a.special img {
visibility: hidden;
position: absolute;
top: 0;
left: 0;
}
a.special img:first-child {
visibility: visible;
}
a.special:hover img:first-child {
visibility: hidden;
}
a.special:hover img:last-child {
visibility: visible;
}
<a class="special" href="#">
<img src="http://ipsumimage.appspot.com/140x100,ff7700">
<img src="http://ipsumimage.appspot.com/140x100,0000FF">
</a>
Best approach for this is to use SVG's and change the fill of the SVG on hover.
Your approach should work however, it might be that you've not got the correct size image? try 'background-size: cover;' Or that the element has no width. Try setting a width on the span too. (don't forget to give it 'display: inline-block;' too.
Ed: checkout https://css-tricks.com/lodge/svg/
Font-Awesome is a great idea for what you're trying to achieve. Takes less data to load the page too if you can get away with using text in place of images.
By the way, when using the :hover property there is no need to redefine the width and height of the image... Just redefine the changes you'd like to make.
I'm trying to fade a Modal in when it's clicked, and have the experience be smooth on mobile devices.
I'm setting both opacity to 0 and display to none. Setting opacity alone isn't enough, as it makes the area underneath unclickable.
#Modal {
display: none;
opacity: 0;
transition: opacity 500ms ease 0s;
}
Fade in Code:
$('#Modal').show();
$('#Modal').css('opacity','100');
However, the Modal doesn't fade in, it simply pops into existence.
Setting a setTimeout here works, but who wants a click delay for the fade in?
What's the best way to fade an element in with an opacity transition without chaining together massive properties like z-index, or some such nonsense?
Toogling display property it's bad way for fade element, Similar topics were already processed e.g: CSS3 transition doesn't work with display property
"display:none; removes a block from the page as if it were never there. A block cannot be partially displayed; it’s either there or it’s not. The same is true for visibility; you can’t expect a block to be half hidden which, by definition, would be visible! Fortunately, you can use opacity for fading effects instead."
quotation author:
Hashem Qolami
You should try to do this by deelay like here Animating from “display: block” to “display: none”
or try toogling class like here: http://jsfiddle.net/eJsZx/19/
CSS:
.Modal {
display: block;
opacity: 0;
transition: all 300ms ease 0s;
height: 0px;
overflow: hidden;
}
.ModalVisible {
display: block;
opacity: 1;
height: 50px;
}
Jquery:
$('button').on('click', function () {
$('#ModalId').addClass('ModalVisible');
});
Html:
<div id='ModalId' class="Modal" > content <br> content </div>
<button>show</button>
Why don't you use jQuery's $("selector").fadeIn() method?
The supposedly correct answer above implies that the OP is attempting a transition on display. They are not. Calling show() will set the display property to block. Then setting the opacity should theoretically trigger the transition from opacity:0.
A similar question has been answered here. To quote #WhoTheHellIsThat, the reason the transition is not triggered is...
...because of the way styles are figured out. Style changes are
expensive so they are effectively saved up until they are needed (a
recalc check like .offsetHeight is called or the next frame needs to
be drawn).
However the answer code in that question was Vanilla Javascript, and I couldn't make it work in jQuery. I found another answer that solved it in jQuery, using a class to trigger the transition.
Here is the full CSS...
#Modal {
display: none;
opacity: 0;
transition: opacity 500ms ease 0s;
}
#Modal.fade-in {
opacity: 1;
}
And here is the full JS:
$('#Modal').show(0, function() {
$(this).addClass('fade-in');
});
Here is a fiddle from RoryMcRossan's answer, demonstrating the solution.
Check this code: http://jsfiddle.net/j7C5B/
This is the html
<div ng-app="myApp">
<div class="searchContainer" ng-controller="SearchCtrl">
<ul>
<li class="advSearchRow" ng-show="advSearch">
<div class="todayButton">Today</div>
<div class="tomorrowButton">Tomorrow</div>
<div class="calendarButton">?</div>
</li>
<li><div class="advSearchButton" ng-click="display()">Advance Search</div></li>
</ul>
</div>
It works perfectly with Chrome and Safari but it has a weird behavior in Firefox, anyone has an idea what is wrong with it? I have been trying to only leave height, line-height and different changes with no result yet. Thanks
Edit: It's very different from what I thought, and is rather simple. Firefox requires line-height to have a unit, while Webkit browsers accept unit-less value and interpret as em. To fix it you just have to add em to the transition (or px, doesn't matter since it is 0, but there must be one unit). See http://jsfiddle.net/78rvg/
.advSearchRow.ng-hide
{
line-height: 0em;
height: 0;
opacity: 0;
margin-bottom: 0;
}
The thing is: Your code assumes that the height of the buttons (.todayButton, .tomorrorButton...) to be the height of their container (.advSearchRow); which is not true in the case of floating elements (that's why we need clearfix).
You can fix this by specify the assumption explicitly for the buttons:
.advSearchRow > div { height: 100% }
or use overflow: hidden:
.advSearchRow { overflow: hidden }