If, for example, you put a CSS hover effect on an element, and also put a JS mouseenter event on it, which one will happen first? Is there any variance with this? Can you control it somehow? Is it possible to force them to execute in a particular order?
which one will happen first?
Notice that there is no such thing as a "CSS event". However, the behaviour is undefined; you could consider the CSS change and the JS event to happen at the same time. The relevant specs CSS Selectors 4, DOM 3 Events and HTML 5 point out the similarities between hover and mouseenter, but do not specify an order. Mouse event order is specified, but does not refer to CSS user action pseudo classes.
Is there any variance with this?
Yes, browsers are free to implement it either way. They could change the layout and redraw the page before they fire the JS events, or they could not. It should however not make much difference.
Is it possible to force them to execute in a particular order?
I personally would expect in the CSS to be applied already when the JS event handler is executed. Even if it was not yet computed, when querying styles (e.g. getComputedStyle(this)) a CSS recomputation is done so that you should always get the dynamic styles - see also When does reflow happen in a DOM environment?.
Try it out at http://jsfiddle.net/n4Z8H/. While most major browsers will yield the expected value (rgb(0, 0, 255), the blue :hover style), older IEs don't seem to do.
Depends a lot on how the browser works, and shouldn't be relied on. Most browsers should run the two at almost exactly the same time. If you want one to execute before/after the other, just control the CSS styling via JavaScript, for example on hover add a class and on not hover remove the class.
Although, if this is an actual issue you have, you're probably doing something wrong.
Related
Introductory information:
I've made a fixed menu button to show the navigation menu when using a mobile device. For this application I'm using the Headroom.js script to make the button smaller when scrolling downwards to ensure that it doesn't block too much of the content. The animation/transition is applied by adding a class with the given changes.
In the original method i changed the size and look of the button by changing height/width of the parent element and padding of the child element with CSS (and css transition).
The new method, which I've read could/should be better according to various sites, is changing the size of the button by using transform: scale(). Note that i'm also moving the element a bit by also applying translate3d(20px,20px,0) in this method. However, it feels a bit smoother when scrolling using the transform: scale() method (could be a placebo effect though), but using chrome dev tools' timeline gives me seemingly inconclusive results.
Therefore a part of my question is also how I should evaluate the best method. Is timeline in Chrome Dev tools the best option, or is there a better way to do it? And which elements of the timeline should I base my choices on? and the other thing is, based on your interpretation of the images and/or tests combined with your knowledge, which method performs the best (or should perform the best in theory)?
Beneath you can see two examples of the timeline with each method.
Changing height/width and padding (original method):
Method using transform: scale() to change the size:
Also you can try the different methods in fiddles here:
link: Original method changing height/width and padding
link to new method: using transform:scale
Please ignore the poor layout of everything; especially the button. The ugly image inside the menu button is just to show, that there's an image included in the layout on my own page and to take that into performance considerations. The images in the back is also included since it's a webshop with a lot of images which could influence performance.
CSS for added class that makes the changes in the original method:
.mobile-nav.headroom--unpinned {
height: 40px;
width: 40px;
}
.headroom--unpinned .mobile-content{
padding-top:4px;
}
CSS for the added class using transform:scale():
.mobile-nav.headroom--unpinned {
transform:scale(.5) translate3d(20px,20px,0);
}
So to summarize my questions:
How do I evaluate which methods has the best performance, and which method would you say performs the best?
A final note: I know that the methods are different (animating different things and more elements in the original method) but these are the 2 options which i prefer as it is right now.
I believe you are missing the point, Chris: the reason why no other property but transform and opacity should ever be animated is because they don't trigger a repaint in anything else, even if the element is in the document flow (and because you can basically do anything with these two alone in like 95% of the cases).
From the "hit-on-performance" point of view, there are two types of animations:
those that trigger a repaint in other elements than the animated element
those that do not.
That's the main reason behind recommending animations by transform, opacity or position:relative;left|right|top|left. Because they don't actually move the element in flow, thus not triggering a repaint to every single other element in flow after the one being animated.
Now, if the said parent was positioned absolute (which I assume to be the case), it wouldn't have triggered a repaint to the rest of DOM anyway so the differences between that method and transform would have been minor. Inconclusive, as you put it. In theory, repainting two elements instead of one should be slower.
If you need to test, make 10k clones and trigger animation on all of them, with each method.
That will be conclusive.
If you really want to min-max this (as in spend absurd amounts of time on hardly noticeable improvements, as I do) you will find plenty of resources that will recommend:
replacing any .animate() with .velocity()
never animating anything but transform or opacity, although Velocity claims they animate anything without a hit on performance (i find that debatable/arguable, at best) - but it's a net improvement over .animate()
sticking to CSS transitions, if possible (basically if you don't need chains)
using Web Animations API
Personal advice: never count on synced CSS animations, especially when you have many of them. If you change tabs or the system does something extremely resource heavy for a while, your animations will be way off. If you need chains, chain.
I have noticed that (using jQuery in Chrome 43) transitions are disabled when the element has display: none. Is this standarized behavior on all browsers, a feature of jQuery, or is it something that cannot be relied on in production?
The transition is enabled when the CSS statements to be animated are changed in a deferred function. Take a look at this JSFiddle. Uncomment line 3 or 6 to see it for yourself.
SOLUTION:
This behavior cannot be relied upon in production as it seems to be a product of optimization/design choices rather than specification (as per #Andriy Horens answer). Instead you should turn on and off the animation (transition-property: none) with a class. Failing to use a class rendered it unreliable for me in Chrome 43. Chrome did also require separate frames (defer the code with a timeout of 0) to update some CSS statements. Just defer anything related to animations if you want to save development time.
According to MDN:
Display
In addition to the many different display box types, the value none lets you turn off the display of an element; when you use none, all descendant elements also have their display turned off. The document is rendered as though the element doesn't exist in the document tree.
So i think elements with display set to none will not be rendered at all across all browsers and therefore transition or any other visual effect won't be applied.
You can also test yourself by subscribing to transitionend event:
$(element).on("transitionend", function () {
console.log("transition ended");
});
Update:
It is also per w3c specification:
And some values (such as display:none, display: contents, and box-suppress: discard) cause the element and/or its descendants to not generate any boxes at all.
Where boxes are visual representations of element. And transition is definitely a part of visual representation as it also can affect layout e.g. when you change relative position of element with transition applied.
Here is one more example of how different are animations of elements with display : none and visibility : hidden in other words of rendered element and not-rendered one.
JSFiddle DEMO
I am a developer for a web application. In this application there is a certain scenario where there are multiple position:fixed elements, and canvases and a overflow:scroll element. In this scenario, scrolling is super slow on firefox when smooth scrolling is enabled.
From the user's perspective the solution is simply to disable smooth scrolling. However, as a developer I can't ensure that the user has done this.
Is there anywhere that I can tell firefox to not to use smooth scrolling for my website from javascript (or html)? Or is there any other known workaround for this?
I do understand that your question basically is how to disable smooth scrolling. however I will answer you a little differently to get this working.
Why different?
Even if you can detect smooth scrolling of users, you cannot force the user to disable it. In other words, you are trying to cover the problem instead of solving it. so lets solve it!
Intro: pixels-to-screen pipeline
On each frame the browser does the following steps to render the page on screen.
JavaScript. Typically JavaScript is used to handle work that will result in visual changes, whether it’s jQuery’s animate function, sorting a data set, or adding DOM elements to the page. It doesn’t have to be JavaScript that triggers a visual change, though: CSS Animations, Transitions, and the Web Animations API are also commonly used.
Style calculations. This is the process of figuring out which CSS rules apply to which elements based on matching selectors, e.g. .headline or .nav > .nav__item. From there, once rules are known, they are applied and the final styles for each element are calculated.
Layout. Once the browser knows which rules apply to an element it can begin to calculate how much space it takes up and where it is on screen. The web’s layout model means that one element can affect others, e.g. the width of the element typically affects its children’s widths and so on all the way up and down the tree, so the process can be quite involved for the browser.
Paint. Painting is the process of filling in pixels. It involves drawing out text, colors, images, borders, and shadows, essentially every visual part of the elements. The drawing is typically done onto multiple surfaces, often called layers.
Compositing. Since the parts of the page were drawn into potentially multiple layers they need to be drawn to the screen in the correct order so that the page renders correctly. This is especially important for elements that overlap another, since a mistake could result in one element appearing over the top of another incorrectly.
details and source: https://developers.google.com/web/fundamentals/performance/rendering/?hl=en
Step 1:
First step is to remove render costly css properties. You might not be able to remove alot, however you can replace rgba(255,255,255,1); with #fff which removes the alpha layer.
check this for more details: https://csstriggers.com/
some properties do not need to do a layout or a paint and there are less heavy than others.
Step 2:
Check for forced synchronous layout triggers. These happen when you force the browser to do a layout while its in the javascript step, then return to javascript, instead of walking smoothly along the pipeline on each frame. to do this, avoid getting layout attributes and setting them directly afterwards in a loop for example.
here is a list of what causes sync layout: https://gist.github.com/paulirish/5d52fb081b3570c81e3a
read more: https://developers.google.com/web/tools/chrome-devtools/profile/rendering-tools/forced-synchronous-layouts?hl=en
Step 3:
Move components on the page that need to be repainted regularly into new layers.
The browser needs to repaint every time you scroll or an animation is playing. to avoid a full page repaint and only repaint the part that is changing, move that part (ex parallax, navigation, animation) to a new layer on the browser (think about it like photoshop layers)
to do so use the will-change css property to tell the browser to move it to a new layer, and use transform: translateZ(0); if you want to force the broswer to move it.
Have you tried adding
backface-visibility: hidden;
to you fixed position elements?
I would rather fix the source of the problem. Often there is one small detail that creates a giant bottleneck and that is easy to fix with the change of one line of code or something. Note that you most probably won't need to reduce the "good looks" of the app at all; it's just a matter of avoiding the small but devastating-for-performance details of the browser's layout engine.
I'll make a guess and say that something on you web app is causing very large repaints and/or frequent reflows. Check for things like usage of offsetTop and position: fixed. Also using requestAnimationFrame instead of updating for every scroll event is something worth looking at. Here's a good guide on both finding and fixing scrolling performance problems.
Use inspect element to try and get a handle on the specific cause.
Also, if you've not installed FireBug, install it and use it instead of the default inspect element. This will give you more code details and allow you to step through the script to find the problem.
There also plugins for FireBug for various frameworks, which can aid the diagnostics if your using one of those frameworks.
We can make assumptions about the cause or come up with shotgun solutions; but, only you can diagnose your code to find the specifics.
Please test this demo.
The whole layout bounce back and forth at the beginning. It seems that it is waiting for Javascripts code interpretation.
I use this code to set the width.
document.getElementById("headc").setAttribute("style","width:800px;");
Don't set the initial width using Javascript. Do it directly in the HTML or CSS.
There are some instances where you want to set it with javascript like this but only when you need some initial effect like divs arriving at the center and so on. If you only need static initial declaration then you should use inline definition or css. For one, there is certain delay between loading DOM structure and firing javascript (in case you are firing js after the DOM is loaded which you should in this case). For fwo, you are forcing some additional computational power to be used where it is not necessary.
Imagine an icon embedded in text that should be perceived as its part and behave like a letter. To achieve this it should have the same color as the text around. Finding the right color is easy in jQuery.
The problem is that the color may change. Either a script may change the CSS value, or a different value may be set in the :hover pseudoclass, and so on. In the case I've run onto it's the :hover value of a div several levels above. The text color around the icon changes, but how can I detect this event in order to adjust the icon itself?
I might be mistaken but I am sure that there is no such thing than a "change event" for CSS styles. In some modern browsers there is a DOMSubtreeModified event, but I don't think that this will even be triggered merely by changing an attribute like this.
So the only way to determine changes here would be to set an interval (window.setInterval) and check for changes every x milliseconds (choose a suitable x here).