I've always thought that CSS3 Animations (differently from CSS3 Transitions) once started, always finish the job, no matter if the selector is not matching anymore the element that activated them.
I'm realizing today that I was probably wrong.
In the following example, an animation is triggered by the :focus and :active pseudo-classes.
Focus on the first textfield:
if you press the tab button slowly, you will see the animations starting and ending correctly;
if you press the tab button quickly, you will see that once a new element get the focus, the old element's animation immediately ends and disappear.
#-webkit-keyframes pressed {
0%, 100% { transform : scale(1); }
50% { transform : scale(2); }
}
#keyframes pressed {
0%, 100% { transform : scale(1); }
50% { transform : scale(2); }
}
a:focus, a:active {
-webkit-animation : pressed 2s;
animation : pressed 2s;
}
a, input {
border : 1px solid silver;
padding : 5px;
height : 40px;
line-height : 28px;
margin : 0px;
display : inline-block;
width : 33.3%;
box-sizing : border-box;
background : white;
vertical-align : middle;
}
a {
color : dodgerBlue;
text-decoration : none;}
input {
color : red;
}
<input type="text" id="foo" value="Start here, then press tab" /><a href = "#">
Lorem
</a><a href = "#">
Ipsum
</a><a href = "#">
dolor
</a><a href = "#">
sit
</a><a href = "#">
amet
</a><a href = "#">
consectetur
</a><a href = "#">
adipiscing
</a><a href = "#">
elit
</a>
I know I can make it end smoothly (on some browser, eg. Firefox yes, Chrome no) by applying:
a { transition: all 2s ease; }
so that if it's loaded up to (for example) 40%, it will animate back from 40% to 0% instead of immediately dropping to 0%.
- I also know that I can use jQuery animations instead of CSS3 animation and make it work that way;
(EDIT: according to the comment, not even jQuery animations will work this way, if I got that right)
What I'm asking here, as a CSS3 Animation newbie, is:
is there a pure CSS3 way to force the animation to run up to 100%, no matter if the initial condition is not valid anymore ?
As discussed in comments there is currently no way to force an animation to complete one full cycle even after the selector rule which originally applied the animation is no longer applicable.
The only way to achieve this is by using scripting. Below is a sample snippet using JavaScript. What this does is to add a class (that has the animation property set) to the element when it gains focus and then remove it only when the animation ends.
Note:
I have used webkitAnimationEnd event in the snippet and so it would not work in other browsers. The code also needs more fine tuning because it currently removes the class only on animation end. So, if you tab out and tab in before one cycle is completed then nothing happens.
window.onload = function() {
var anchors = document.querySelectorAll('a');
for (var i = 0; i < anchors.length; i++) {
anchors[i].addEventListener('focus', function() {
addanim(this);
});
anchors[i].addEventListener('webkitAnimationEnd', function() {
endanim(this);
});
}
function addanim(el) {
el.classList.add('focus');
}
function endanim(el) {
el.classList.remove('focus');
}
}
#keyframes pressed {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(2);
}
}
.focus {
animation: pressed 2s;
}
a,
input {
border: 1px solid silver;
padding: 5px;
height: 40px;
line-height: 28px;
margin: 0px;
display: inline-block;
width: 33.3%;
box-sizing: border-box;
background: white;
}
a {
color: dodgerBlue;
text-decoration: none;
}
input {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<input type="text" id="foo" value="Start here, then press tab" />
Lorem
Ipsum
dolor
sit
amet
consectetur
adipiscing
elit
The animationcancel event mentioned in comments (by BoltClock) could have been more useful for our case but it is just an event that is fired when an abnormal end to the animation is encountered. We would still have to write our own code to make it continue till the end of a cycle.
Related
I work on a WordPress site for a friend and use the free Kadence Theme for that.
I want to display some text and when i hover over some of the words inside it, i want the whole background-colour of the site to change.
For that i identified an element to address with DevTools:
<div id="wrapper" class="site wp-site-blocks">
It works just fine, but ONLY if you hover fast enough from one span-element to another.
Try it out. First move your cursor slowly (no change of the background color). Then move the cursor quickly (background changes).
Why is that?
/* color change of text on hover */
h1>span:hover {
color: #dddddd;
}
/* color change of background on hover */
#wrapper:has(#rule1:hover) {
background: red;
}
#wrapper:has(#rule2:hover) {
background: blue;
}
#wrapper:has(#rule3:hover) {
background-color: green;
}
<div id="wrapper" class="site wp-site-blocks">
<section class="intro">
<h1>Rae magnim
<span id="rule1">volorrum</span>
<span id="rule2">recate</span>
<span id="rule3">parchil</span> ipsandiscias est labo.
</h1>
</section>
</div>
I also tried in vain to address the class instead like so:
.site:has(#rule1:hover) {
background: black;
}
Any ideas?
You can't change the background of the site when hovering an element using css only. Generally speaking a child cannot affect a parent in css, it's the other way around. It is only when using relative selectors that you can achieve it. Selectors such as: + and >.
With javascript that's a different story.
var list = document.querySelectorAll(".affects-wrapper");
var wrapper = document.querySelector("#wrapper");
list.forEach(function(item) {
item.addEventListener('mouseenter', function() {
wrapper.classList.add(item.id + "-class")
})
item.addEventListener('mouseleave', function() {
wrapper.classList.remove(item.id + "-class")
})
})
/* color change of text on hover */
h1>span:hover {
color: #dddddd;
}
/* color change of background on hover */
.rule1-class {
background: red;
}
.rule2-class {
background: blue;
}
.rule3-class {
background-color: green;
}
<div id="wrapper">
<section class="intro">
<h1>Rae magnim
<span id="rule1" class="affects-wrapper">volorrum</span>
<span id="rule2" class="affects-wrapper">recate</span>
<span id="rule3" class="affects-wrapper">parchil</span> ipsandiscias est labo.
</h1>
</section>
</div>
I have a small html like this:
h1:target {
animation: yellow-fading 0.8s linear;
}
#keyframes yellow-fading {
0% {
background-color: yellow;
}
100% {
background-color: transparent;
}
}
[Flash-title1] [Flash-title2]
<h1 id="title1">Title 1</h1>
<h1 id="title2">Title 2</h1>
My purpose is to do a background color flashing for the target I just clicked. It works(on Chrome 75), but with a flaw.
If I click [Flash-title1] twice in succession, the second click will not match the h1:target rule so the color flashing does not occur. How to solve this?
here's a little css I use to swap images. I use them for little 32px social icons which go from green image to red image. They just swap.
This is the code (using placeholder images):
.soc img:last-child {
display: none;
}
.soc:hover img:first-child {
display: none;
}
.soc:hover img:last-child {
display: inline-block;
}
<li>
<a class="soc" href="some-link-here" target="_blank">
<img src="https://lorempixel.com/32/32/cats" />
<img src="https://lorempixel.com/32/32/food" />
</a>
</li>
I want to add a little DELAY between the swaps. Like 0.8 or 1.6 seconds.. so the transition between images / color changes are smoother and prettier.
The transition should go like this: Normal Condition to HOVER condition and also, when you just hover and move mouse away, getting back to its normal condition should also have the delay.. well that's what I would love to make.
I tried all kinds of DELAY codes that I was able to google, none worked.. But I also don't want to change the main aforementioned css code which I use, to the ones that will have to have the background-image so that for each icon I gotta make a different 5 line-long css code.
Can anyone please help me with this?
Thank you.
set the right opacity starting points
:first-child or green starts at 1 or default value and goes to 0 on :hover and :first-child or red starts at 0 and goes to 1 on :hover.
position:absolute stacks the objects on top of each other.
then use transition:opacity ease 1s for the smooth animation effect. What this property does is it tells the browsers to fade the transition from opacity:0 to opacity:1 over the duration of 1s.
adding it to the selector instead of the pseudo-class :hover makes the smooth transition occur both when you hover and when you leave the :hover state.
.soc img {
position: absolute;
width: 40px;
height: 40px;
transition: opacity ease 1s;
}
.soc img:first-child {
background: green
}
.soc:hover img:first-child {
opacity: 0
}
.soc img:last-child {
opacity: 0;
background: red
}
.soc:hover img:last-child {
opacity: 1
}
<li>
<a class="soc" href="some-link-here" target="_blank">
<img src="/iconslocation/icongreen.png" />
<img src="/iconslocation/iconred.png" /></a>
</li>
It sounds you want to soften the transition between images by fading from one to the other. I recommend using CSS to transition opacity over time.
In my example below, I've positioned the second image absolutely, so it's placed directly in front of the first one, and set it to be transparent. Then I fade it in upon hover.
ul {
list-style: none;
padding: 0;
margin: 0;
}
.soc {
position: relative;
display: inline-block;
}
.soc img:last-child {
position: absolute;
top: 0;
left: 0;
opacity: 0;
transition: opacity .2s;
}
.soc:hover img:last-child {
opacity: 1;
}
<ul>
<li>
<a class="soc" href="some-link-here" target="_blank">
<img src="//lorempixel.com/50/50/abstract/1/" />
<img src="//lorempixel.com/50/50/abstract/2/" /></a>
</li> <li>
<a class="soc" href="some-link-here" target="_blank">
<img src="//lorempixel.com/50/50/abstract/3/" />
<img src="//lorempixel.com/50/50/abstract/4/" /></a>
</li>
</ul>
Pure CSS
If we apply the default image as the background-image: url() of all a.soc elements, and give them all an :after pseudo element holding the second image to show on :hover (and optionally (and ideally for accesibility) :focus), we can then transition the opacity of the :after element to 1 on user interaction, and at full opacity it will cover/hide its parent's background-image.
The effect is as expected, but requires no <img> markup and is automatically applied by the magic of CSS to every link with class="soc".
.soc,
.soc:after {
display: inline-block;
width: 32px;
height: 32px;
background: url( https://lorempixel.com/32/32/cats );
}
.soc:after {
opacity: 0;
content: "";
position: absolute;
background: url( https://lorempixel.com/32/32/food );
transition: opacity 800ms; /* timing can be in seconds or milliseconds */
}
.soc:hover:after,
.soc:focus:after {
opacity: 1;
}
<ul>
<li><a class="soc" href="some-link-here" target="_blank"></a></li>
<li><a class="soc" href="some-other-link-here" target="_blank"></a></li>
<li><a class="soc" href="another-link-here" target="_blank"></a></li>
</ul>
I want to zoom image with only CSS. The code below zooms the image when the left button of the mouse is kept pressed but I want to zoom in and out with a mouse click. How can I achieve that?
.container img {
transition: transform 0.25s ease;
cursor: zoom-in;
}
.container img:active {
-webkit-transform: scale(2);
transform: scale(2);
cursor: zoom-out;
}
Let's use a trick here, an input checkbox:
input[type=checkbox] {
display: none;
}
.container img {
margin: 100px;
transition: transform 0.25s ease;
cursor: zoom-in;
}
input[type=checkbox]:checked ~ label > img {
transform: scale(2);
cursor: zoom-out;
}
<div class="container">
<input type="checkbox" id="zoomCheck">
<label for="zoomCheck">
<img src="https://via.placeholder.com/200">
</label>
</div>
Building on #Nhan answer: https://stackoverflow.com/a/39859268/661872
Shorter, scoped and does not require tracking ids for multiple elements.
.click-zoom input[type=checkbox] {
display: none
}
.click-zoom img {
margin: 100px;
transition: transform 0.25s ease;
cursor: zoom-in
}
.click-zoom input[type=checkbox]:checked~img {
transform: scale(2);
cursor: zoom-out
}
<div class="click-zoom">
<label>
<input type="checkbox">
<img src="https://via.placeholder.com/200">
</label>
</div>
<div class="click-zoom">
<label>
<input type="checkbox">
<img src="https://via.placeholder.com/200">
</label>
</div>
4 Ways to Add Click Events with Only CSS Pseudo-Selectors
Note: I'll be using the word target when referring to the element we want to manipulate and trigger as the element we are using to manipulate target.
:checked
Use checkboxes or radios and :checked to determine or cause a target's state and/or to take action.
Trigger
<label>
<input type="checkbox">
<!--or-->
<input type="radio">
Conditions
Requires that the target must be:
A sibling that follows the trigger or...
...a descendant of the trigger.
Note
Hide the actual <checkbox> with display:none
Ensure that the <checkbox> has an id and that the <label> has a for attribute with a value matching the id of the <checkbox>
This is dependant upon the target being a sibling that follows the trigger or the target as a descendant. Therefore be aware that you'll most likely use these selector combinators: ~, +, >.
HTML
<label for='chx'>CHX</label>
<input id='chx' type="checkbox">
<div>TARGET</div>
CSS
#chx:checked + div {...
:target
Use an <a>nchor and apply the :target pseudo-selector on the target element.
Trigger
Conditions
Assign an id to the target.
Assign that same id to the <a> href attribute preceding with a hash #
HTML
<a href='#target'>A</a>
<div id='target'>TARGET</div>
CSS
#target:target {...
:focus
The trigger element must be either an <input> type or have the attribute tabindex in order to use :focus.
Trigger
<div tabindex='0'>ANY INPUT OR USE TABINDEX</div>
Conditions
Target must a sibling that is located after the trigger or *target must be a descendant of the trigger.
State or effect will persist until user clicks elsewhere thereafter a blur or unfocus event will occur.
HTML
<nav tabindex='0'>
<a href='#/'>TARGET</a>
<a href='#/'>TARGET</a>
<a href='#/'>TARGET</a>
</nav>
CSS
nav:focus ~ a {...
:active
This is a hack that cleverly exploits the transition-delay property in order to actually have a persistent state achieved with no script.
Trigger
<a href='#/'>A</a>
Conditions
Target must a sibling that is located after the trigger or *target must be a descendant of the trigger.
There must be a transition assigned to the target twice.
The first one to represent the persistent state.
The second one to represent the normal state.
HTML
A
<div class='target'>TARGET</div>
CSS
.target {
opacity: 1;
transition: all 0s 9999999s;
}
a:active ~ .target {
opacity: 0;
transition: all 0s;
}
Wacked looking, right? I'll try to explain it, but you're better off reading this article.
Under normal circumstances, if your trigger had the :active pseudo-selector, we are able to manipulate the target upon keydown. That means our active state is actually active as long as you keep your finger on the button...that's crappy and useless, I mean what are you expected to do to make .active to be useful? Maybe a paperweight and some rubber bands to keep a steady and persistent pressure on the button?
We will leave .active the way it is: lame and useless. Instead:
Make a ruleset for target under normal circumstances. In the example above it's opacity:1.
Next we add a transition: ...ok then... all which works, next is 0s ...ok so this transition isn't going to be seen it's duration is 0 seconds, and finally... 9999999s ...116 days delay?
We'll come back to that, we will continue onto the next rulesets...
These rulesets declare what happens to target under the influence of trigger:active. As you can see that it just does what it normally does, which is onkeydown target will become invisible in 0 seconds. Now once the user keys up, target is visible again...no *target's * new state of opacity:0 is persistent! No paperweight, technology has come a long way.
The target is still actually going to revert back to it's normal state, because :active is too lazy and feeble to work without rubber bands and paperweights. The persistent state is perceived and not real because target is still leaving the state brought on by :active which will be about 116 days before that will happen. ;)
This Snippet features the 4 ways previously mentioned. I'm aware that the OP requested zoom (which is featured therein), but thought it would be to repetitive and boring, so I added different effects as well as zooming.
SNIPPET
a {
text-decoration: none;
padding: 5px 10px;
border:1px solid red;
margin: 10px 0;
display: inline-block;
}
label {
cursor: pointer;
padding: 5px 10px;
border: 1px solid blue;
margin: 10px 0;
display:inline-block;
}
button {
cursor:pointer;
padding: 5px 10px;
border: grey;
font:inherit;
display:inline-block;
}
img#img {
width: 384px;
height: 384px;
display: block;
object-fit: contain;
margin: 10px auto;
transition: width 3s height 3s ease-in;
opacity: 1;
transition: opacity 1s 99999999s;
}
#zoomIn,
#zoomOut,
#spin {
display: none;
padding: 0 5px;
}
#zoomOut:checked + img#img {
width: 128px;
height: 128px;
transition: all 3s ease-out;
}
#zoomIn:checked + img#img {
width: 512px;
height: 512px;
transition: all 3s ease-in-out;
}
#spin:checked ~ img#img {
transform: rotate(1440deg);
}
img#img:target {
box-shadow: 0px 8px 6px 3px rgba(50, 50, 50, 0.75);
}
a.out:focus ~ img#img {
opacity: 0;
transition: opacity 1s;
}
a.in:active ~ img#img {
opacity: 1;
transition: opacity 1s;
}
.grey:focus ~ img#img {
filter: grayscale(100%);
}
<a href='#/' class='out'>FadeouT</a><a href='#/' class='in'>FadeiN</a>
<a href='#img'>ShadoW</a>
<br/><button class='grey' tabindex='0'>GreyscalE</button><br/>
<label for='spin'>SpiN</label>
<input type='checkbox' id='spin'>
<label for='zoomIn'>ZoomiN</label>
<input type='radio' id='zoomIn' name='zoom'>
<label for='zoomOut'>ZoomouT</label>
<input type='radio' id='zoomOut' name='zoom'>
<img id='img' src='https://i.ibb.co/5LPXSfn/Lenna-test-image.png'>
.container img {
margin: 100px;
transition: transform 0.25s ease;
cursor: zoom-in;
}
input[type=checkbox]:checked ~ label > img {
transform: scale(2);
cursor: zoom-out;
}
<div class="container">
<input type="checkbox" id="zoomCheck">
<label for="zoomCheck">
<img src="https://via.placeholder.com/200">
</label>
</div>
<html>
<head>
<title>Image Zoom</title>
<style type="text/css">
#imagediv {
margin:0 auto;
height:400px;
width:400px;
overflow:hidden;
}
img {
position: relative;
left: 50%;
top: 50%;
}
</style>
</head>
<body>
<input type="button" value ="-" onclick="zoom(0.9)"/>
<input type="button" value ="+" onclick="zoom(1.1)"/>
<div id="imagediv">
<img id="pic" src=""/>
</div>
<script type="text/javascript" language="javascript">
window.onload = function(){zoom(1)}
function zoom(zm) {
img=document.getElementById("pic")
wid=img.width
ht=img.height
img.style.width=(wid*zm)+"px"
img.style.height=(ht*zm)+"px"
img.style.marginLeft = -(img.width/2) + "px";
img.style.marginTop = -(img.height/2) + "px";
}
</script>
</body>
</html>
I want to change the opacity of some text in my div when hover over happens :
currently my transition looks like this, and it just moved the box up a bit:
.bg-onebyone {
transition: all 0.5s ease;
background: #17B6A4 none repeat scroll 0% 0% !important;
}
.bg-onebyone:hover {
margin-top: -8px;
}
In my div.bg-onebyone I have another div holding some text like this
.bg-onebyone .widget-stats .stats-icon {
color: #FFF;
opacity: 0.5;
}
And what I want to do is just when the main div is hovered over I want to also increased the above opacity in the transition. How can I do this ?
<a href="/url">
<div class="widget widget-stats bg-onebyone">
<div class="stats-icon stats-icon-lg">
<i class="fa fa-search fa-fw"></i>
</div>
<div class="stats-desc">Creating Grouped Unmatched Aliases</div>
</div>
</a>
You need to use the :hover pseudo-class on parent and then select the child element.
.bg-onebyone:hover .stats-icon {
opacity: 0.8;
}
Also .bg-onebyone .widget-stats .stats-icon is incorrect for your HTML markup since it targets .stats-icon as a grand-child of .bg-onebyone which does not exist.
Output:
.bg-onebyone {
width: 300px;
height: 100px;
transition: all 0.5s ease;
background: #17B6A4 none repeat scroll 0% 0% !important;
}
.bg-onebyone:hover {
margin-top: -8px;
}
.bg-onebyone .stats-icon {
color: #FFF;
opacity: 0.5;
}
.bg-onebyone:hover .stats-icon {
opacity: 0.8;
}
<div class="widget widget-stats bg-onebyone">
<div class="stats-icon stats-icon-lg">Test text for opacity
<i class="fa fa-search fa-fw"></i>
</div>
<div class="stats-desc">Creating Grouped Unmatched Aliases</div>
</div>
Via JavaScript, use jQuery .hover() and .css(), like this:
$( "mainDiv" ).hover(
function() {
$("whereToChangeTheOpacity").css( "opacity", "0.5" );
},
function() {
$("whereToChangeTheOpacity").css( "opacity", "0" );
}
);