I'm currently starting on an animation project. In the project I'll have more than 40000 divs and animate them iteratively. If any of divs are in passive state (i.e. it's not animating at least for 2 seconds), I won't display them to increase animation performance.
The question is: which css property is the most suitable for this?
.passive1{
display:none
}
.passive2{
visibility:hidden;
}
.passive3{
opacity:0;
}
And how can I measure rendering performance like fps, gpu usage?
While all 3 properties make an element's box seem invisible, there are crucial differences between them:
Property
Painted
In layout
Stacking context
Pointer events
Keyboard events
opacity: 0;
No
Yes
New
Yes
Yes
visibility: hidden;
No
Yes
Varies
No
No
display: none;
No
No
Varies
No
No
The "Painted" column indicates if the browser will paint the element's background (e.g. background-image), #text content, and so on.
An element cannot be painted without also participating in the page's layout, of course.
This is No for all 3 properties and values, as the browser won't need to paint the element's box as it's invisible.
The "In layout" column indicates if the browser will compute the layout and dimensions of the element - along with any of its descendants not excluded from layout.
This is only No for display: none;, as with opacity: 0; and visibility: hidden; the browser will still determine the size of the element so it can correctly layout other elements relative to the current element (e.g. if you have span.hidden { visibility: hidden; display: inline; }).
The "Stacking context" column indicates that any use of opacity (except opacity: 1.0;) will create a new stacking-context, which complicates use of the position property.
The "Pointer events" column indicates if the element will respond to user-interaction from a pointing device, such as a mouse, touch-screen, stylus, etc.
e.g. with visibility: hidden; then the :hover state won't work, and clicking the same element won't apply :focus or :active either.
Additionally, the DOM won't raise any pointer events you'd handle in JavaScript (e.g. visibility: hidden; won't raise mouseclick, touchstart, etc - note that the click event can still be raised by certain elements, like <button> if invoked by the user using a non-pointer input method, such as with keyboard or voice (accessible) navigation means.
You can use pointer-events: none; to block pointer events, but this won't block keyboard and other non-pointer input and so should not be used to disable an element because the user can still use the keyboard to interact with it (especially <button>, <input />, <select>, and <textarea>).
The "Keyboard events" column indicates if the element can be interacted-with using keyboard navigation (and possibly other navigation means).
This includes smart-device (smartphones' and tablets') browsers' "Prev/Next Field" buttons for navigating <form> elements (as this uses tabindex).
Unlike how pointer-events can be disabled in CSS using pointer-events: none;, there is no CSS property to disable keyboard interaction.
This table shows a more complete comparison between the main values of those 3 properties:
Property
Painted
In layout
Stacking context
Pointer events
Keyboard events
Animatable
Opacity
opacity: 0;
No
Yes
New
Yes
Yes
Yes
opacity: 0.1;
Yes
Yes
New
Yes
Yes
Yes
opacity: 0.9;
Yes
Yes
New
Yes
Yes
Yes
opacity: 1;
Yes
Yes
Varies
Yes
Yes
Yes
Visibility
visibility: hidden;
No
Yes
Varies
No
No
Yes, with caveats
visibility: visible;
Yes
Yes
Varies
Yes
Yes
Yes, with caveats
Display
display: none;
No
No
Varies
No
No
No
display: contents;
Text and children only
Text and children only
Varies
Yes
Yes
No
Other
pointer-events: none;
N/A
N/A
N/A
No
Yes
No
The "Animatable" column indicates if that property can be used with a CSS transition (transition:) or CSS animation (#keyframes).
Crucially, the display: property cannot be animated, which is why we can't use a #keyframes timeline to completely hide an element after the animation is complete.
But curiously, we can animate the visibility: property despite being non-continuous, albeit with caveats.
Also, don't get confused by the similarly-named backface-visibility and content-visibility properties.
backface-visibility is only applicable to 3D transform operations.
content-visibility is an optimization to speed-up page rendering during initial page-load, but requires CSS Containment first, which is out-of-scope for this QA.
The answer found here will answer your first question (most likely display:none as the space is collapsed completely).
To your second question, tools such as this will probably be useful for you. However 40,000 divs sounds like way too many and you will probably have better performance using canvas or SVG (for example, using the KineticJS library as this handles animations - transformation, rotation, scale, etc.) for you.
display:none will hide the whole element and remove that from layout space whereas visibility:hidden hides an element but take up the same space as before.
Opacity can be used if you want to create transparency or fade effect.
Performance will be an issue if display:none or visibility:hidden is used since they trigger paint and layout in most browsers which means your browser will redraw the viewport whenever those two changes so I will recommend opacity but still for that number of divs it will still be not performant as expected you can try webgl using a library called html-gl which render your divs in webgl check https://github.com/PixelsCommander/HTML-GL
Here is a compilation of verified information from the various answers.
Each of these CSS properties is in fact unique. In addition to rendering an element not visible, they have the following additional effect(s):
Collapses the space that the element would normally occupy
Responds to events (e.g., click, keypress)
Participates in the taborder
collapse events taborder
opacity: 0 No Yes Yes
visibility: hidden No No No
visibility: collapse * No No
display: none Yes No No
* Yes inside a table element, otherwise No.
got from link
display:none because the divs are taken out of the flow then, thus their position does not have to be calculated.
That being said, 40000 divs sounds crazy. Did you consider the alternatives like HTML5 canvas or SVG?
Sometime i use visibility and opacity together to achieve effect to avoid click event
e.g.
normal state/element removed from screen:
visibility:hidden;
opacity:0;
transition: all .3s;
hover state/element on screen:
visibility:visible;
opacity:1;
Found this thread whilst investigating a hover: bug in Safari mobile
Confirming that opacity: 0 is a valid approach (it is in my case, thanks all). opacity: 0 fixes it enough to be workable (still requires an annoying js redraw on screen rotate [width change]).
Background info on the bug I fixed with opacity: 0:
The hover is on a li containing a div that is revealed when hovering (or single touch on mobile) a calendar entry. Really random working/not working in Safari mobile - and even weirder the behavior changes on a screen rotate++ [nb no media queries involved so not that].
So annoying as otherwise works in all other browsers I've tried.
Related
two ways
There are two common ways to dynamically change the visibility of an element with javascript...
By modifying its style:
// occupies same space when hidden
elem.style.visibility = 'hidden' // 'visible' to unhide
// does not occupy any space when hidden
elem.style.display = 'none' // 'block' | 'inline' to unhide
By modyfying its classList:
elem.classList.add('hidden') // classList.remove('hidden') to unhide
.hidden {
visibility: hidden; /* occupies same space when hidden */
/* display: none; /* does not occupy any space when hidden */
}
ℹ️ There are other properties that can be used such as opacity (See #Kingfish's comment), but they all must still be updated by either modifying their style or their classList.
but which is better for hiding/unhiding large numbers of elements?
For one or a few elements, modifying style or classList will work the same. But I need to change the visibility of dozens or even hundreds of elements. For volume changes, Which methods is better in terms of the following?
minimizing CPU usage
minimizing redraw latency
minimizing UI thread blocking
I have a very fast computer so it is hard to know the impacts on people with more performance constrained devices.
is there a third way?
All of the elements I want to show/hide in unison have a specific class. It seems most logical that I should be able to modify the CSS rules for that class, i.e. to set its CSS visibility property, rather than add/remove another class for each of the hundreds of members of the class. I've researched this and few people talk about it, which makes me wonder if it is considered a hack, not-compatible across browsers, or bad for some other reason.
Is there a well-founded explanation one way or the other?
Is there a third way?
Yes, there is. There's even a fourth way.
All of the elements I want to show/hide in unison have a specific class. It seems most logical that I should be able to modify the CSS rules for that class, i.e. to set its CSS visibility property, rather than add/remove another class for all of these elements.
Yes, this is possible, using the CSS Object Model. Get the stylesheet defining the rule, get the rule for that class, and change its definition.
It's not a hack, and has good browser support. It's rarely done because few people know about it, and because accessing the right rule is a bit fiddly (which you can work around by creating and inserting the rule object using CSSOM in the first place). I'm not certain how well-optimised it is - but it certainly needs less JS processing than to alter each individual element.
However, there's a much easier solution: use cascading style sheets!
body.hide-x .x {
display: none;
}
/* or, reverse:
.x {
display: none;
}
body.show-x .x {
display: inline;
}
*/
document.body.classList.toggle('hide-x');
This will show/hide all elements with class x inside the document, based on whether the show-x class is applied to the body or not.
You can replace display: none with visibility: hidden or opacity: 0 or any other property change you want to apply en masse depending on your needs.
I have a DIV with the following CSS code attached:
.active,#foo:active {background-color: rgba(0,0,0,0.75)}
In addition, I have set up keydown and keyup javascript routines to convert a selected keypress to add and remove the 'active' class, darkening it accordingly. My problem is when the user clicks on the DIV (darkening it as expected)...but in a setInterval I have running, polling the DIV and several more like it periodically to get state information, I run into the problem of not being able to tell the current DIV state.
Getting the state via the active class is easy enough. I simply have to do this...
document.getElementById("foo").classList.contains("active")
That gives me a boolean on/off I can use, but the following does not work to read a mouse long click.
document.getElementById("foo").classList.contains(":active")
This is because activated pseudoes do not show up in classList. I tried rewriting the mouse-examining check to look like this:
document.getElementById("foo") === document.activeElement
But as the element is a DIV, this never resolves to a true as document.activeElement stays stuck on the BODY element of the page. I also tried looking at the current background-color, but the following doesn't update when :active is in use.
document.getElementById("foo").style.backgroundColor === "rgba(0,0,0,0.75)"
Is there another way to proceed without needing to resort to removing :active from the CSS and installing onclick() and onmouseout() to my code? Using that particular workaround does not scale well depending on how many DIVs I set up in this fashion. I would much rather detect when the DIV is currently using the CSS rule described above.
Please, no jQuery solutions or external libraries. I want to use vanilla JavaScript.
Pseudo elements are not part of the DOM so you cannot trigger events on them.
https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements
In addition .style prototype function does not work as intended, to look for computed style use
var ele = document.querySelector('.example-value')
window.getComputedStyle(ele, null).backgroundColor === "rgba(0,0,0,0.75)"
https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
Polling to get the current state of an element is a bad idea, instead use events.
When polling, you are making CPUs always process for nothing, it can make your page irresponsible, cause useless bottlenecks, but most of all, it consumes a lot of electricity for nothing. Always think about the trees when coding.
But one has to admit that for the :active status, it may be a bit cumbersome to listen to all the events that can trigger it.
So we've got to be a bit smarter, and create our own event from there.
We can create an empty animation triggered only in the :active state.
Then we just have to listen for the animationstart event to act as an replacement for our pseudo-class activation event.
/* older browsers might need vendor prefixes... */
foo.addEventListener('animationstart', function(evt){
// to be sure it's our correct event, we check for the animationName
console.log('active', evt.animationName === 'active');
});
#foo:active{
background-color: #FAFFAA;
-webkit-animation: active 0s linear;
-o-animation: active 0s linear;
-ms-animation: active 0s linear;
animation: active 0s linear;
}
#keyframes active{}
#-webkit-keyframes active{}
#-o-keyframes active{}
#-ms-keyframes active{}
<div id="foo">
click me to activate me
</div>
And if ever you need to know at any time if an element has an pseudo-class, you can use Element.matches(cssRule).
The word "active" has different meanings:
An element with the :active pseudo-class. This means the element is in the process of being clicked, usually. This is most commonly used for creating some visual effect when the user mouse-downs on a button, and remove it when he mouse-ups, for example. This is probably not relevant to your use case.
The element given by document.activeElement. This does not mean the element with the :active pseudo-class; it means the element with focus. It will be the body if there is no specific focus, or it could be some input element, or it could be any other element with a tabindex attribute. This is also the element with the :focus pseudo-class. An element can be focused by clicking on it, or tabbing to it, or calling HTMLElement#focus on it.
Some application-defined concept of "active", such as the currently active tab in a tabbed interface, often represented by the presence of a user-defined class on the element, such as your .active.
In general, people write far too much JavaScript to check things, or intercept events, or set magic variables, or add and remove classes or even local styles, or in the worst jQuery style add and remove elements from the DOM, or God forbid do polling, when in many cases CSS could handle what needs to be done if used properly. A trivial example is writing mouseover handlers when :hover could do the job.
I don't fully understand what you are trying to accomplish, or what the desired behavior is. However, the following code might give you some clues:
const activeElement = document.getElementById("activeElement");
const divElement = document.getElementById("div");
function showActiveElement() {
activeElement.textContent = document.activeElement.tagName;
}
function updateActiveElement() { setInterval(showActiveElement, 500); }
function setFocus() { divElement.focus(); }
updateActiveElement();
/* Show a message if the div is active (being clicked on). */
#activeMessage { display: none; }
#div:active ~ #activeMessage { display: block; }
/* Show a message if the div is focused. */
#focusMessage { display: none; }
#div:focus ~ #focusMessage { display: block; }
/* Style the div when it is focused. */
#div:focus { background-color: rgba(0, 0, 0, 0.75); color: white; }
#div { border: 1px solid gray; }
<p>
Here is the div we are working with.
Click on it, or tab to it, to give it the focus.
</p>
<!-- The div in question. Give it a tabindex to allow focus. -->
<div id="div" tabindex="1">
Hello Bob
</div>
<p id="activeMessage">
The div is active, in the sense that the mouse is being clicked on it, and
therefore its <tt>:active</tt> pseudo-class is set.
</p>
<p id="focusMessage">
The div is active, in the sense that it has the focus,
therefore its <tt>:focus</tt> pseudo-class is set.
<p>
The element with focus at the moment (<tt>document.activeElement<//tt>) is
<span id="activeElement"></span>
</p>
<button onclick="setFocus()">Make the div focused ("active")</button>
I've been dabbling in simple CSS transitions and hover events etc recently. I notice that when you press the TAB key it generally finds links which is fine but...
If I have a hover event, like a piece of text is revealed or something similar, how can I ensure that pressing the TAB key will trigger hover and or focus events?
This is because I have a page full of squares made up of DIVs that look similar to this:
When you hover over this block with your mouse it changes color via a hover event, essentially to visually inform the user that the element is in some way interactive.
Is there a way I could trigger the hover event with the TAB key or even the arrow keys? My reasoning is because if for some reason you did not have a mouse or touch device you could potentially miss out on content.
Amending my question slightly
So the TAB key is treated as a :focus event and works well when you give a link a :hover state but is it possible for the TAB key to acknowledge DIV elements?
With CSS you can use also :focus, try this:
div {
float:left;
margin:2px;
}
a {
display:block;
height:100px;
width:100px;
line-height:100px;
text-align:center;
background:purple;
color:white;
transition:.3s linear;
}
a:hover, a:focus {
background:orange;
}
<div>item1</div>
<div>item2</div>
<div>item3</div>
<div>item4</div>
<div>item5</div>
This is with respect to your last comment:
So the TAB key is treated as a :focus event and works well when you give a link a :hover state but is it possible for the TAB key to acknowledge DIV elements?
I believe you are looking for tabindex="0". Adding that attribute will make your element capable of receiving focus.
So <div tabindex="0">Hello World</div>
The tabindex doesn't have to be 0. It can be negative, 0, or a possitive integer following these rules:
From MDN on tabindex
a negative value means that the element should be focusable, but should not be reachable via sequential keyboard navigation;
0 means that the element should be focusable and reachable via sequential keyboard navigation, but its relative order is defined by
the platform convention;
a positive value means should be focusable and reachable via sequential keyboard navigation; its relative order is defined by the
value of the attribute: the sequential follow the increasing number of
the tabindex. If several elements share the same tabindex, their
relative order follows their relative position in the document.
With a javascript click-event I am adding extra html-text (to be specific: an "X"-icon" within a span) to a button. I am doing this with switching the property on the span-icon-class from display: none to display: block.
The button therefore becomes bigger because of the added icon after the click event instantly.
What CSS/js do I need to add, to make this transition smooth, so that the button grows slowly bigger instead of instantly?
Thanks a lot and sorry for maybe complicated questioning.
Maybe look at https://www.w3schools.com/css/css3_transitions.asp.
If you have a fixed initial button width, it should be something like this :
JS :
$('.mybutton').on('click', function(){ $(this).addClass('clicked') };
CSS :
.mybutton{
width : 90px;
transition : width 0.5s ease;
}
.mybutton.clicked{
width : 120px;
}
Yes, you can add CSS to do such a transition. However, you can't use from display: none -> block for a CSS transition. If I don't remember it incorrectly it's because it transitions over time, and display: none/block is a binary system, meaning it can only be shown or not shown, there is no in between. I believe visibility can be used instead because it supports this in-between state of both existing and not existing so to speak.
See this question: Transitions on the display: property
Also, google "css transition display none block" and you'll get a bunch of helpful links.
Okay, I resolved it with a scale-property. I transition the scale of the x-icon, which also slowly enlarges the button.
Here is the description of my goal:
I've got box with display: none
At some moment I need to display it with opacity animation.
Here my solution:
1. transition: opacity 0.5s ease-in-out;
2. On first step set display: block; opacity: 0
3. On second step set display: block; opacity: 1, do it in setTimeout() to apply first step.
The problem is that first step applied only in some cases - sometimes it works / sometimes doesn't and browser just skips first step. I thought changing setTimeout to requestAnimationFrame should fix the problem but it doesn't - check my example
Why setTimeout / requestAnimationFrame does not force browser to apply first step? How to force browser to apply first step before applying second one?
Solution: http://jsfiddle.net/sxny7zs2/
.box{display:none} should be .box{display: block;}
When you set display:none you remove the object from the DOM almost entirely. By resetting to display:block you bring the object back fully and it begins to interact with other objects. The display feature is not meant for animations but for removing objects from interfering with others.
I suspect this is the villain:
$box.removeClass('is-animate-enter').addClass('is-animate-active');
By removing is-animate-enter class you trigger display:none; before you are able to add your next class. This means the object is unloaded from the view. Meanwhile when you do is-animate-active you instantly set display:block and opacity:1. As far as the browser is concerned you are creating a new element, not modifying an old one here. As previously stated, when toggling the display you are actually loading and unloading an object so no animation is possible.
Maybe .switchClass() could fix this but I'm not sure, to reiterate the display command is for loading and unloading and not for animations.