digitoimistodude / air-light

💨 WordPress starter theme - designed to be minimal, ultra-lightweight (< 20 kB) and easy for all kinds of WordPress projects. 7+years/1000+hours of development and still updating daily! We prefer the original WordPress way of doing things so no strange templating languages or frameworks here.
https://airwptheme.com
MIT License
929 stars 139 forks source link

Move to top is janky on Firefox #190

Closed raikasdev closed 10 months ago

raikasdev commented 11 months ago

The "move to top" button seems to only move users upwards a bit on Firefox (about as much as pressing up arrow key). On Chromium this seems to work fine.

https://github.com/digitoimistodude/air-light/assets/29684625/bdf033b8-71b4-41db-a0e5-0fbcfdb6857f

This problem seems to happen at least on my local development environment and Gofore's website. For some reason, this doesn't happen on Dude's website.

Solution

Personally I fixed this by setting moveTo's anchor to #page instead of the first focusable element, and made user blur the currently focused item (to reset focus order, basically going back to top on that matter).

/* eslint-disable max-len */
/**
 * @Author: Roni Laukkarinen
 * @Date:   2022-05-07 12:20:13
 * @Last Modified by:   Roni Äikäs
 * @Last Modified time: 2023-06-03 23:13:50
 */
import MoveTo from 'moveto';

const backToTop = () => {
  // Back to top button
  const moveToTop = new MoveTo({ duration: 300, easing: 'easeOutQuart' });
  const topButton = document.getElementById('top');
  const target = document.getElementById('page');

  function trackScroll() {
    const scrolled = window.pageYOffset;
    const scrollAmount = document.documentElement.clientHeight;

    if (scrolled > scrollAmount) {
      topButton.classList.add('is-visible');
    }

    if (scrolled < scrollAmount) {
      topButton.classList.remove('is-visible');
    }
  }

  if (topButton) {
    topButton.addEventListener('click', (event) => {
      // Don't add hash in the end of the url
      event.preventDefault();

      // Move smoothly to the first focusable element on the page
      moveToTop.move(target);

      if (document.activeElement) document.activeElement.blur();
    });

    // Focus too, if on keyboard
    topButton.addEventListener('keydown', () => {
      window.scrollTo({ top: 0 });
      if (document.activeElement) document.activeElement.blur();
    });
  }

  window.addEventListener('scroll', trackScroll);
};

export default backToTop;
ronilaukkarinen commented 11 months ago

Have you tested it on latest air-light version without modifications? Have you figured out what causes it, is it moveTo or something else? :thinking: On Dude website the new Air-light back to top wasn't yet used. The focus should always go to the first focusable element for accessibility reasons. #page is not that.

Have to test this further when I have time. If you have a bulletproof solution to this, feel free to send a PR :)

raikasdev commented 11 months ago

Doesn't happen on latest air-light. Doesn't work for me on commit 7902d56, but seems to work on 1d770f8 (commit before the latest changes to _accessibility :shrug:). I'll try to find the root cause to understand what breaks it.

raikasdev commented 11 months ago

Problem is caused when moveto tries to get window's (container) pageYOffset, meaning amount of pixels from the top of the screen (how much the user has scrolled), and on Firefox it seems the value being returned is 10 for some reason, and on Chromium it's 0.

When it's 10, this clause returns the move function, because lastYOffset (now being 10 returned by last loop) is smaller currentYOffset. If the value is 0, the "if (lastYOFfset)" clause will not run, because 0 is a nullish value in JavaScript :+1:

Because of this I believe some styles made by developers after starting to style the website may cause the scrolling to break.

raikasdev commented 11 months ago

I have identified why this happens is not 100% moveTo's fault. When the user get's focused to the skip to content link, Firefox places the user 5 pixels scrolled down (margin?).

This can be fixed by focusing the user only after the animation has finished (callback). I'll create a PR