I'm doing a mobile (responsive to be exact) version of an already existing desktop site that already has a lot of hefty hover effects on it. It often happens, than when I scroll it on mobile by touching the screen it fires some performance heavy hover actions like fading in a big box-shadow and/or animating border. I don't need that on mobile browser for obvious reasons and I've heard that just disabling hovers on scroll for mobile devices can result in a big performance gains.
Is there a way to easily, temporarily disable all hover effects for the time of scrolling on mobile devices only? Is it safe (from UX perspective) and is it worth implementing (from performance perspective)?
You could set for all of the hover effects to only occur when the body has a certain class.
Which would allow you to simply add / remove that class when you want to toggle the effects.
HTML
<body class="alloweffects">
<a>Some text</a>
</body>
CSS
.alloweffects a:hover {
color:red;
}
Then,to only prevent effects during scrolling you can simply listen to the scroll event and set the class accordingly.
Regarding performance:
I believe that the performance gain will be minimal but it'll probably vary on different browsers & devices.
But basically it should mean less event listeners, meaning more memory to deal with other things, scrolling is among them.
if we're talking about large amounts of elements I believe that the performance gain will be more significant - especially on weak devices.
You want to use pointer-events: none;
CSS:
.disable-hover {
pointer-events: none;
}
Javascript:
var body = document.body,
timer;
window.addEventListener('scroll', function() {
clearTimeout(timer);
if(!body.classList.contains('disable-hover')) {
body.classList.add('disable-hover')
}
timer = setTimeout(function(){
body.classList.remove('disable-hover')
},500);
}, false);
Reference article: http://www.thecssninja.com/css/pointer-events-60fps
This deserves a little bit of testing but the issues are:
What breakpoint do you want to use for mobile (480px is usually the standard)
How to select all currently hoverable items
What effects do you need to override
Anyway a good starting point can be:
#media screen and (max-width: 480px) {
:hover > * {
// override current effects to none
}
In your responsive css, add a x:hover class for the elements, but leave it blank.
If that does not work, negate the effect.
Related
The background:
I am making a 2D HTML/CSS animation using JQuery. This will place roughly 25 same-sized images on top of each other, moving some as desired. UI elements get a pixel position and are much smaller than the full-sized images. I have recently moved from forcing hardware acceleration form backface-visibility: hidden to will-change css properties. The entire animation is scaled by the div it is contained in and the css transform property.
The problem:
In Google Chrome specifically, the compositing starts malfunctioning in certain scenes, in a reproduceable but seemingly random manner. Images are partly cut off, images are placed behind images they are supposed to be on top of, the like. Scrolling up and down replaces some malfunctions with others, as does changing the display properties of some items (happens due to UI interaction).
The question:
What is causing this behaviour, why are all my other test browsers (firefox, opera, internet explorer, edge) not affected, and how can I avoid it?
My current status:
From what I have tried so far and my gut feeling this is an issue caused by overuse of hardware acceleration or the transform: scale css property, or a combination of both. I'm currently working on other scaling methods as the easiest "fix" to the problem, but I still do not know what is causing all of this. Sadly all of my UI elements aren't a neat % of the parent.
What I tried:
Firstly I tried setting the z-index for all elements manually. This accomplished nothing, sadly.
As sometimes scrolling up/down or interacting with the UI seemed to fix problems, I tried adding a method that would force the browser to re-do the layout at certain points in code. This argueably only made things worse, as it added a lot of white flickering, and failed to fix the issue.
I tried switching animation libraries from jquery to velocity, which did not help.
I removed the "hack" that forced things into hardware acceleration. This fixed the compositing problem, but reintroduced choppy animations in chrome (only).
I then tried to assign hardware acceleration on a need-to-get basis by adding and removing a css class containing the "hack". The behaviour was the same as in 4 .
I tried using will-change instead of the backface visibility hack, but this just set the behaviour back to where it was before I started my attempts at fixing this.
I tried reducing the amount of layers while keeping hardware acceleration. I used jquerys ".hide()" on elements only animating once replaced the final picture with a singular image. This did not fix the issue of composite "mashup".
Finally, I disabled the css transform scale property, which fixed the unexpected behaviour, but is imperative so I can scale my animation for screen size.
Some code:
Here's some css code of the animation container
.animationContainer {
position: relative;
width:2560px;
height:1440px;
/*transform: scale(0.55);*/
transform-origin: top left;
overflow: hidden;
display: inline-block;
}
And some javascript detailing the animation I use for the majority of objects, making them "fall" into the scene
function fadein(el, direct)
{
var falldistance = 150;
$(el).show();
$(el).css("opacity", 0.0);
switch(direct) {
case 0:
$(el).css("top", "-="+falldistance);
$(el).animate(
{
top: "+="+falldistance,
opacity: 1.0
}, 1000);
break;
[...]
I do not know how useful more code would be, if you require any more please ask. Web design isn't a field I have much experience in, and I am slowly running out of ideas. Any help or suggestions would be appreciated.
Example Visual:
This is an example of one of the bugs:
This is what it looks like after scrolling down and back up in the browser:
I am not sure if this is the proper place to ask this question; I considered Software Recommendations however I figured I'd give it a shot here first.
I am creating a PWA and with every iteration I'd really like to focus on making it look/feel like less janky and as smooth as possible. Well, like a native app.
So here is the problem—I am using transitions (animations) provided by Semantic-UI-React, and while it works beautifully when an element which is occupying its on space, I was wondering if there a practice or pattern which handles the behavior of adjacent DOM elements to one that is being animated?
I supplied a GIF below to illustrate what I mean. I suppose I could just move the animated elements out of the container of the form to prevent jank, but I wondered if there was a more elegant pattern or approach to this...
Merci!
Unfortunately, there is no particularly elegant approach to this.
Regarding the question in the title of your post, a common approach is to animate the height of the new elements. If you don't know the height of the elements, unfortunately animating from 0px to auto does not yet work but you can fudge it by animating max-height from 0px to a value slightly larger than the maximum size you expect the element to be.
But don't do that.
It will cause the browser to layout the page on every animation frame and will almost certainly jank on lower-end devices.
Instead, you're better off to animate transform.
The most common approach is to:
Grab the original position of the elements (using getBoundingClientRect() etc.) that are going to be affected just before you add the new elements to the DOM (perhaps using getSnapshotBeforeUpdate unless you're using hooks, in which case you can use useRef to similar effect).
After you've added the new elements (which you're presumably also animating in by using transform with a suitable scale() function), calculate the delta from where the offset elements are now, compared to where they used to be.
Setup a transform animation from the negative of the delta, to zero (i.e. the FLIP approach). E.g. if the element has been shifted 300px down the page, animate transform from translateY(-300px) to none.
Of course you need to do that for all the elements in the flow that are affected so it might be easiest to put them all in one container and just animate that.
Regarding the third point you have a few choices of technology, none of which are great:
CSS transitions. These are simplest but you'll have to trigger a style flush to get the negative delta starting point to stick. e.g.
elem.style.transform = `translateY(-${offset}px)`;
elem.style.transition = `transform .3s`;
getComputedStyle(elem).transform; // Flush style
elem.style.transform = `none`;
CSS animations. Producing #keyframes rules dynamically using the CSSOM is a pain but at least you don't need to trigger a style flush (and you can set an animation with an implicit to-keyframe for convenience).
#keyframes random-id { from: { translateY(-300px); } }
Web animations. This is probably the most well-suited.
elem.animate({ transform: [ `translateY(-${offset}px)`, 'none' ] }, 300);
// In future when browsers ship support for implicit to/from keyframes:
elem.animate({ transform: `translateY(-${offset}px)`, offset: 0 }, 300);
Unfortunately Safari only has Web Animations support in Tech Preview so Safari users will get the un-animated version until the next Safari release. Edge users will also get the un-animated version until the Chromium based Edge ships. There is a polyfill too but it's not actively maintained at this point in time.
If you're using React, Animating the Unanimatable is a helpful article on all this.
The other tricky part is making sure the scroll doesn't jump for which you might need to look into scroll anchoring.
There's site I'm working at and design for tablet/mobile devices require me to move elements (from left sidebar to right, change order, etc.). These elements include simple text and images (pretty small, logos).
My question is, what would be the best way to go about this? So far I have only two ways.
1) Duplicate HTML content and then show/hide with CSS media queries. My concern here is that it's not SEO friendly, and content / images still gets rendered, even if hidden. Could that leave me with a performance problem?
2) Move elements using JS. My concern here is that people with JS disabled will still see the content in old places, and maybe this JS solution could impact performance even more?
Would really appreciate some input on best practices in a situation like this.
Here's what I'm trying to achieve:
The questions are contradicting a bit :)
First of all, it's 2016 in the WEB, if you are speaking about having responsive layout, support of mobiles, tablets, desktops - they will have JavaScript support, so you shouldn't worry. - it is answer on your question number #2. The percentage of people not having JS is extremely low, it's below <1%.
CSS media queries are enough to make good responsiveness. Sometimes you need to add helper methods with JS to manipulate DOM and to make it even more advanced.
You may check how they do responsiveness with classes in Twitter Bootstrap.
Sometimes content will be duplicated in HTML, but as soon as it's not visible simultaneously on the screen because of visibility rules from CSS media queries - it will not do any harm on SEO.
There's the way to over-complicate things a bit, RESS: Responsive Web Design + Server-Side Components, and to serve different HTML layouts depending on the detected User Agent.
You mentioned that you were reluctant to use display: hidden because something about them being rendered. If I'm understanding correctly, then you can use display: none on the right side for example. Then in your media queries, you can set content on the left to display: none and content on the right to display: initial. That should work just fine, if I understood you correctly. Then no space will be allocated for the hidden elements.
For example:
.leftDiv {
display: initial;
}
.rightDiv {
display: none;
}
#media screen only and (max-width: 1000px) {
.leftDiv {
display: none;
}
.rightDiv {
display: initial;
}
I am trying to get a div to go from 100% opacity to 0% opacity on scroll.
I made this Fiddle and it works great in a web browser, just as I'd hope. It works in mobile browsers too, but with one horrible downside.
var divs = $('.cover_image');
$(window).on('scroll', function() {
var st = $(this).scrollTop();
divs.css({
'opacity': (1 - st / 40)
});
});
(What is happening in the fiddle is the top div is going to opacity:0 as you scroll, revealing another div below it with the same background-image, but blurred. Creating the impression the same image is blurring the more you scroll)
In a web browser as you scroll the div drops in opacity progressively with a fade like affect which is great.
However in a mobile browser the change of opacity doesn't take effect until you release your finger from the the screen. So there is no progressive change of opacity. It only makes the changes visually as you release your finger from the screen, not as you scroll.
Is there a solution for this? I have tried adding in scrolling touch to my css, but it doesn't make a difference.
-webkit-overflow-scrolling: touch
Scrolling distance on mobile works very different from desktop. Even if you detect each step in the touch event, this is only half the truth. When the user releases, the site will continue to scroll for a bit while deaccelerating. This is called momentum scroll and will in most cases not be picked up by the touch event.
There are to my knowledge no perfect solution to this, since different devices handle scroll and touch very differently. There are however a few options you could look into.
Scrolling libraries
There are libraries to help you solve this problem. For instance one called scrollability that emulates scrolling to work more consistently.
Scrollability adds a good imitation of iOS native scrolling to your
mobile web apps.
Scrollability is a single script, it's small, and it has no external
dependencies. Drop it into your page, add a few CSS classes to
scrollable elements, and scroll away.
Ignore the scroll completely
Don't look at the touch or scroll events. Instead use setInterval or requestAnimationFrame with desired frequency that reports the pages current position (document.documentElement.scrollTop) at all time. Base your animation on this value instead of scroll or touch events. You might want to limit this to touch devices since it's not needed for desktop.
Write your own scroll functionality
Disable scrolling and make your own, without for instance momentum scroll, that is suited for your needs. Note that the scroll event is usually disabled on desktop if you disable scroll, but mousewheel works. I have been down this path and I would not recommend it. Instead you should probably go with the library approach at the top.
When setting up a rollover effect in HTML, are there any benefits (or pitfalls) to doing it in CSS vs. JavaScript? Are there any performance or code maintainability issues I should be aware of with either approach?
CSS is fine for rollovers. They're implemented basically using the :hover pseudo-selector. Here's a really simple implementation:
a{
background-image: url(non-hovered-state.png);
}
a:hover{
background-image: url(hovered-state.png);
}
There are a few things you need to be aware of though:
IE6 only supports :hover on <a> tags
Images specified in CSS but not used on the page won't be loaded immediately (meaning the rollover state can take a second to appear first time)
The <a>-tags-only restriction is usually no problem, as you tend to want rollovers clickable. The latter however is a bit more of an issue. There is a technique called CSS Sprites that can prevent this problem, you can find an example of the technique in use to make no-preload rollovers.
It's pretty simple, the core principle is that you create an image larger than the element, set the image as a background image, and position it using background-position so only the bit you want is visible. This means that to show the hovered state, you just need to reposition the background - no extra files need to be loaded at all. Here's a quick-and-dirty example (this example assumes you have an element 20px high, and a background image containing both the hovered and non-hovered states - one on top of the other (so the image is 40px high)):
a{
background-image: url(rollover-sprites.png);
background-position: 0 0; /* Added for clarity */
height: 20px;
}
a:hover{
background-position: 0 -20px; /* move the image up 20px to show the hovered state below */
}
Note that using this 'sprites' technique means that you will be unable to use alpha-transparent PNGs with IE6 (as the only way IE6 has to render alpha-transparent PNGs properly uses a special image filter which don't support background-position)
It will still work in CSS if the browser happens to have Javascript disabled.
Because it's an aspect of presentation, I'd say it's more standards based to do it with CSS. It used to be done in Javascript, simply because we couldn't do it with CSS (old browsers suck, and I don't think :hover was even added until CSS 2).
Implementing a rollover with CSS uses the :hover pseudo-class to define the style of the target element when it is hovered over. This works great in many browsers but not in IE6 where it only works well with the anchor tag (i.e. a:hover). I used CSS hover to implement a tabbed navigation bar but had to use IE behaviors to get it working in IE6.
Yep, the best way to do this is css sprites. An annoying problem occurs in IE6, when browser make a request every time an element is hovered. To fix this, take a look here.
I'd stay on the CSS side of the house, but I've done very little Javascript.
CSS seems to be easier to standardize across browsers than Javascript, though that may be changing with the advent of Chrome's V8 and Firefox's upcoming new rendering tool.
Isn't there a mnemonic for remembering the sequence of declarations in CSS?