Animating HTML element position on DOM change - html

I have a list of tags that form a tag cloud. I'll include a search option that will hide/remove the tags that don't match the search. If I do this the tag cloud will rearrange automatically, but it will be instantaneously. I want some form of animation.
I've researched and discovered that the attribute position is not animatable. I've played around with MutationObserver and was able to detect changes in the DOM but was still unsure of what to do on the callback function.
.tagcloud ul {
margin: 0;
padding: 0;
list-style: none;
}
.tagcloud ul li {
position: relative;
display: inline-block;
margin: 0 .75em .5em 0;
padding: 0;
}
.tagcloud ul li a {
position: relative;
display: inline-block;
height: 28px;
line-height: 28px;
padding: 0 1em;
background: #fff;
border: 1px solid #aaa;
border-radius: 3px;
word-wrap: normal;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
color: #333;
font-size: 13px;
text-decoration: none;
-webkit-transition: .2s;
transition: .2s;
}
.tagcloud ul li span {
position: absolute;
top: 0;
right: -10px;
z-index: 2;
width: 28px;
height: 28px;
line-height: 28px;
background-color: #3498db;
border: 1px solid #3498db;
border-radius: 100%;
color: #fff;
font-size: 13px;
text-align: center;
opacity: 0;
-webkit-transition: .2s;
transition: .2s;
-webkit-transform: scale(.4);
transform: scale(.4);
}
.tagcloud ul li span::after {
position: absolute;
top: 50%;
left: -8px;
content: '';
width: 0;
height: 0;
margin-top: -7px;
border-color: transparent #3498db transparent transparent;
border-style: solid;
border-width: 7px 14px 7px 0;
}
.tagcloud ul li a:hover {
border: 1px solid #3498db;
color: #3498db;
}
.tagcloud ul li:hover span {
right: -26px;
opacity: 1;
-webkit-transform: scale(1);
transform: scale(1);
}
<div class="tagcloud">
<ul>
<li>javascript<span>10</span></li>
<li>android<span>6</span></li>
<li>c++<span>20</span></li>
<li>c<span>15</span></li>
<li>java<span>16</span></li>
<li>html<span>4</span></li>
<li>css<span>11</span></li>
<li>python<span>17</span></li>
<li>artificial intelligence<span>5</span></li>
<li>computer graphics<span>9</span></li>
<li>php<span>8</span></li>
<li>computer audio<span>7</span></li>
<li>database<span>7</span></li>
<li>web<span>12</span></li>
<li>sql<span>6</span></li>
</ul>
</div>

This might not answer your question directly because in my opinion hiding element is a lot easier than remove it (or wait it removed).
After input event triggered, for each element we need to determine to do some animations or ignore it. In case of do animation we need to determine more between 2 animations which are exit animation (animate element then hide it) and entry animtion (show element then animate it).
input.addEventListener('input', event => {
let re = new RegExp(input.value)
ul.querySelectorAll('li').forEach(li => {
let isHidden = li.style.display === 'none'
let shouldHide = !re.test(li.textContent)
if (shouldHide && !isHidden) {
// Play exit animation
}
if (!shouldHide && isHidden) {
// Play entry animation
}
})
})
I suggest to use javascript animation library (in example I use animejs) to handle animation instead of pure css animation because we need to toggle display which not working on pure css.
See full example here.
I hope this help. Thanks.

Related

Event on input within div acting differently to event on input within a label

This is an odd one but I'm sure there's a simple explanation. Can someone explain to me why an event on an input within a div acts differently to an event on input within a label. Please see the following fiddle as an example:
https://jsfiddle.net/anthill/h8v106o7
$('#container .switch input[type="checkbox"]').change(function(e) {
alert($(this).data('message'));
})
body {
font-family: Helvetica, Arial, sans-serif;
padding: 10px;
}
h5 {
margin-bottom: 0.5em;
}
.switch {
background-color: #fff;
border: 1px solid #999;
border-radius: 2px;
color: #fff;
cursor: pointer;
display: inline-block;
font-size: 0;
height: 34px;
overflow: hidden;
margin: 0;
padding: 0;
position: relative;
width: 80px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
.switch:after, .switch:before {
background-color: #9bca3e;
color: #fff;
content: "On";
display: block;
font-size: 13px;
line-height: 1.5;
height: 100%;
left: 0;
padding: 7px 0;
position: absolute;
text-align: center;
top: 0;
width: 51%;
/* This is so the darker color doesn't show through the rounded corners of the knob */
}
.switch:before {
background-color: #999;
content: "Off";
left: auto;
right: 0;
width: 50%;
}
.switch .knob {
background: #f7f7f7;
border: 1px solid #999;
border-bottom: none;
border-top: none;
border-radius: 2px;
display: block;
font-size: 13px;
height: 100%;
left: -1px;
position: relative;
top: 0;
width: 40px;
z-index: 2;
-webkit-transition: all 0.15s ease;
-moz-transition: all 0.15s ease;
-ms-transition: all 0.15s ease;
transition: all 0.15s ease;
}
.switch .knob:before, .switch .knob:after {
border: 4px solid transparent;
border-left-color: inherit;
content: "";
display: block;
height: 0;
left: 50%;
margin-left: 2px;
margin-top: -3px;
position: absolute;
top: 50%;
width: 0;
}
.switch .knob:before {
border-left-color: transparent;
border-right-color: inherit;
margin-left: -10px;
}
.switch input {
position: absolute;
visibility: hidden;
}
.switch input:checked + .knob {
left: 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<label class="switch">
<input type="checkbox" checked data-message="Checkbox 1"/>
<span class="knob"></span>
</label>
<div class="switch">
<input type="checkbox" checked data-message="Checkbox 2" />
<span class="knob"></span>
</div>
</div>
The change event fires fine on the first checkbox but not at all on the second. Is it something to do with the label sharing the event on the hidden input, whereas the div does not do this?
Testing here: https://jsfiddle.net/Twisty/947vsjpa/5/
You can see that Click events are happening on the Switch & Knob, yet not on the Input. The label element has a relationship with input. The click event upon the Label can effect the state of the Input. div does not have this relationship; therefore, the click event does not reach the invisible element.
How do you want to fix this? Use Label element for both? Capture the click event on the .switch and then trigger a click of the Input.
Consider the following: https://jsfiddle.net/Twisty/947vsjpa/18/
JavaScript
$(function() {
$('#container .switch').click(function(event) {
console.log(event.type, event.target);
event.preventDefault();
var $input = $("input[type='checkbox']", this)
$input.prop("checked", !$input.prop("checked"));
alert($input.data("message"));
});
});
This changes the property even if it's not visible and can't be clicked upon.

Overlap ::after and ::before with text in CSS

Goal: Make nice effect of hovering buttons in pure CSS, which will use ::after and ::before pseudo-elements. Look at this jsFiddle example to see, what I want to reach.
Code: Button will have some styling, also an background-color, which is turned off in this example.
.button {
display: inline-block;
position: relative;
height: 35px;
line-height: 35px;
padding: 0 15px;
/*background-color: white;*/
text-decoration: none;
color: black;
}
Problem: I want to use background-color and when I enable it, then I can't see pseudo-elements. It is like that, because these pseudo-elements have z-index: -1;, which put them behind the background. When I change z-index to 0 or 1, then text is not visible.
What I can't do: I can't add new elements inside buttons (like spans), because this is one already running website and client decided to change the behavior of buttons, so here I am. There are tons of buttons in this website, so this is the reason, why I want to find solution with pseudo-elements, because trying to find every single button and change them would be inappropriate.
If i understood you well, this is what you are looking for:
.button {
display: inline-block;
position: relative;
height: 35px;
line-height: 35px;
padding: 0 15px;
/*background-color: white;*/
text-decoration: none;
color: black;
border:1px solid;
}
a.button:before {
content: " ";
display: block;
z-index: -1;
position: absolute;
height: 0%;
top: 0;
right: 0;
left: 0;
background: #ddd;
transition: height 0.2s ease;
}
a.button:hover:before {
height:100%;
}
TEST
Consider an alternative method of doing the background colour transition thing.
As seen in this edited demo:
/* remove all references to .button::before */
.button {
background-image: linear-gradient(to bottom,
transparent, transparent 100%,
red 100%, red);
transition: background-image 0.5s ease 0s;
}
/* the "gradient" above has the practical result of being fully transparent,
but it has been carefully crafted so that the transition gives the desired result */
.button:hover {
background-image: linear-gradient(to bottom,
transparent, transparent 0%,
red 0%, red);
}
You can transition gadients, and in this case it is done stop-by-stop. The first and last stops don't change, but the middle two transition from 100% to 0%, essentially meaning that the cut-off point between transparent and red slides from the bottom to the top of the button, giving the effect you want.
You can now replace transparent with your desired background colour.
* You may need to remove the z-index:-1 from the ::after element to get the border effect back.
You can do something like,
HTML
CSS
body {
background: #FF7272;
}
.button {
display: inline-block;
position: relative;
height: 35px;
line-height: 35px;
padding: 0 15px;
text-decoration: none;
color: black;
z-index: 0;
background-color: white;
width: 50px;
}
.button::before, .button::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: 0;
}
.button::after {
content: "TEST";
height: 50%;
width: 72px;
text-align: center;
z-index: 2;
line-height: 0.2;
border-left: 4px solid red;
border-right: 4px solid red;
border-bottom: 4px solid red;
}
.button::before {
height: 0%;
background-color: red;
transition: all 0.5s ease 0s;
z-index: 1;
}
.button:hover::before {
height: 100%;
}
Example: https://jsfiddle.net/LL0f7rwp/6/
Some values are hard coded, but hope you can get an idea out of it :)
It's because z-index: -1 and background-color: white will push your :before and :after elements beneath.
Remove z-index: -1 from :after and :before and add to hover .button:hover::before
Make the background-color: transparent while hovering. Updated fiddle.
body {
background: #FF7272;
}
.button {
display: inline-block;
position: relative;
height: 35px;
line-height: 35px;
padding: 0 15px;
background-color: white;
text-decoration: none;
color: black;
}
.button::before,
.button::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: 0;
}
.button::after {
height: 50%;
border: 4px solid red;
border-top: 0;
}
.button::before {
height: 0%;
background-color: red;
transition: all 0.5s ease 0s;
}
.button:hover::before {
height: 100%;
z-index: -1;
}
.button:hover {
background-color: transparent;
}
TEST

:before element overlapping my button text on hover

I have a button with a background color, and text color set. What I like to do, is when the user hover the mouse on the button, the background to animate from bottom to top and change the color of the text to the color of the background.
For terms of simplicity of the code, I didn't put the transient I like to apply on the CSS properties. I know it's much easyer to change the button background code, but I plan to use transient for changing the :before height on hover.
So I have the following code, but when I hover the mouse on the button, the :before overlapping my button text.
I have also try to play with the z-index but no luck. Do you think is there any solution to this problem ?
body {
background: #111;
}
.btn {
color: #FFF;
background: #333;
border: none;
font-size: 18px;
font-weight: bold;
padding: 18px 60px;
position: relative;
}
.btn:before {
display: block;
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 0;
background: #FFF;
}
.btn:hover {
color: #333;
}
.btn:hover:before {
height: 100%;
}
<br />
Do Stuff
You need to add additional <span> element which would stay above the ::before pseudoelement:
span {
position: relative;
z-index: 1;
}
fiddle
The effect you desire can also be achieved without adding the additional span. By utilising the before and after pseudo elements for background colours and positioning them correctly.
To position the pseudo elements behind the text, set a positive z-index on the element and a negative z-index on the pseudo-element.
.btn {z-index: 1}
.btn:before {z-index: -1;}
Reference this article by Nicolas Gallagher which explains in more detail, see section 'Pseudo background-position' http://nicolasgallagher.com/an-introduction-to-css-pseudo-element-hacks/.
Also see fiddle with it in action: https://jsfiddle.net/j9whmcmz/2/
This technique does not work if you apply a background color to the .btn itself.
Choose your poison I guess, both solutions do the trick.
Try this:
body {
background: #333;
}
.btn {
color: #FFF;
background: #333;
display: inline-block;
position: relative;
overflow: hidden;
-webkit-transition: color 0.3s ease-in-out;
transition: color 0.3s ease-in-out;
}
.btn span {
display: inline-block;
padding: 18px 60px;
border: none;
font-size: 18px;
font-weight: bold;
z-index: 10;
position: relative;
}
.btn:after {
display: block;
content: '';
position: absolute;
top: 100%;
left: 0;
width: 100%;
max-height: 0;
background: #FFF;
height: 100%;
z-index: 9;
-webkit-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.btn:hover {
color: #333;
}
.btn:hover:after {
max-height: 100%;
top: 0;
}
<span>Do Stuff</span>
Solution if pretty obvious - content of the button should be also absolute positioned. Then browser order them properly behind each other.
EDIT: Maybe my formatting and styling is not the best for the case, but it was quick update of your code to get the idea
body {
background: #111;
}
.btn {
color: #FFF;
background: #333;
border: none;
font-size: 18px;
font-weight: bold;
padding: 18px 60px;
position: relative;
}
.btn span {
display: block;
content: '';
position: absolute;
top: 18px;
left: 0px;
width: 100%;
text-align: center;
}
.btn:before {
display: block;
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 0;
background: #FFF;
}
.btn:hover {
color: #333;
}
.btn:hover:before {
height: 100%;
}
<br />
<span>Do Stuff</span>

Need a loading icon to show on a mouseclick with just CSS

Is there a way to get a loading icon to show when a button/link is clicked, with just CSS/HTML?
I'm using FontAwesome so I figured I could use their classes to spin a loading icon, but I can't find a way to get it to spin after a specific action (mouseclick).
Basically this is all the code:
<i class="fa fa-circle-o-notch fa-spin"></i> Spin
The only way I could manage now was hiding it in background color.. but that doesn't seem very professional:
body {
background: none repeat scroll 0 0 #dcdcdc;
color: white;
}
.fa {
color: #dcdcdc;
}
a:active .fa {
color: red;
}
Any suggestions?
If an element that looks like a button is acceptable, you can do this with CSS as long as you don't need to support IE8. It works by using a label "for" a checkbox or radio as your button. Then, the :checked CSS pseudo class does the trick!
This example sets the background color of a div, but you can set whatever properties you need.
label {
display: inline-block;
background-color: #28529c;
color: white;
padding: 4px 8px;
}
#loading {
background-color: transparent;
width: 20px;
height: 20px;
}
#hidden-flag {
display: none;
}
#hidden-flag:checked ~ #loading {
background-color: green;
display: inline-block;
padding-left: 10px;
}
<input id="hidden-flag" type="radio">
<label for="hidden-flag">Button</label>
<div id="loading"></div>
Needs a bit of work, But as I'm off to beddy byes now I'm not carrying on with it, Up early tomorrow. Pure css sort of unfinished example. When the new page is loaded just bring in a new set of buttons to click.
#to_click:visited {
content:"";
display: block;
float: left;
position: relative;
margin: 40px 30px;
border-radius: 100px;
width: 20px;
height: 20px;
border: 5px solid #57E;
border-top-color: transparent;
border-bottom-color: transparent;
animation-name: rotateclock;
animation-duration: .75s;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
#to_click:before {
content: "";
display: block;
position: absolute;
top: 0;
left: 0;
border-radius: 100px;
width: 10px;
height: 10px;
border: 5px solid #57E;
border-left-color: transparent;
border-right-color: transparent;
}
#to_click:after {
content: "";
display: block;
position: absolute;
top: 5px;
left: 5px;
border-radius: 100px;
width: 0px;
height: 0px;
border: 5px solid #57E;
border-top-color: transparent;
border-bottom-color: transparent;
}
#keyframes rotateclock {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
Click Me

Css border shape and working with hover state

Question: Why when you make css shapes using the border tricks seen here does it move the visual of your element out of its dom-box?
So I found these two questions while searching:
Can I use CSS hover on this complex irregular shaped link
Hovering on overlapping CSS3 shapes
But I don't think that either addresses my question (though if I want to change my html structure I could probably use the answer from that first link.
Example pics to illustrate:
Which means that when I hover over the bottom half of that element it highlights the one below it.
I understand that even though I have a diamond visually the box-model says that it's still a rectangle, but why is the diamond not contained inside that rectangle?
Is there a way around this - with css/markup -, or do I have to go with the maping solution from the first link?
My source code incase anyone wants that:
<header class="navigation">
<div class="nav">
<ul class='master_nav'>
<li>Home</li>
<li>About</li>
<li>Resources</li>
<li>FAQs</li>
</ul>
</div>
</header>
.navigation li{
height: 0;
width: 0;
margin: 10px 0;
list-style: none;
position: relative;
border: 70px solid transparent;
border-bottom: 90px solid #2B2B2B;
display: block;
}
.navigation li:after{
content: '';
position: absolute;
left: -70px; top: 90px;
width: 0;
height: 0;
border: 70px solid transparent;
border-top: 90px solid #2B2B2B;
}
.navigation li a{
height: 25px;
width: 100%;
display: inline-block;
text-decoration: none;
color: #b7b7b7;
position: absolute;
top: 75px;
left: -19px;
}
.navigation li:hover a{
color: #010101;
}
I'm not 100% sure why but you can get around it by making the <a> the hover target and filling the space:
.navigation li a{
height:70px;
width: 80px;
display: block;
text-decoration: none;
color: #b7b7b7;
position: absolute;
top: 38px;
left: -40px;
padding-top: 40px;
text-align: center;
border: 1px solid yellow; //just to see it.
}
.navigation a:hover{
color: #010101;
}
Here's a working pen http://codepen.io/willthemoor/pen/KpcLD/ (updated)
Edit
Getting it lined up perfectly might take a little trial and error but you can use the transform property to rotate and skew the <a> to match the shape. I updated the pen.
I had to add some skew to match the shape of the diamonds, and then use a a <span> inside of the <a> to sort revert the changes. Skew messes with text so you might try to find a happy medium between the shape of your border diamonds and the shape you can make without using skew.
.navigation li{
height: 0;
width: 0;
margin: 10px 0;
list-style: none;
position: relative;
border: 70px solid transparent;
border-bottom: 90px solid #2B2B2B;
display: block;
/* position fix */
top: -90px;
left: -19px;
}
.navigation li:before{
content: '';
position: absolute;
left: -70px;
top: 90px;
width: 0;
height: 0;
border: 70px solid transparent;
border-top: 90px solid #2B2B2B;
}
.navigation li a{
background-color: rgba(255, 0, 0, 0.3);
color: #B7B7B7;
display: block;
height: 68px;
left: -55px;
padding-top: 40px;
position: absolute;
text-align: center;
text-decoration: none;
top: 34px;
-webkit-transform:rotate(314deg);
transform: rotate(314deg);
width: 110px;
}
.skew li a {
/* following lines it up better but hard to 'rewind it' on the span witout the text looking a little strange */
-webkit-transform: rotate(310deg) skewX(-11deg) skewY(-2deg);
transform: rotate(310deg) skewX(-11deg) skewY(-2deg);
height: 73px;
left: -55px;
width: 112px;
}
.navigation a:hover{
background-color: rgba(0,0,255,.3);
color: #010101;
}
.navigation a > span {
display: block;
/* and reset the text*/
-webkit-transform: rotate(46deg);
transform: rotate(46deg);
}
.skew a > span {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
letter-spacing: .04em;
}
/*
lis are actually covering each other on the bottoms.
Adding this hacky bit makes the bottom of the diamond link work. Could use classes instead.
*/
.navigation li { z-index: 100; }
.navigation li + li { z-index: 90; }
.navigation li + li + li { z-index: 80; }
.navigation li + li + li + li { z-index: 70; }