ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Category:

JavaScript: кнопка вверх, кнопка вниз

Решил задачу «Кнопка вверх/вниз» к подразделу 3.6 «Прокрутка» второй части учебника по JavaScript.

Задача оказалась несложная и я быстро с ней справился. После этого я решил немного ее расширить и в процессе дописывания решения узнал о нескольких интересных особенностях.

Дана HTML-страница (в песочнице) с содержимым, которое не помещается в области просмотра браузера по высоте, в результате чего в браузере присутствует полоса прокрутки по вертикали. Еще в теле этой HTML-страницы есть стрелка «вверх», представленная HTML-элементом div с идентификатором arrowTop (собственно, сама стрелка отображается символом «▲» или \25B2 в Юникоде). Этот HTML-элемент имеет фиксированное позиционирование (это определено в стилях на языке CSS) относительно верхнего левого угла области просмотра браузера (то есть он постоянно остается на одном и том же месте, даже если HTML-страница прокручивается).

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

Содержимое, не помещающееся в области просмотра браузера по вертикали, тоже создается с помощью отдельного скрипта на языке JavaScript, этот скрипт уже есть на заданной HTML-странице:
<script>
    for (let i = 0; i < 2000; i++) document.writeln(i)
</script>

Этот скрипт помещен внутри HTML-элемента div с идентификатором matrix. Таким образом, этот HTML-элемент заполняется текстом, состоящим из чисел от 0 до 1999, разделенных одним пробельным символом (в качестве которого в данном случае выступает символ новой строки).

Кроме этого, в стилях на языке CSS этому HTML-элементу задана ширина в 400 пикселей, чтобы еще больше увеличить высоту этого HTML-элемента, а, следовательно, и высоту всей HTML-страницы.

* * *

С самого начала стрелка «вверх» отображается на заданной HTML-странице. Когда я в первый раз загружаю заданную HTML-страницу в своём браузере, ползунок находится вверху, то есть я вижу начало (верх) HTML-страницы. По идее, стрелка сразу же не должна (по условиям задачи) отображаться в этом случае.

Я сразу подумал, что можно сделать HTML-элемент, представляющий стрелку «вверх», скрытым с помощью свойства hidden с самого начала, добавив это свойство в код заданной HTML-страницы. Так сделали авторы задачи в своём решении.

Однако, затем я засомневался: я ведь могу загрузить HTML-страницу, передвинуть ползунок полосы прокрутки по вертикали вниз до места, в котором по условиям задачи стрелка «вверх» должна быть отражена, и затем обновить HTML-страницу. При этом HTML-страница уже загрузится с ползунком не в начале HTML-страницы, а в том месте, в котором я нажал на кнопку обновления HTML-страницы. То есть при загрузке страницы ползунок полосы прокрутки теоретически может находится в любом месте HTML-страницы, а не только в самом ее начале (вверху). И при этом, как я думал, после загрузки страницы стрелка «вверх» либо должна быть видна, либо нет, возможны оба случая.

И, всё же, решение с самого начала скрыть стрелку «вверх» с помощью свойства hidden в коде заданной HTML-страницы таки будет работать. Почему? Дело в том, что если мы обновляем HTML-страницу при ползунке, передвинутом вниз, то браузер сначала загружает HTML-страницу с ползунком в самом верхнем положении, а затем мгновенно передвигает его в то место, в котором он был перед запуском обновления HTML-страницы, то есть генерируется событие scroll. А при обработке этого события мы и так будем анализировать и устанавливать видимость стрелки «вверх» (это будет описано ниже). Таким образом, скрытие стрелки «вверх» с самого начала нужно только для ситуации, когда при загрузке HTML-страницы ползунок полосы прокрутки находится в самом верху, никуда не передвигается и событие scroll не генерируется. Итак, вносим изменение в код заданной HTML-страницы (красным цветом я указал изменение):
<div id="arrowTop" hidden></div>

Далее, когда я писал расширенное решение для этой задачи, то заметил, что код анализа и установления видимости стрелки «вверх» у меня повторяется в нескольких местах, поэтому решил выделить этот код в отдельную функцию, так как дублирование кода является плохим стилем программирования. Пишем код этой функции:
function showArrow() {
    // высота окна (клиентской части области просмотра браузера)
    let viewportHeight = document.documentElement.clientHeight;

    if (pageYOffset < viewportHeight) arrowTop.hidden = true;
    else arrowTop.hidden = false;
}

Если сдвиг HTML-страницы по вертикали pageYOffset меньше высоты окна viewportHeight (клиентской части области просмотра браузера), то скроем стрелку «вверх», в противном случае покажем эту стрелку.

Осталось лишь повесить функцию showArrow в качестве функции-обработчика события scroll, а на событие click нажатия основной кнопки мыши над стрелкой «вверх» повесить функцию-обработчик, передвигающую нашу HTML-страницу вверх, в начало. Дополним код:
function showArrow() {
    // высота окна (клиентской части области просмотра браузера)
    let viewportHeight = document.documentElement.clientHeight;

    if (pageYOffset < viewportHeight) arrowTop.hidden = true;
    else arrowTop.hidden = false;
}

window.addEventListener("scroll", showArrow);

arrowTop.addEventListener("click", function() {
    scrollTo(pageXOffset, 0);
});

Метод scrollTo передвигает HTML-страницу в указанные координаты. В данном случае мы не будем двигать HTML-страницу по горизонтали, поэтому оставим по горизонтали тот сдвиг pageXOffset, который есть на момент, когда пользователь нажмет мышкой на стрелку «вверх». А вот по вертикали (второй параметр) мы установим координату равной нулю, из-за чего HTML-страница передвинется вверх, к своему началу.

В принципе, задача решена. И это решение практически повторяет решение от авторов этой задачи. Однако, мне не понравилось, что при изменении размеров окна (области просмотра браузера) видимость стрелки «вверх» не пересматривается. А ведь при изменении размеров окна высота окна меняется и условия задачи могут быть нарушены!

Поэтому я решил повесить имеющуюся функцию showArrow в качестве функции-обработчика еще и на событие resize для области просмотра браузера. Дополним код (я пометил дополнение красным цветом):
function showArrow() {
    // высота окна (клиентской части области просмотра браузера)
    let viewportHeight = document.documentElement.clientHeight;

    if (pageYOffset < viewportHeight) arrowTop.hidden = true;
    else arrowTop.hidden = false;
}

window.addEventListener("scroll", showArrow);
window.addEventListener("resize", showArrow);

arrowTop.addEventListener("click", function() {
    scrollTo(pageXOffset, 0);
});

Вот такое решение мне нравится. Оно работает у меня в браузере.

* * *

После вышеописанного я захотел сделать еще и кнопку «вниз» действующую аналогично кнопке «вверх», но с зеркально отраженными условиями: кнопка «вниз» должна появляться тогда, когда низ области просмотра браузера находится не ближе высоты области просмотра браузера к низу (концу) HTML-страницы и эта кнопка должна при нажатии мышкой на нее перемещать HTML-страницу вниз, к ее концу. В коде заданной HTML-страницы я добавил представление кнопки «вниз» таким образом (красным цветом я отметил добавленное):
<div id="arrowTop" hidden></div>
<div id="arrowBottom"></div>

А в стили, описанные на языке CSS, я добавил (по аналогии со стилями кнопки «вверх») такие стили (я выделил красным цветом ключевые изменения):
#arrowBottom {
  height: 9px;
  width: 14px;
  color: green;
  position: fixed;
  bottom: 16px;
  left: 10px;
  cursor: pointer;
}

#arrowBottom::before {
  content: "\25BC";
}

Вместо top: 10px; для кнопки «вверх» я вписал указание bottom: 16px; для кнопки «вниз», чтобы она появлялась в левом нижнем углу области просмотра браузера. А вместо символа «▲» или \25B2 в Юникоде для кнопки «вверх» я вписал символ «▼» или \25BC в Юникоде для кнопки «вниз».

Код, анализирующий и регулирующий видимость кнопки «вниз», я решил поместить в ту же функцию showArrow, переименовав ее в showArrows. Меняем код (я отметил изменения красным цветом):
function showArrows() {
    // высота окна (клиентской части области просмотра браузера)
    let viewportHeight = document.documentElement.clientHeight;

    if (pageYOffset < viewportHeight) arrowTop.hidden = true;
    else arrowTop.hidden = false;

    // высота HTML-страницы
    let htmlHeight = document.documentElement.offsetHeight;
    // ограничитель по высоте для видимости нижней стрелки
    let heightLimiter = htmlHeight - 2 * viewportHeight;
    
    if (pageYOffset > heightLimiter) arrowBottom.hidden = true;
    else arrowBottom.hidden = false;
}

window.addEventListener("scroll", showArrows);
window.addEventListener("resize", showArrows);

arrowTop.addEventListener("click", function() {
    scrollTo(pageXOffset, 0);
});

К моему удивлению этот код не заработал для стрелки «вниз». Дело в том, что в стилях, описанных на языке CSS для заданной HTML-страницы, есть следующее указание:
body, html {
  height: 100%;
  width: 100%;
  padding: 0;
  margin: 0;
}

Согласно этому указанию высота HTML-элемента html будет всегда равна (будет автоматически растягиваться или сужаться) высоте окна (области просмотра) браузера. Поэтому свойство document.documentElement.offsetHeight не будет показывать высоту заданной HTML-страницы так, если бы эта высота регулировалась содержимым HTML-страницы, а будет всегда равна свойству document.documentElement.clientHeight. Таким образом, в данном случае всё это приводит к ошибке в работе скрипта. Чтобы получить высоту HTML-страницы в случае, когда ее содержимое выходит за пределы области просмотра браузера (наш случай), можно применять свойство document.documentElement.scrollHeight.

Кроме этого, добавим еще код функции-обработчика, который запустится, когда пользователь нажмет основной кнопкой мыши на стрелку «вниз» на заданной HTML-странице. Меняем код (я пометил изменения красным цветом):
function showArrows() {
    // высота окна (клиентской части области просмотра браузера)
    let viewportHeight = document.documentElement.clientHeight;

    if (pageYOffset < viewportHeight) arrowTop.hidden = true;
    else arrowTop.hidden = false;

    // высота HTML-страницы
    let htmlHeight = document.documentElement.scrollHeight;
    // ограничитель по высоте для видимости нижней стрелки
    let heightLimiter = htmlHeight - 2 * viewportHeight;
    
    if (pageYOffset > heightLimiter) arrowBottom.hidden = true;
    else arrowBottom.hidden = false;
}

window.addEventListener("scroll", showArrows);
window.addEventListener("resize", showArrows);

arrowTop.addEventListener("click", function() {
    scrollTo(pageXOffset, 0);
});

arrowBottom.addEventListener("click", function() {
    // высота HTML-страницы
    let htmlHeight = document.documentElement.scrollHeight;
    
    scrollTo(pageXOffset, htmlHeight);
});

Это окончательный вариант скрипта.
Tags: Образование, Программирование
Subscribe

  • GIMP, формат PNG, утилита pngcrush

    В одном из предыдущих постов я разбирал постановку задачи в учебнике по JavaScript. Авторы задачи создали тестовую HTML-страницу, на которой…

  • Роскомнадзор и DeviantArt.com, 2021 год

    Система блокировки сайтов в России в некоторых случаях уже работает настолько четко (не прошло и десяти лет с ее создания), что люди не успевают за…

  • Название тестового фреймворка Mocha

    Изучая язык JavaScript, узнал о существовании библиотеки «Mocha», предназначенной для создания автоматических тестов для скриптов на языке…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments