I want to make an image begin scaled all the way down in x in, and then animate all the way up in x when classes are added (via javascript). The pattern that I am using works well for things like rotate, but I am thinking this is only because rotate goes a full 360 degrees. I am not sure why this does not work:
CSS:
.scaleXStart {
visibility: hidden;
z-index: 0;
transform:scaleX(.5);
}
.scaleXEnd {
z-index: 3;
transform: scaleX(2);
transition: transform 1s;
}
Javascript:
a = document.querySelector('#myDiv');
a.className = 'scaleXStart';
a.className = 'scaleXEnd';
I would think this would work because it is adding and then remove a class, so the scaleXproperty would be set to 0 and then 1 but this is not working. Thanks for any ideas on why
The problem is, it never gets a chance to get the start class and it goes straight to the end class. Putting the end class change into a timeout (even zero milliseconds!) will trick it into doing the both class changes:
function anim(){
a = document.querySelector('#myDiv');
a.className = 'scaleXStart';
setTimeout(function(){a.className = 'scaleXEnd';}, 0)
}
function anim2(){
a = document.querySelector('#myDiv');
a.className = 'scaleXStart';
a.className = 'scaleXEnd';
}
See what I mean here: http://jsfiddle.net/shomz/nzJ8j/
Related
my question has 3 parts. Any assistance with any part of this JS problem would be greatly appreciated. I am attempting to learn and comprehend JS by trial and error.
I've created this nice looking travel landing page, https://portfolioprime.github.io/Nature%20carousel/glidejs.html with a thumbnail carousel which uses Glide.js, which is really cool and works well. The carousel moves to the left and has arrow buttons to manually control the slide.
But I've been trying to implement a vanilla JS carousel slider,but I am failing miserably. Been struggling for 2 days and the best I can achieve is getting a single carousel item moving left and right. See https://portfolioprime.github.io/Nature%20carousel/.
What I'd like is to get the carousel sliding left automatically, with arrow buttons to manually control the slider.
I'm targeting all the carousel-items with querySelectorAll('.carousel-items') and adding left:-274px to the carousel container glide__slides.
Here's my JS code.
// var & event-listener buttons
document.querySelector(".left").addEventListener("click", slideLeft);
document.querySelector(".right").addEventListener("click", slideRight);
// Function slide left
function slideLeft(left) {
document.querySelector('.glide__slides').style.left = left;
}
// Function slide left
function slideRight(right) {
document.querySelector('.glide__slides').style.left = right;
}
Secondly, I'd like to have an active carousel-item, which when active automatically changes the background Image.
Right now I have the hero.style.background = var; and I've got it changing onclick with onclick = function('01.jpg') on each carousel item.
Here's the code.
// Change Hero Img
function heroChange(hmmm) {
var hero = document.querySelector('.hero');
hero.style.background = hmmm;
}
So I guess I would add EventListeners to the carousel-items and add an active class to the carousel-item like so,
var slides = document.querySelectorAll('.carousel-items');
function changeBgImg() {
slides.forEach(s => s.classList.remove('active');
this.classList.add('active');
//change the bg image === this
//But I have no idea how to do that
}
Thirdly I've got the content, background and carousel indicators using the same functions above but it seems like really dirty code. The HTML has each .carousel-item, there are ten of them, calling 4 functions each. It looks like this:
<div class="glide hero-carousel">
<div class="glide__track" data-glide-el="track">
<ul class="glide__slides">
<li class="glide__slide carousel-item"
onclick="heroChange('url(images/02.jpg) bottom/cover no-repeat');
number('01');
h4('Destination Shire');
h1('Valley<br> of Dreams');">
<div class="carousel-text">
<p>Destination Shire</p>
<h3>Valley<br> of Dreams</h3>
</div>
</li>
<li class="glide__slide carousel-item"
onclick="heroChange('url(images/03.jpg) bottom/cover no-repeat');
number('02');
h4('Destination Westwood');
h1('Misty<br> Woodlands');">
<div class="carousel-text">
<p>Destination Westwood</p>
<h3>Misty<br> Woodlands</h3>
</div>
</li>
</ul>
</div>
</div>
So it looks pretty yucky. It works though, but I would love to find a more elegant way of achieving this by putting all of these functions into one function that does each part in sequence.
Lastly, I'd want to get transition on-click animations going but that's another kettle of fish entirely.
So that's it. Whew!
Thanks for taking the time guys, I appreciate it. Any help you can provide is going to make me a better designer. There are actually a bunch of projects I have will benefit from the answers.
If you can provide help with at least Part 2 & 3: cleaning up the code into 1 function and getting the bg-image changing on the active class that would be a big big help.
There's just so much that JS can do and I'm not finding the answers on Google and youTube.
Thank you again.
An Update:
I have edited the slider by by using margin-left as shown by this question:
vanilla javascript carousel not sliding
// var & event-listener buttons
document.querySelector(".left").addEventListener("click", slideLeft);
document.querySelector(".right").addEventListener("click", slideRight);
let marginLeft = 0;
const slides = document.querySelector('.glide__slides');
// Function slide left
function slideLeft() {
marginLeft += 264;
slides.style.marginLeft = marginLeft + 'px';
console.log(getComputedStyle(slides).marginLeft);
}
// Function slide Right
function slideRight() {
marginLeft -= 264;
slides.style.marginLeft = marginLeft + 'px';
console.log(getComputedStyle(slides).marginLeft);
}
This has now got the carousel moving manually 1 slide at a time.
Still not fully understanding why my previous code above didn't work. If anyone can explain that to me that would be great.
I'm still left with some issues:
Autosliding and looping at the end of the slides.
Having the active slider change the background automatically. At this point it only changes onclick.
Finding a way to tidy up the function calls and functions.
The question asks for various ideas on how to simplify code and how to use native JavaScript to create a slider that rolls continuously.
The code originally used glider and it may be something simpler would be sufficient to get the desired result, for example using animationend event to change the background when a slide gets to the left hand side. However, eating the elephant slowly I'll tackle the yucky code (part 3) first.
Although the HTML looks rather daunting, 4 calls on a click for every li element for example, it is currently what is required so let's investigate creating it at run time. This gives us more easily maintainable code. For example, if we want to remove a slide, or alter the order of slides or add one we can just alter the slider array defined below and JavaScript will do the rest.
Part 1 of the question asked about sliding. We slide the whole ul element using CSS animation defined something like this, where 33vw is the total width of a slide (inc. margins/padding)
#keyframes sliding0 {
0% { left: 0; }
30% { left: 0; }
100% { left: -33vw; }
}
and we add an event listener to the element to trap animationend events because when the ul has slid one slide's width we want to change the hero image, and we want to put the slide that has just disappeared onto the back of the infinie sliding will work. We then set the animation running again.
See the snippet for details on how this and other events are dealt with. It also shows how the changeHero function can work which was part 2 of the question. Note, the snippet works more or less in the SO environment, though occasionally hover action is partially ignored. Running the code on your own machine it should be fine though.
<!DOCTYPE html>
<html>
<head>
<style>
#keyframes sliding0 {
0% { left: 0; }
30% { left: 0; }
100% { left: -33vw; }
}
#keyframes sliding1 {
0% { left: 0; }
30% { left: 0; }
100% { left: -33vw; }
}
body {
background-repeat: no-repeat no-repeat;
background-size: cover;
background-position: center center;
}
div .glide_track {
position: relative;
width: 100vw;
overflow-x: hidden;
}
ul {
position:relative;
left: 0;
width: 330vw;
height:100vh;
animation-name: sliding0;
animation-duration: 3s;
animation-delay: 0s;
animation-iteration-count: 1;
animation-timing-function: linear;
margin: 0;
padding: 0;
list-style-type: none;
}
li {
position: relative;
left:0;
top:0;
float:left;
width: 32vw;
height:30vw;
display: inline-block;
margin: 0;
margin-right: 1vw;
padding: 0;
background-size: cover;
background-repeat: no-repeat no-repeat;
background-position: center center;
}
</style>
</head>
<body>
<script>
// we put the two lots of text and the image url for each slide in an array in the order they are to be shown
// this makes it easier to maintain when you want to add or remove a slide or change their order
// we only have one slider at the moment but this makes it more general
// these are the offsets in the array describing a slide. Done as indexes rather than named as easier to set up sliders array
const img = 0;
const text1 = 1;
const text2 = 2;
const sliders = [
[
['https://ahweb.org.uk/boxfordmosaic.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/gear-in-turbine-house-reading.jpg','Westwood','Misty Woodlands'],
['https://ahweb.org.uk/tricycle-in-abbey-ruins.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/boxfordmosaic.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/gear-in-turbine-house-reading.jpg','Westwood','Misty Woodlands'],
['https://ahweb.org.uk/tricycle-in-abbey-ruins.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/boxfordmosaic.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/gear-in-turbine-house-reading.jpg','Westwood','Misty Woodlands'],
['https://ahweb.org.uk/tricycle-in-abbey-ruins.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/tricycle-in-abbey-ruins.jpg','Shire','Valley<br> of Dreams']
]
];
// go through each slider and create its outer divs and its ul element
sliders.forEach(createSlider);
function createSlider(slider,sliderno) {
const div1 = document.createElement('DIV');
const div2 = document.createElement('DIV');
const ul = document.createElement('UL');
div1.classList.add("glide","hero-carousel");
div2.classList.add("glide_track");
div2.setAttribute("data-glide-el","track");
div1.appendChild(div2);
div2.appendChild(ul);
document.body.appendChild(div1);
ul.classList.add("glide__slides");
ul.addEventListener("animationend", animationEnd);
slider.forEach(createLi);
function createLi(slide,slideNo) {
const li = document.createElement('LI');
li.classList.add("glide__slide","carousel-item");
li.style.backgroundImage='url('+slide[img]+')';
li.addEventListener("click",slideClicked);
li.addEventListener("mouseover",slideHovered);
li.addEventListener("mouseout",slideUnhovered);
li.setAttribute('data-slideno','0' + slideNo);//! needs generalising if you have >10 slides !
ul.appendChild(li);
const div = document.createElement('DIV');
const p = document.createElement('P');
const h3 = document.createElement('H3');
p.innerHTML = slide[text1];
div.appendChild(p);
h3.innerHTML = slide[text2];
div.appendChild(h3);
li.appendChild(div);
}
}
// this is for testing, in real version use whatever required (i.e. whichever element is to have the hero image)
function ahHeroChange(backgroundImage) {
document.body.style.background = backgroundImage + " bottom/cover no-repeat";
}
function slideClicked(event) {
var slide = event.target;
var slideNo = slide.getAttribute('data-slideno');
// make the hero image the same as the slide's
ahHeroChange(slide.style.backgroundImage);
/* I don't know what these functions do - they were executed in the original on a click
number(slideno);
h4(slide.firstElementChild.querySelector('p').innerHTML);// text1 of the slide is passed to h4
h1(slide.firstElementChild.querySelector('h3').innerHTML;// text2 of the slide is passed to h1
*/
}
function slideHovered(event) {
var slide = event.target;
var slider = slide.parentElement;
slider.style.animationPlayState = 'paused';
ahHeroChange(slide.style.backgroundImage);
}
function slideUnhovered(event) {
var slide = event.target;
var slider = slide.parentElement;
//restore the hero image to the first one in the slider
ahHeroChange(slider.firstElementChild.style.backgroundImage);
//get the animation running again
slider.style.animationPlayState = 'running';
}
function animationEnd(event) {
//find the element that was clicked (it will be a ul element representing a slider)
var slider = event.target;
//take the first slide off the list and put it back at the end
slider.append(this.firstElementChild);
//change the hero image to the slide which is now the leftmost - use modified heroChange in the final version
document.body.style.backgroundImage = this.firstElementChild.style.backgroundImage;
// toggle the animationName (to an identical keyframes action) to force the animation to start again
slider.style.animationName='sliding'+(Number(event.animationName.replace('sliding',''))+1)%2;
}
</script>
</body>
</html>
I know this is fairly easy in jQuery, but I want to do this in plain 'ol "will be around forever" javascript.
I have a dropdown select on my page. I choose one of 8 options. There is a default image showing on the page. When I select an option, the image changes to that pic. It all works fine.
But I want to make the image change a fade out, fade in switch over because I, like most of you, can't leave well alone. We have to keep fiddling.
The javascript that I have, which is triggered by an onchange="setPicture()" on the select dropdown is:
function setPicture(){
var img = document.getElementById("mySelectTag");
var value = img.options[img.selectedIndex].value;
document.getElementById("myImageDiv").src = value;
}
This works fine. The value of the selected index is a string with the path for each image. I just want a fade out then fade in stuck in there somewhere. I have fiddled about a bit, calling another function before changing the src but no luck.
Can someone point me in the right direction?
The easier way would be to use css keyframes alone.
But from javascript there is the web animation api made for that.
Here is a quick modif from the example, to match your case.
function setPicture(){
alice.animate(
[
{ opacity: 1 },
{ opacity: .1},
{ opacity: 1 }
], {
duration: 3000,
iterations: Infinity
}
)
}
<button onclick="setPicture()">
OPACITY ANIMATION
</button>
<img id="alice"
src="https://mdn.mozillademos.org/files/13843/tumbling-alice_optimized.gif"
>
</img>
How about setting the image default CSS with the opacity of 0 and with transition time
then in JavaScript just add a class that will make the opacity set to 1
HTML:
<img class="img1" src="sampleimg.jpg">
CSS:
.img1 {
opacity: 0;
transition: all .3s;
}
.img1.show {
opacity: 1;
}
JS:
function setPicture() {
var img = document.querySelector('.img1');
img.src = 'urlofnewimage';
img.classList.add('show');
}
Hope this helps.
Juste one function for all :
function fadeOutEffect(target) {
var fadeTarget = document.getElementById(target);
fadeTarget.style.opacity = 1;
fadeTarget.style.transition = "opacity 2s";
fadeTarget.style.opacity = 0;
setTimeout(function(){
fadeTarget.style.display = "none";
}, 2000);;
}
This question already has answers here:
How can I force WebKit to redraw/repaint to propagate style changes?
(33 answers)
Closed 4 years ago.
Currently I am working on an animation for a website which involves two elements having their position changed over a period of time and usually reset to their initial position. Only one element will be visible at a time and everything ought to run as smoothly as possible.
Before you ask, a CSS-only solution is not possible as it is dynamically generated and must be synchronised. For the sake of this question, I will be using a very simplified version which simply consists of a box moving to the right. I shall be referring only to this latter example unless explicitly stated for the remainder of this question to keep things simple.
Anyway, the movement is handled by the CSS transition property being set so that the browser can do the heavy lifting for that. This transition must then be done away with in order to reset the element's position in an instant. The obvious way of doing so would be to do just that then reapply transition when it needs to get moving again, which is also right away. However, this isn't working. Not quite. I'll explain.
Take a look at the JavaScript at the end of this question or in the linked JSFiddle and you can see that is what I'm doing, but setTimeout is adding a delay of 25ms in between. The reason for this is (and it's probably best you try this yourself) if there is either no delay (which is what I want) or a very short delay, the element will either intermittently or continually stay in place, which isn't the desired effect. The higher the delay, the more likely it is to work, although in my actual animation this causes a minor jitter because the animation works in two parts and is not designed to have a delay.
This does seem like the sort of thing that could be a browser bug but I've tested this on Chrome, Firefox 52 and the current version of Firefox, all with similar results. I'm not sure where to go from here as I have been unable to find this issue reported anywhere or any solutions/workarounds. It would be much appreciated if someone could find a way to get this reliably working as intended. :)
Here is the JSFiddle page with an example of what I mean.
The markup and code is also pasted here:
var box = document.getElementById("box");
//Reduce this value or set it to 0 (I
//want rid of the timeout altogether)
//and it will only function correctly
//intermittently.
var delay = 25;
setInterval(function() {
box.style.transition = "none";
box.style.left = "1em";
setTimeout(function() {
box.style.transition = "1s linear";
box.style.left = "11em";
}, delay);
}, 1000);
#box {
width: 5em;
height: 5em;
background-color: cyan;
position: absolute;
top: 1em;
left: 1em;
}
<div id="box"></div>
Force the DOM to recalculate itself before setting a new transition after reset. This can be achieved for example by reading the offset of the box, something like this:
var box = document.getElementById("box");
setInterval(function(){
box.style.transition = "none";
box.style.left = "1em";
let x = box.offsetLeft; // Reading a positioning value forces DOM to recalculate all the positions after changes
box.style.transition = "1s linear";
box.style.left = "11em";
}, 1000);
body {
background-color: rgba(0,0,0,0);
}
#box {
width: 5em;
height: 5em;
background-color: cyan;
position: absolute;
top: 1em;
left: 1em;
}
<div id="box"></div>
See also a working demo at jsFiddle.
Normally the DOM is not updated when you set its properties until the script will be finished. Then the DOM is recalculated and rendered. However, if you read a DOM property after changing it, it forces a recalculation immediately.
What happens without the timeout (and property reading) is, that the style.left value is first changed to 1em, and then immediately to 11em. Transition takes place after the script will be fihished, and sees the last set value (11em). But if you read a position value between the changes, transition has a fresh value to go with.
Instead of making the transition behave as an animation, use animation, it will do a much better job, most importantly performance-wise and one don't need a timer to watch it.
With the animation events one can synchronize the animation any way suited, including fire of a timer to restart or alter it.
Either with some parts being setup with CSS
var box = document.getElementById("box");
box.style.left = "11em"; // start
box.addEventListener("animationend", animation_ended, false);
function animation_ended (e) {
if (e.type == 'animationend') {
this.style.left = "1em";
}
}
#box {
width: 5em;
height: 5em;
background-color: cyan;
position: absolute;
top: 1em;
left: 1em;
animation: move_me 1s linear 4;
}
#keyframes move_me {
0% { left: 1em; }
}
<div id="box"></div>
Or completely script based
var prop = 'left', value1 = '1em', value2 = '11em';
var s = document.createElement('style');
s.type = 'text/css';
s.innerHTML = '#keyframes move_me {0% { ' + prop + ':' + value1 +' }}';
document.getElementsByTagName('head')[0].appendChild(s);
var box = document.getElementById("box");
box.style.animation = 'move_me 1s linear 4';
box.style.left = value2; // start
box.addEventListener("animationend", animation_ended, false);
function animation_ended (e) {
if (e.type == 'animationend') {
this.style.left = value1;
}
}
#box {
width: 5em;
height: 5em;
background-color: cyan;
position: absolute;
top: 1em;
left: 1em;
}
<div id="box"></div>
I have a message which I display on the screen when a user clicks on some links. I use a transition on the opacity to get the message to fade away.
The problem is that when a user clicks on the next link which is supposed to display the message, the element has its opacity set to 0 (thus it's not visible).
The opacity transition is triggered by a JavaScript function.
My question: would it be possible to reset the opacity (back to 1) before the transition effect happens?
I only see a nasty way such as triggering a function from within the function that triggers the opacity transition, to reset the opacity back to 1. Something like:
setTimeout(function(){elem.style.opacity = 1;)}, 3000);
But this is not great because I'd like the opacity to be reset as soon as a user clicks another link for which this message is displayed.
ideas?
EDIT:
Fiddle
HTML:
<div id="pop_up" class="pop_up">negative</div>
<a class="something_else" href="#" onclick="show(event, this); return false;">toto</a>
<a class="something_else" href="#" onclick="show(event, this); return false;">titi</a>
CSS:
.pop_up
{
position: absolute:
top: -10px;
padding: 10px;
background-color: orange;
opacity: 1;
transition: opacity 1s linear;
}
JS:
function show(e, elem)
{
msg = document.getElementById("pop_up");
msg.style.top = elem.offsetTop;
msg.style.left = elem.offsetLeft;
msg.style.opacity = 0;
msg.innerHTML = "Hug Me!";
}
I think I know what you want. You need to reset the transition to none and back again each time, and also reset the opacity each time and hide/show. Using the following CSS:
.pop_up
{
position: absolute;
display: none;
padding: 10px;
background-color: orange;
}
and the following javascript:
function show(e, elem) {
var msg = document.getElementById("pop_up");
msg.style.transition = 'none';
msg.style.display = 'none';
msg.style.opacity = 100;
msg.innerHTML = "Hug Me!";
msg.style.display = 'block';
msg.style.top = '' + elem.offsetTop + 'px';
msg.style.left = '' + elem.offsetLeft + 'px';
msg.style.transition = 'opacity 2s linear';
msg.style.opacity = 0;
}
I think you get the effect you want. I have changed to a 2s transition to see ity better, that you can adjust. The point is to hide and reset the popup each time, then reset the transition and show so that the transition runs again.
I have made msg a local vairable (with the vardeclaration) but also agree with the comments that using global functions and inline event handlers like this is not ideal. Improvments to that depnd on if you want to use a js library and if so which or if you want to stick to pure js.
Working fiddle (only tested in Firefox).
This technique stopped working with me, until I nailed done what actually make it work. It's necessary to keep this line in the JS code:
msg.style.bottom = elem.offsetTop + 'px';
If you remove it, it seems like the CSS for the element is not re-evaluated, which means the transition is actually not reset from 'none' to 'opacity 1s ease' for instance, which actually triggers the transition. So, if you remove the lines that actually reset the position of the div, the css won't be re-evaluated. In my case, I ended up needing the element to have a fixed position. So I first do:
msg.style.bottom = elem.offsetTop + 'px';
Immediately followed by:
msg.style.bottom = 0;
The first call internally forces the transition to be reset from none to something else, and then of course I finally positioned the element where I want.
Note that the first line might as well be:
var tmp = elem.offsetTop;
What's important here, is to change the state of the element, so that its CSS is being internally re-evaluated by the browser engine.
I am creating event's page and at the bottom I placed many logos. It sliding from right to left. I don't use jQuery, only pure Javascript and I just wondering about the best performance. My code works, but maybe there is better way to do that ? I think this 'animation' sometimes slow down.
var banners = [],
links = [];
links[0] = 'http://...',
banners[0] = 'img/logo1.png',
...
var banLenght = banners.length,
banContent = "<div id='bannersBack'><div id='banners' style='display:inline-block;'>";
for (var ii =0; ii < banLenght; ii++){
banContent += "<a target='_blank' href='"+links[ii]+"'><img src='"+banners[ii]+"'></a>";
}
banContent += "</div></div>";
document.getElementById('sliding-logos').innerHTML = banContent;
var actual = document.getElementById('banners');
var move = function(){
position = actual.offsetLeft;
position -= 1;
actual.style.left = position +"px";
// 3000 is sum of banner's width
if (position > -3000) {
setTimeout( move, 20);
}else {
actual.style.left = "0px";
move();
}
};
move();
single image sprite rather than multiple image
CSS Transitions rather than JS. CSS is part of the browser engine and doesn't have to modify the properties of the DOM so it should be faster
Here's an example (however it is does NOT work in all browsers yet)
http://css-tricks.com/infinite-all-css-scrolling-slideshow/
Best performance is achieved by using CSS transforms+translate. Modern browsers will be able to use the GPU to do the transformation.
.animation {
transition: .25s ease-in-out;
transition-property: transform, width;
}
.move {
width: 200px; // set width to 200px
translateX(-200px); // move 200px to the left (always relative)
}
Typically, if you move large images or large DOM Nodes, you will see some stuttering. With CSS transform you get no stuttering.
If you can't use CSS transform (because you need it to work in IE8 or lower) I'd use jQuery's .animate.