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

Эта статья представляет собой краткое руководство по созданию раскрывающихся меню, таких как 👆.

Мы создадим меню со следующими взаимодействиями: -

  1. Если щелкнуть значок, появится меню.
  2. Щелчок по чему-либо внутри меню не закрывает меню.
  3. Щелчок в любом месте вне меню закроет меню.

Установка

Первое, что нам нужно, это компонент, в котором находится это меню.

У нас есть компонент Card с кнопкой, а также блок div с классом menu, который будет нашим скрытым раскрывающимся меню, которое сейчас на самом деле не скрыто, не так ли?

Давай исправим это. Мы можем установить переменную состояния в конструкторе для хранения текущего видимого состояния меню.

Мы устанавливаем this.state.showMenu в значение false по умолчанию, так как мы хотим, чтобы меню было по умолчанию скрыто. Мы добавили небольшую проверку в строке 20, чтобы увидеть, каково значение this.state.showMenu, и, если оно истинно, мы отображаем меню.

Но… Как нам переключить this.state.showMenu? 🤔

Отображение меню

Давайте добавим обработчик кликов к кнопке Show menu, чтобы отобразить меню.

Давайте добавим в наш компонент функцию showMenu. Каждый раз, когда вызывается этот метод, он устанавливает this.state.showMenu в true. После того, как значение установлено, компонент будет повторно отрисован, и, поскольку оно имеет значение true, теперь будет отображаться меню

Но полной функции все еще не хватает.

Если вы заметили в конце ☝️, я нажимаю за пределами меню, но ничего не происходит. Нам нужно найти способ закрыть меню, когда мы щелкаем за пределами меню.

Скрытие меню

Мы знаем, что у всего на странице есть общий родитель - document. Один из способов, которым мы можем справиться с взаимодействием щелчка вне меню, чтобы закрыть его, - это добавить прослушиватель событий щелчка к этому общему родительскому элементу, чтобы мы могли щелкнуть в любом месте, чтобы закрыть меню.

Когда мы вызываем showMenu, давайте также добавим прослушиватель событий в document. Каждый раз, когда происходит событие щелчка для document, мы должны установить для this.state.showMenu значение false.

А поскольку это событие клика, которое мы хотим отслеживать только один раз, давайте также удалим прослушиватель событий из document, как только мы закончим.

Мы добавили новый метод класса closeMenu(), который устанавливает для this.state.showMenu значение false. И внутри showMenu() в строке 19 мы добавляем прослушиватель событий в document, чтобы вызывать closeMenu при каждом нажатии. (Подробнее о callback-версии setState читайте здесь.)

И так как мы хотим, чтобы он запускался только один раз, мы также удаляем обработчик событий, добавленный к document внутри closeMenu в строке 25.

Теперь давайте посмотрим на это в действии!

И вроде работает как положено! или по крайней мере… вроде того.

Хотя это работает так, как мы хотим, вы заметили в конце ☝️, что нажатие на любой из пунктов меню внутри меню также закрывает меню! Это то, чего мы, возможно, не хотим.

Как это исправить? 🤔

Проверьте .contains ()

Один из способов обойти это - проверить, исходит ли происхождение нашего события щелчка от элемента, содержащегося в раскрывающемся меню, - в основном, от его дочерних элементов.

Для этого нам сначала понадобится ссылка на раскрывающееся меню. Мы можем использовать свойство ref, чтобы получить ссылку на элемент DOM:

Здесь, в строках 43–45, мы используем свойство ref, которое предоставляет React, чтобы установить переменную класса с именем this.dropdownMenu, чтобы она указывала на ссылку DOM <div class="menu"> ... </div>.

Теперь, когда у нас есть ссылка, когда мы вызываем closeMenu, давайте проверим, является ли источник события, к которому мы можем получить доступ через event.target, узел DOM, содержащийся в элементе this.dropdownMenu.

Строка 25 - это то место, где мы проверяем, содержит ли this.dropdownMenu event.target. Другими словами, мы проверяем, было ли происхождение события из самого меню, и только если это не так, мы устанавливаем состояние и удаляем обработчик события из document.

Теперь посмотрим, как это работает!

И работает отлично! 😄

Щелчок по кнопке «Показать меню» отображает меню. Щелчок по любой кнопке внутри меню больше не закрывает меню, но щелчок за пределами меню закрывает его. 🎉

~ Фин ~

Если вы нашли это полезным, оставьте 👏!

Где-то застряли, нужна дополнительная помощь или просто хотите поздороваться? Отправьте мне прямой вопрос на Hashnode или напишите мне в Twitter. Вы также можете найти меня на Github. 🙃