0

Есть ли колбек для window.scrollTo?

5

Я хочу вызвать метод focus() для элемента ввода после окончания прокрутки окна. Я использую плавное поведение для метода scrollTo(). Проблема в том, что метод focus нарушает плавное поведение. Решение заключается в том, чтобы вызвать функцию focus сразу после завершения прокрутки.

Однако я не могу найти документацию или обсуждений, касающихся того, как определить конец метода scrollTo.

let el = document.getElementById('input')
let elScrollOffset = el.getBoundingClientRect().top
let scrollOffset = window.pageYOffset || document.documentElement.scrollTop
let padding = 12
window.scrollTo({
  top: elScrollOffset + scrollOffset - padding,
  behavior: 'smooth'
})
// дождаться завершения прокрутки и затем
el.focus()

Есть ли у кого-нибудь идеи?

4 ответ(ов)

0

Ваше решение с использованием функции scrollTo выглядит весьма элегантно и позволяет избежать перезаписи глобального события window.onscroll. Основная идея заключается в том, чтобы отслеживать событие прокрутки и вызывать коллбэк только тогда, когда достигнуто нужное смещение.

Вот краткий анализ вашего кода:

  1. Функция: Вы создали обобщенную функцию, которая принимает два параметра: offset — смещение, до которого нужно прокрутить страницу, и callback — коллбэк, вызываемый при достижении заданного положения прокрутки.

  2. Фиксированное смещение: Вы используете toFixed(), чтобы привести значение offset к стандартному виду. Это хорошо, так как так вы избегаете ошибок связных с плавающей запятой.

  3. Обработчик прокрутки: Вы создаете функцию onScroll, которая проверяет, достигнуто ли необходимое смещение. В случае совпадения, вы удаляете обработчик события и вызываете переданный коллбэк.

  4. Добавление и выполнение: Вы добавляете обработчик события scroll и сразу вызываете onScroll, чтобы проверить, на каком этапе находитесь в момент выполнения.

  5. Прокрутка: В конце вы вызываете window.scrollTo с заданным смещением и поведением "плавно".

Таким образом, ваш код безопасен и не нарушает глобальное состояние. Это позволяет избежать конфликтов с другими возможными обработчиками события прокрутки на странице. Отличная работа!

0

Ваше решение действительно работает, но, возможно, оно немного "хакерское". Ваша логика прокрутки и установка фокуса при достижении целевой позиции достаточно изящны, однако есть несколько моментов, которые можно улучшить для более чистого и устойчивого кода.

Во-первых, вы используете событие onscroll, чтобы отслеживать, когда страница достигла целевой позиции. Хотя это сработает, лучше использовать более предсказуемый подход, например, IntersectionObserver, который позволяет выполнять действия, когда элемент становится видимым в пределах области просмотра. Это избавит вас от необходимости вручную управлять обработчиком событий прокрутки.

Вот возможный пример улучшенного кода:

let el = document.getElementById('input');
let padding = 12;

let targetPosition = el.getBoundingClientRect().top + window.pageYOffset - padding;

window.scrollTo({
  top: targetPosition,
  behavior: 'smooth'
});

// Используем IntersectionObserver для фокуса
let observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      el.focus();
      observer.disconnect(); // отключаем наблюдение
    }
  });
});

// Начинаем наблюдение за элементом
observer.observe(el);

Этот код будет более эффективным и читаемым. Он использует IntersectionObserver, чтобы точно определить, когда элемент попадает в область видимости, и устанавливает фокус на него без необходимости в ручной проверке прокрутки. Надеюсь, это поможет сделать ваш код более "чистым" и поддерживаемым!

0

Другие ответы мне не совсем подошли, поэтому, основываясь на ответе @Fabian von Ellerts, я написал собственное решение.

У меня были следующие проблемы:

  • Элемент, который я прокручивал (и все его родительские элементы в иерархии), всегда имел offsetTop равный 0, поэтому это не сработало.
  • Мне нужно было прокрутить вложенный элемент.

Использование getBoundingClientRect и элемента-контейнера в качестве ориентира работает:

const smoothScrollTo = (
    scrollContainer,
    scrolledContent,
    offset,
    callback
) => {
    const fixedOffset = (
        scrollContainer.getBoundingClientRect().top + offset
    ).toFixed();
    
    const onScroll = () => {
        if (
            scrolledContent.getBoundingClientRect().top.toFixed() ===
            fixedOffset
        ) {
            scrollContainer.removeEventListener('scroll', onScroll);
            callback();
        }
    };

    scrollContainer.addEventListener('scroll', onScroll);
    onScroll();
    scrollContainer.scrollTo({
        top: offset,
        behavior: 'smooth',
    });
}

Надеюсь, это поможет тем, кто сталкивается с аналогичными проблемами!

0

Вы можете сделать асинхронную версию функции scrollTo, используя промисы и обработчики событий. В вашем коде вы заменили устаревший pageYOffset на window.scrollY, что является хорошим улучшением. Также вы добавили точки с запятой, что делает код более читаемым.

Вот ваш код с некоторыми небольшими комментариями для пояснения:

/**
 * Асинхронная функция прокрутки
 * @param offset - смещение, к которому нужно прокрутиться
 */
async function scrollTo(offset) {
    return new Promise((resolve, reject) => {
        const fixedOffset = offset.toFixed(); // Округляем значение смещения
        const onScroll = function () {
            // Проверяем, достигли ли нужного положения на странице
            if (window.scrollY.toFixed() === fixedOffset) {
                window.removeEventListener('scroll', onScroll); // Убираем обработчик события прокрутки
                resolve(); // Разрешаем промис
            }
        };

        window.addEventListener('scroll', onScroll); // Добавляем обработчик события прокрутки
        onScroll(); // Вызываем функцию сразу, чтобы проверить текущее положение
        window.scrollTo({
            top: offset, // Прокручиваем страницу к заданному смещению
            behavior: 'smooth' // Используем плавную прокрутку
        });
    });
}

Не забудьте, что использование асинхронных функций и промисов позволяет управлять потоками выполнения. Таким образом, вы можете вызывать scrollTo() и использовать await для ожидания завершения прокрутки перед выполнением других действий.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь