I know that is not possible to transition properties like display, position, visibility but how can one add a transition effect (over opacity in my case; working either on show and hide) AND being able to put a display: none on that element?
There's no need to transition: display .., but just that after it is set to block then the transition on opacity would start.
I thought I could cheat using transition: display 0s, opacity .3s but I can't manage to make it work.
Of course I could use tricks like setting height: 0; overflow: hidden; to hide my element but in my case I also need to remove the element from the flow (the element is position: fixed) and prevent TABbing on it's descendants and focusing hidden elements.
Is it using animations the only supported alternative?
No Javascript solutions, please; I believe the presentation should be a concern of CSS.
You can probably use CSS animation #keyframes. Note, it can add appearing animation, but won't do the disappearing part, as display:none will be applied instantly.
function show() {
var el = document.getElementById("display");
el.classList.remove("hide");
el.classList.add("show");
}
.hide {
display: none;
}
.show {
display: block;
}
.animation {
animation: fadeIn 2s;
}
#keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p>
<input type="text" placeholder="input 1">
</p>
<p id="display" class="animation hide">
<input type="text" placeholder="input 2">
</p>
<button onclick="show()">show</button>
Related
Summary: In my code, when hovering over a text element, an image appears underneath. I want to do this with other text elements, and the only thing I want to change is the image url. However, I don't want to make a whole new item:hover class to change the url of the image that appears after hovering. Is there any way to work around this?
I have two items styled with the same class.
<h6 class = "item"> felucian garden spread </h6>
<h6 class = "item"> yobshrimp noodle salad </h6>
When hovering on "item," my CSS below makes an image appear. However, I want a different image to appear when hovering over "yobshrimp noodle salad." Is there a way to do this, even if they are under the same class? I am trying to avoid making a new class and hover event for every menu item.
.item:hover:after {
content: "";
background-image:url(felucian.jpg); // <- i want to change this part *only* for a different text, "yobshrimp noodle salad"
background-size: 100% 100%;
margin-left: 30%;
display: block;
height: 266px;
width: 437px;
-webkit-animation: fade-in-top 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
animation: fade-in-top 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
left: 150px;
top: 50px;
transition: all ease-in-out 1s
}
You could use a custom attribute! In your HTML:
<h6 class="item" data-hover-image="felucian.jpg">felucian garden spread</h6>
<h6 class="item" data-hover-image="yobshrimp.jpg">yobshrimp noodle salad</h6>
And in CSS:
.item :hover :after {
...
background-image: attr(data-hover-image url);
}
About attr(): read here
Edit:
Since not a lot of browsers support custom attributes, you could use javascript to iterate through all of the .item elements and change their url. For example:
const items = document.getElementsByClassName("item");
for(const item of items) {
item.style.backgroundImage =
"url(" + item.getAttribute("data-hover-image") + ")";
}
Without undoing the HTML you have, this is really only easily accomplished with CSS Custom properties. https://developer.mozilla.org/en-US/docs/Web/CSS/--*
CSS - Create a custom property for the background image with something like this:
.item {
--bg-url: url(felucian.jpg); /* you might want to set a default or something on the class */
}
.item:hover:after {
content: "";
background-image: var(--bg-url); /* use the custom prop like this */
background-size: 100% 100%;
margin-left: 30%;
display: block;
height: 266px;
width: 437px;
-webkit-animation: fade-in-top 0.6s cubic-bezier(0.39, 0.575, 0.565, 1) both;
animation: fade-in-top 0.6s cubic-bezier(0.39, 0.575, 0.565, 1) both;
left: 150px;
top: 50px;
transition: all ease-in-out 1s;
}
HTML - Use inline styles to override the --bg-url.
<h6 class = "item" style="--bg-url: url(felucian.jpg);"> felucian garden spread </h6>
<h6 class = "item" style="--bg-url: url(yobshrimp.jpg);"> yobshrimp noodle salad </h6>
Faced with similar things, I've opted to make <img> tags in the HTML and use selectors to have the images only appear on hover or focus, but this technically would work.
youc an use :nth-child() as in the example below. With that you can select a specific element according to certain rules. For the 2nd element with the class-name, use :nth-child(2)
.item:nth-child(2):hover {
background-color: yellow;
}
<h6 class = "item"> felucian garden spread </h6>
<h6 class = "item"> yobshrimp noodle salad </h6>
Surprisingly, after googling this for a bit I found tons of questions about how to fade the background of a div (not the text), but none of the converse. Please point me to one if you can find it.
I'm looking for a way to fade in the contents of a div. For example, I have a modal and if I apply the following rule to it:
animation-name: contentfadein;
animation-duration: 1s;
#keyframes contentfadein {
0% { opacity: 0; }
100% { opacity: 1; }
}
Then it fades in everything (including the modal background itself).
Rather, I would like to only fade in the contents, such that the modal immediately loads and has its white background, but all of the contents of the modal fade in (such as the text, buttons, etc.)
All of the approaches I've tried so far also fade in the background, which I am looking to avoid.
The only thing I could think of is to apply this rule on every.. single.. item.. in the modal, but that would result in hundreds of individual rule additions (and also stop working when a new item is added, until that item is "fixed"), which seems inefficient compared to a better way.
You can apply rules to all component inside an element with css easily. Then you will need to deal with text directly inside your container, you can change the color of your text to transparent.
.container{
background: red; /* won't fade */
}
.container, .container *{
transition-duration: 1s;
}
/* This will apply for all content in .container */
.container:hover * {
opacity: 0;
}
/* OPTIONAL : This will apply for text directly in container */
.container:hover {
color: transparent;
}
<div class='container'>
I'm text directly in container
<p>I'm a paragraphe</p>
<input type='submit' value="I'm a submit button"/>
<div>
<p>I'm in a div and a paragraphe</p>
</div>
</div>
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'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.
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.