When does CSS transform actually get set? - javascript

I am making a simple slider using CSS transform.
It works like
Translate next slide to 100%
Add transitions to current and next
Translate next to 0, current slide to -100%
Remove transitions.
The problem is that next slide never gets translated. It seems to work fine in Chrome but not Firefox. My question is, how long it takes for browsers to really set the transform?
var curSlide = slides[index].style;
var nextSlide = slides[index + 1].style;
nextSlide['transform'] = 'translateX(100%)'; // never occurs
//Giv it some time ??? How long ???
setTimeout(function(){
nextSlide['transition'] = 'transform 0.2s';
curSlide['transition'] = 'transform 0.2s';
nextSlide['transform'] = 'translateX(0)';
curSlide['transform'] = 'translateX(-100%)';
},0);// if timeout is set to 100ms it works fine

Solution was to force a reflow before next translation.
console.log(elementTranslated.offsetHeight);
Thanks to this question Force browser to trigger reflow while changing CSS

Related

Problem synchronizing DOM manipulation with style changes in carousel

I built a basic picture carousel a while back, and I'm finally getting around to transferring it from MooTools over to jQuery so I can drop MooTools. I've got the script completely functional, but for whatever reason when the carousel slides in one direction, you can see a "pop" where it resets itself.
I've tried playing around with the order it handles everything, but no matter what it seems to always desync for just a fraction of a section.
Here's a copy of my code: https://jsfiddle.net/Chaosxmk/pf6dzchm/
The offending section of code is this:
styles['position'] = 'absolute';
styles[self.params.axis] = -32768;
$(self.list[0]).css(styles).hide();
$(self.list[0]).appendTo(self.carousel);
$(self.list[conf.mi]).css(self.params.axis, (100-conf.pr)+'%');
styles = {};
styles['position'] = 'relative';
styles[self.params.axis] = 'auto';
$(self.list[conf.mi]).css(styles);
Issue is that $.fadeOut() sets display:none on the element, which causes some strange rendering issues in your setTimeout() callback. Works better if you use $.fadeTo() instead:
if (self.params.direction) {
// Go forward
self.carousel.css(self.params.axis, '-'+conf.pr+'%');
$(self.list[0]).fadeTo(400, 0);
$(self.list[conf.mi]).css(self.params.axis, '100%').fadeTo(400, 1);
} else {
// Go backward
self.carousel.css(self.params.axis, conf.pr+'%');
$(self.list[conf.mi-1]).fadeTo(400, 0);
self.list.last().css(self.params.axis, '-'+conf.pr+'%').fadeTo(400, 1);
}
For simplicity I used a 400ms duration, but you can set this to whatever you need.
JSFiddle

JS: element.style = value; first time not executed when appearing twice

For a website I made a JS, to animate a div upon scroll. I was animating the CSS top value, but since I had a transition applied to that property, I had to come up with a way of animating "top" without the lag that CSS transitions results in.
I came up with this inside a function:
//Get the Div Element
var div = document.querySelector('.someclass');
//Disable Transition
div.style.transition= 'none';
//Change the top value - value was connected to scroll
div.style.top = anumber + 'px';
//Reset Transition to the CSS's file default
div.style.transition = '';
But this resulted in the unwanted lag again, because it somehow ignored the "Disable Transition" step.
To make sure that each step would be executed, I came up with the Idea to wrap the "Reset" step into a setTimeout function. I changed this line:
//Reset Transition to the CSS's file default
div.style.transition = '';
to
//Reset Transition to the CSS's file default
setTimeout(function () {
div.style.transition = '';
},1);
And Tadaa, it worked. But now I'm wondering, if there was a cleaner way to prevent the first line of not being executed, and of course an explenation why it even happened.
I appreciate all the help!
Changes to the DOM only get reflected to the underlying model when the CSS engine runs, which only happens if JS stopped running. By using setTimeout the execution of JS ends, the CSS engine has time to run, then the timer fires and JS runs again.
You could solve ut a bit more elegantly with:
const tick = () => new Promise(resolve => setTimeout(resolve));
(async function() {
//Get the Div Element
var div = document.querySelector('.someclass');
//Disable Transition
div.style.transition= 'none';
//Change the top value - value was connected to scroll
div.style.top = anumber + 'px';
await tick();
//Reset Transition to the CSS's file default
div.style.transition = '';
})();

Transition time ignored unless a DOM style property is queried before it's set

I'm creating a div using JavaScript and inserting it into a page. I'm doing this by changing the parent div's transform: translateY to shift it up by the div's height, inserting the div, then sliding it back down.
Here's the basic code:
attachTo.style.transform = 'translateY(-' + divHeight + 'px)';
attachTo.insertBefore(div, attachTo.firstElementChild);
attachTo.style.transition = 'transform 2s';
attachTo.style.transform = 'translateY(0)';
With that code the transform time is ignored and the added div pops in as normal. If I change it to something like this, however:
attachTo.style.transform = 'translateY(-' + divHeight + 'px)';
attachTo.insertBefore(div, attachTo.firstElementChild);
// Either of these can be used, as can any statement or expression that queries an element's CSS styles.
console.log(document.body.clientHeight);
var foo = pageWrap.offsetParent;
attachTo.style.transition = 'transform 2s';
attachTo.style.transform = 'translateY(0)';
The div will animate properly. Less surprisingly to me, I can also wrap the final transition and transform changes in a zero-length timeout.
The behaviour's the same in Firefox and Chromium.
My question is why this happens, and why the code isn't executed synchronously? I'm thinking it's related to the browser's execution queue, as covered in this question about zero-length timeouts, but not only would I like to know for certain that's the case, I'd also like an explanation on why using a DOM element's style property achieves the same effect (my guess is it creates a slight pause in execution).
When javascript runs a series of commands, the browser does not redraw the elements until it is done, or if one tell it to, and since the last command resets everything, nothing happens.
This is also the case with many other programming languages.
So one either have to put it in a function, in this case using a setTimeout, or call for example window.scrollHeight in between, to make the browser redraw.
Why a property is used is because javascript does not have a method, like for example Application.DoEvents(); in Dot.Net.
Src: What forces layout / reflow in a browser

jQuery height change not working in Safari

I have a JS feature on the following site that is working just fine in Firefox but not in Safari: http://rossbolger.com/kids/light-stories/
The feature slides out a grid of thumbnails called #image-thumbs when the mouse hovers over the container called #hit-area. It works (at least in Firefox) by first changing #image_thumbs height from '48px' to 'auto', the height is then measured using jQuery's height(). This height is stored in a variable and then using jQuery's css() it is given back to the #image-thumbs when the mouse is over.
The code on the site looks a little something like this:
// Thumbnails Nav Reveal and Hide Scripts
var thumbs_height = 1,
thumbs = $('#image-thumbs'),
thumbs_original_height = thumbs.css('height');
// Slide Up Thumbs
(function revealThumbs() {
// On hover let the thumbs become their full height
$('#image-thumbs #hit-area').hover(function(){ // Mouse over
// Get the unrestricted height of the thumbs
thumbs.css('height', 'auto');
thumbs_height = thumbs.height();
// then put it back to what it was so we can animate it using CSS3 transition
thumbs.css('height', 'inherit');
// delay 0.1s before triggering the change in height (time for the calculations to complete)
setTimeout( function() { thumbs.css('height', thumbs_height ) }, 100 );
}, function(){ // Mouse out
hideThumbs();
});
})();
// Hide thumbs
function hideThumbs(){
thumbs.css('height', thumbs_original_height );
};
The reason for measuring the unrestricted height and passing it back as a pixel value, rather than simply setting the height to 'auto', is to create a sliding effect via CSS3 (i.e. transition: height 0.5s). The transition only takes place if the affected attribute goes from one numeric value to another.
Thanks for any help bug testing this. I haven't even looked at other browsers yet.
All the best,
Laurence
Okay, so I've worked it out...
In the javascript document (scripts.js on the site) there was a function higher up the page calling the hideThumbs() function. So it wasn't working because the variables in hideThumbs() hadn't been declared at that point. Funny that it should still work in Firefox and IE9!
I've moved all this code to a point before that other function and the problem is now resolved. So far I've only done this locally. I'll update the site in the link above later.

Animating a div disappearance, how to smooth that?

I've been trying to animate a Dashboard Widget's div disappearance, but it just brutally goes "poof" (as in, disappears as expected, only instantly).
function removeElement(elementId)
{
duration = 9000; // The length of the animation
interval = 13; // How often the animation should change
start = 1.0; // The starting value
finish = 0.0; // The finishing value
handler = function(animation, current, start, finish) {
// Called every interval; provides a current value between start and finish
document.getElementById(elementId).style.opacity = current;
};
new AppleAnimator(duration, interval, start, finish, handler).start();
interval = 1;
start= "visible";
finish = "hidden";
duration = 9001;
handler = function(animation, current, start, finish) {
document.getElementById(elementId).style.visibility="hidden";
};
new AppleAnimator(duration, interval, start, finish, handler).start();
}
I expected this to "disappear" the div a millisecond after its opacity reaches zero, but for a not so obvious reason (to me), it just disappears immediately. If I comment out the second animation code, the div fades out (but it's still active, which I don't want).
All solutions I've yet seen rely on using JQuery and wait for the event at the end of the animation, is there another way to do that, other than JQuery?
If you are looking for a pure javascript solution it probably needs a good understanding of how javascript event work and basically about javascript language. As reference you should check this question on CodeReview
But as I think the best solution for you and not to rely on jQuery is to checkout CSS3 animations. Even if they are not supported by all browsers you could use Modernizer to fill polyfills for animations.
My favorite CSS3 Animation library is Animate.css. It's pretty neat and gives you a variety of demos in the page.
You'll first have to choose an animation and add it to your css stylesheets. Then have another custom class that contain everything about the animation.
.disappear{
-webkit-animation-duration: 3s;
-webkit-animation-delay: 2s;
-webkit-animation-iteration-count: infinite;
}
Then you could use javascript events to toggle in classes to your Elements. Below is how you add a class to an element.
var animationObject = document.getElementById("poof");
animationObject.className = animationObject.className + " disappear";
If you need more help regarding javascript of how this should be done check out this answer.
Hope this helps...
I found it: AppleAnimator possesses animator.oncomplete: A handler called when the timer is complete.
In my case:
var anim = new AppleAnimator(duration, interval, start, finish, handler);
anim.oncomplete= function(){
document.getElementById(elementId).style.visibility="hidden";
};
anim.start();
The Apple documentation actually calls "Callback" the animation code itself, and "handler" the callback, which makes it a bit hard to realize at first.
Thanks frenchie though, the "YourCallbackFunction" made me realize I was missing something related to callbacks :D

Categories

Resources