insertRule not working with keyframes - javascript

If I use CSS without keyframes, insertRule works and everything is perfect. But if I use a CSS that contains a keyframe, I'll get the error:
Failed to execute 'insertRule' on 'CSSStyleSheet': Failed to parse the
rule ...
This works:
const css = window.document.styleSheets[0];
css.insertRule(`
div {
width: 100%;
animation: move 2s;
position: absolute;
transition: 0.65s;
}
`, css.cssRules.length);
This doesn't work:
const css = window.document.styleSheets[0];
css.insertRule(`
div {
width: 100%;
animation: move 2s;
position: absolute;
transition: 0.65s;
}
#keyframes move {
0% {
left: 0;
}
20% {
left: 100px;
}
100% {
left: -100%;
}
}
`, css.cssRules.length);
How can I make it work?

The posted code tries to insert two rules with a single call to insertRule.
Using two calls to insert a rule each
const css = window.document.styleSheets[0];
css.insertRule(`
div {
width: 100%;
animation: move 2s;
position: absolute;
transition: 0.65s;
}
`, css.cssRules.length);
css.insertRule(`
#keyframes move {
0% {
left: 0;
}
20% {
left: 100px;
}
100% {
left: -100%;
}
}
`, css.cssRules.length);
div { height: 3em;}
<div> DIV element</div>
works without throwing an error. Generating a syntax error if the rule parameter of insertRule contains more than one rule is documented on MDN in listed Restrictions.

Related

Play new css animation seamlessly

I created a simple HTML game, which disappears under the screen when I click on a moving box.
However, the animation that disappears starts at the original location, not where it was clicked.
I think 0% of the remove #keyframes should have the location of the click, but I couldn't find a way
How shall I do it?
(function () {
const charactersGroup = document.querySelectorAll('.character');
const stage = document.querySelector('.stage')
const clickHandler = (e) => {
const target = e.target;
if (target.classList.contains('character')) {
target.classList.remove(`f${target.dataset.id}`);
target.classList.add('f0');
target.classList.add('remove');
setTimeout(() => { stage.removeChild(target) }, 2000);
}
}
stage.addEventListener('click', clickHandler);
}());
.stage {
overflow: hidden;
position: relative;
background: #eeeeaa;
width: 40vw;
height: 20vw;
}
#keyframes moving {
0% {
transform: translateX(0);
}
100% {
transform: translateX(30vw);
}
}
#keyframes remove {
0% {
transform: translate(0);
}
100% {
transform: translateY(60vw);
}
}
.character {
position: absolute;
width: 50px;
height: 50px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
animation: moving infinite alternate;
}
.remove {
animation: remove 0.2s cubic-bezier(.68,-0.55,.27,1.55) forwards;
}
.f0 {
background-color: black;
animation-duration: 2s;
}
.f1 {
left: 5%;
bottom: 5%;
animation-duration: 2s;
background-color: red;
}
<div class="stage">
<div class="character f1" data-id="1"></div>
</div>
Change the first animation to consider left instead of translate then append both of them to the element initially and you simply toggle the animation-play-state when adding the remove class
(function() {
const charactersGroup = document.querySelectorAll('.character');
const stage = document.querySelector('.stage')
const clickHandler = (e) => {
const target = e.target;
if (target.classList.contains('character')) {
target.classList.add('remove');
setTimeout(() => {
stage.removeChild(target)
}, 2000);
}
}
stage.addEventListener('click', clickHandler);
}());
.stage {
overflow: hidden;
position: relative;
background: #eeeeaa;
width: 40vw;
height: 20vw;
}
#keyframes moving {
100% {
left:calc(95% - 50px);
}
}
#keyframes remove {
50% {
transform: translateY(-30vh);
}
100% {
transform: translateY(60vw);
}
}
.character {
position: absolute;
width: 50px;
height: 50px;
background:red;
left: 5%;
bottom: 5%;
animation:
moving 2s infinite alternate,
remove 1s cubic-bezier(.68, -0.55, .27, 1.55) forwards paused;
}
.remove {
animation-play-state:paused,running;
background: black;
}
<div class="stage">
<div class="character f1" data-id="1"></div>
</div>
If your use case is to deal with a lot of such boxes and complexity, it's better to go with handling everything with pure JS but I tried to make this work with minimal changes in JS and CSS.
I have added comments to the new JS lines.
Also taken the liberty to have a separate class with name moving for animation moving so that we can remove it on click.
(function () {
const charactersGroup = document.querySelectorAll('.character');
const stage = document.querySelector('.stage')
const clickHandler = (e) => {
const target = e.target;
if (target.classList.contains('character')) {
target.classList.remove(`f${target.dataset.id}`);
target.classList.add('f0');
// remove the moving animation
target.classList.remove('moving');
// Get offsetWidth which is the half of width to substract later while calculating left for the target i.e our box.
const offsetWidth = parseInt(getComputedStyle(target).width)/2;
// e.clientX gives us the x coordinate of the mouse pointer
// target.getBoundingClientRect().left gives us left position of the bounding rectangle and acts as a good offset to get the accurate left for our box.
target.style.left = `${e.clientX -target.getBoundingClientRect().left - offsetWidth}px`;
target.classList.add('remove');
setTimeout(() => { stage.removeChild(target) }, 2000);
}
}
stage.addEventListener('click', clickHandler);
}());
.stage {
overflow: hidden;
position: relative;
background: #eeeeaa;
width: 40vw;
height: 20vw;
}
#keyframes moving {
0% {
transform: translateX(0);
}
100% {
transform: translateX(30vw);
}
}
#keyframes remove {
0% {
transform: translate(0vh);
}
100% {
transform: translateY(60vw);
}
}
.character {
position: absolute;
width: 50px;
height: 50px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
}
.moving{
animation: moving infinite alternate;
}
.remove {
animation: remove 0.2s cubic-bezier(.68,-0.55,.27,1.55) forwards;
}
.f0 {
background-color: black;
animation-duration: 2s;
}
.f1 {
left: 5%;
bottom: 5%;
animation-duration: 2s;
background-color: red;
}
<div class="stage">
<div class="character moving f1" data-id="1"></div>
</div>

SVG Preloader with CSS animation that randomizes on each page load

So i made a svg logo preloader, made some css animations for it also. But my main problem is how do i make the preloader load different animation on refresh/new page loading using javascript. Like for example on one page loading the logo should use the bounce animation and upon a page refreh or on another tab opening the preloader to use the rotate animation i made, etc.
var strings = [
'animation1.',
'animation2.',
'animation3.'
];
var rand = strings[Math.floor(Math.random() * strings.length)];
document.getElementById('loading-animation').innerHTML = rand;
.loading {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
background: #ddd;
padding-top: 200px;
}
.svg {
display: block;
width: 80px;
height: 80px;
background: #aaa;
margin: 10px auto;
}
.animation1 {
just an example
}
.animation2 {
just an example
}
.animation2 {
just an example
}
<div id="container" class='loading' >
<div id='loading-animation' class='loading-animation'>Processing</div>
<svg>just an example svg in inserted in the html, no external src link to it</svg>
</div>
I'm pretty sure that .innerHTML shouldn't be there since the javascript file will be external linked in the head section. And i know i haven't linked all of the codes used just because it's to much code to paste here so i made a mini example, hope i can make myself understood. Thanks.
You can use JavaScript to randomly assign a CSS class to the element you want to animate. Here is an example.
var animationClasses = [
'animation1',
'animation2',
'animation3'
];
var choosenAnimation = animationClasses[~~(Math.random() * animationClasses.length)];
document.getElementById('elementToAnimate').classList.add(choosenAnimation);
#keyframes grow {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
#keyframes fade {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#keyframes fly-down {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0%);
}
}
.animation1 {
width: fit-content;
animation: grow 1s;
}
.animation2 {
animation: fade 1s;
}
.animation3 {
animation: fly-down 1s;
}
<div id="elementToAnimate">This will get a random animation</div>
A random class in the array animationClasses is assigned to elementToAnimate. Each class contains CSS for a different animation, allowing for a random animation each time.

What is the JavaScript equivalent of the following jQuery code?

I want to make spinning wheel but instead of rotating wheel i want to rotate the pointer. I have the following jQuery code snippet which rotates a pointer but I don't know how to convert this jQuery snippet into JavaScript.
jQuery code:
$( document ).ready(function() {
var startJP = (164) + (360 * 3);
$('.jackpot-pointer').animate({ transform: startJP }, {
step: function(now,fx) {
$(this).css('-webkit-transform','rotate('+now+'deg)');
},
duration:6000
},'linear');
});
i want to do something like this
https://i.stack.imgur.com/0KArr.gif
Here is a simple example, that shows how you could solve it with vanilla JS and CSS by using CSS animations which can be toggled on and off via JS.
var stage = document.querySelector('#stage');
var rot = document.querySelector('#rotating');
stage.addEventListener('click', function () {
rot.classList.toggle('animated');
});
#stage {
position: relative;
height: 100px;
background-color: #000;
}
#rotating {
position: absolute;
top: calc(50% - 25px);
left: calc(50% - 25px);
width: 50px;
height: 50px;
background-color: #c00;
animation: rotate linear 6s;
animation-iteration-count: infinite;
animation-play-state: paused;
transform-origin: 50% 50%;
}
#rotating.animated {
animation-play-state: running;
}
#keyframes rotate {
0% {
transform: rotate(0deg) ;
}
100% {
transform: rotate(359deg) ;
}
}
<div id="stage">
<div id="rotating"></div>
</div>
Depending on which browsers you want/have to support, you might have to add vendor prefixes in the CSS code.

Transform $.show() to css

I got this css:
#pop {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0,0,0,0.8);
z-index: 10;
display: none;
}
Until now, in order to show this #pop div I used $("#pop").show(450);
How can I do it more "Cheaply" with css? I'd like to keep the same fade in effect $.show(ms) provides, not just display it.
well you can create animation and switch class on element something like:
$('#pop').addClass('show');
and you would need css something like this:
#pop {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0,0,0,0.8);
z-index: 10;
display: block;
visibility: hidden;
opacity: 0;
}
#pop.show {
visibility: visible;
opacity: 1;
transition: 1.45s all;
}
As far as I know display property doesn't support transitions so you will need to do it with opacity. You could potentially put both classes on same element to simulate feel of element becoming visible on page load.
Here's update with fiddle, you need to use visibility property:
https://fiddle.jshell.net/6jwfz608/
you can use JS to toggle classes using "className" and use transition in the CSS
CSS
.pop_hidden {
transition:all 450ms;
position: absolute;
top: 0;
left: -101vw;
width: 100vw;
height: 100vh;
background-color: rgba(0,0,0,0.8);
z-index: 10;
display: none;
}
.pop_shown {
transition:all 450ms;
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0,0,0,0.8);
z-index: 10;
display: none;
}
(css edit) i got rid of display and changed it to moving the div from out of frame going right.
JS
document.getElementById('pop').className = 'pop_hidden';//to load the hidden div you can use id too
setTimeout(() => {
document.getElementById('pop').className = 'pop_shown';
}, 20);///adjustable delay if needed(ex: set to var in game loop)
Edit: my opinion on using CSS transition in combination with setting classNames. It's easy and fun to do. For a fading effect, toggle opacity. for and slide effect, toggle positions, there are tons of creative ways to change your elements. And since the naming of classes is completely arbitrary, you can have multiple classes to switch to. Also, i switched it to class out of habit(SORRY). But it should not matter, you can toggle id's the same way.
Here's an example showing onclick functionality and handled with Vanilla Javascript and no Jquery, since you wanted something less weighty.
document.getElementById('showBill').addEventListener('click', function () {
var bill = document.getElementById('bill');
if (bill.classList.contains('hide')) {
bill.classList.remove('hide');
} else {
bill.classList.add('hide');
}
});
img {
opacity: 1;
transition: opacity 3s ease-in-out;
-moz-transition: opacity 3s ease-in-out;
-webkit-transition: opacity 3s ease-in-out;
}
img.hide {
opacity: 0;
}
<button id='showBill'>Show Bill</button>
<br/>
<img src='http://fillmurray.com/300/300' class='hide' id='bill' />

No exit animation applied - Polymer

I have a paper-icon-button that animates (spin & opacity) when I hover an image using on-mouseenter and on-mouseleave.
The animations occur properly on-mouseenter, but the paper-icon-button simply disappears on-mouseleave rather than repeating the animation.
Can anybody help?
HTML
<img id="avatar" class="userAvatar" src="../images/hipster.png" slot="item-icon" on-mouseenter="cogSpin" on-mouseleave="cogSpin"></img>
<paper-icon-button id="cogSpin" icon="settings" class="cog" on-click="doSomething"></paper-icon-button>
CSS
.cog {
position: fixed;
color: white;
top: 129px;
left: 64px;
opacity: 0;
transition: opacity 1s, transform ease-in-out 1s;
visibility: hidden;
}
.cogOn {
visibility: visible;
opacity: 1;
transform:rotate(360deg);
}
JS
cogSpin : function() {
// css class only applied if the drawer containing it has been expanded
if(this.$.drawer.classList.contains('drawerExpand')) {
this.$.cogSpin.classList.toggle('cogOn');
}
}
That's because visibility:hidden; and it's counterpart is not an animatable CSS property. (See dev docs regarding interpolation)
Change your CSS rule to:
.cog {
position: fixed;
color: white;
top: 129px;
left: 64px;
opacity: 0;
transition: opacity 1s, transform ease-in-out 1s;
}
.cogOn {
opacity: 1;
transform:rotate(360deg);
}
The visibility property is unnecessary in any case thanks to your use of opacity.

Categories