Содержание
Нафига?
JQuery - это библиотека, которая позволяет сделать глупый html умнее и интерактивнее. Сложные анимации, выпадающие менюшки, обмен данными с сервером - вот это вот всё и ещё много всякого, на сколько хватит фантазии.
Установка
Есть много способов добавить JQuery в проект. Самый простой - подключить библиотеку с какого-нибудь CDN:
Некоторые другие CDN указаны на сайте jquery.
Более сложные способы включают в себя установку через npm и ручную установку и для данного учебника не интересны.
$
Функция $() является базовой функцией библиотеки и может использоваться для множества целей. Например, если передать в неё любой css-селектор, то она возвратит коллекцию html-элементов, соответствующих им. Важно отметить, что в случае, когда не удалось найти ни одного элемента по заданному селектору, будет возвращена пустая коллекция. Взаимодействие с ней ничем не отличается от полной - JQuery просто тратит меньше сил.
Кроме того доступны несколько особых псевдо-классов JQuery, которых нет в css, например, :first, :last, :checked, :selected и другие. Полный список можно посмотреть в документации.
Другим применением является создание кусков DOM из строк. Достаточно просто передать в функцию строку с куском html-разметки и она создаст несвязанные элементы, с которыми можно работать так же, как и с обычными, а потом вставить куда-нибудь на страницу.
Помимо этого, в функцию $() можно передавать массивы, html-элементы, любые скалярные элементы, например, числа или строки, и всё это JQuery превратит в коллекцию.
И что с этим потом делать?
А дальше начинается интересное. В JQuery предусмотрено множество функций для взаимодействия с разметкой, начиная от установки текста, заканчивая анимациями, изменением структуры страницы и другими страшными вещами.
Текстовые функции, такие, как .text() и .html(), можно вызывать без параметров, чтобы получить текущее значение. К другим подобным функциям относятся .val() для получения и установки значения (value) input, .data() для data-атрибутов, .prop() и .attr() для значений атрибутов (разница в том, что первая конвертирует значение в наиболее подходящий тип JS, а вторая всегда возвращает сырой текст, как он записан в html). Если любую из них вызвать на коллекции из более чем одного элемента, то вместо одного значения будет возвращена их коллекция, которую можно сконвертировать в массив при помощи функции .get(). При этом .text() и .html() всё равно возвращают одну общую строку.
Коллекции $
В JQuery есть несколько встроенных инструментов для обработки коллекций: циклы по элементам, изменение, фильтрация, поиск и кое-что ещё.
Пример: в корзине лежат кучками фрукты разных размеров. Посчитаем количество бананов в кучках больше 3 штук. Для этого нужно сначала найти все кучки бананов, потом собрать их количества, отфильтровать слишком маленькие и, наконец, просуммировать.
События $
Всё это было бы бесполезно, если бы нельзя было приурочить к чему-то, например, к клику по кнопке или наведению курсора на пункт меню. Для этого в JavaScript существует концепция событий. Чтобы что-то сделать во время какого-либо события, его нужно перехватить. В JQuery сотворить это можно при помощи функции .on() или одной из специальных функций-перехватчиков событий: .click(), .change(), .blur() и так далее.
В самом простом виде функция .on() принимает два параметра: название события и функцию-обработчик, которая будет вызвана при перехвате события.
Самое главное событие, которое происходит в жизни каждого html-документа — это ready, то есть завершение загрузки. В этот момент можно создавать другие обработчики событий, всё, что создано за его пределами работать не будет, потому что код выполняется по мере загрузки, а до её завершения DOM не существует полностью.
Есть несколько способов создать такой обрабочик:
Для простоты вместе с JQuery обычно используется второй способ.
И теперь, зная, как создаются события и производятся действия над всякими элементами, можем немного поиграться:
Есть ещё несколько вещей, которые нужно знать о события. Они не умирают после перехватывания первым обработчиком, а продолжают распространяться дальше по дереву элементов до самого верха и заканчивают свой путь только на глобальном объекте document. Отсюда следует возможность перехватывать события не только непосредственно на самих объектах, но и на любом из предков, а так же прерывать их распространение, когда это необходимо.
Функция-обработчик в качестве параметра принимает объект события, который содержит несколько специальных методов. Нас интересуют Event.preventDefault() и Event.stopPropagation(). Первый отменит действие по умолчанию для элемента, например, переход на другую страницу для ссылки (для чего обычно и используется), а второй остановит распространение события, чтобы никто его не смог перехватить после этого момента.
Чёрная магия и телепатия $
Многим приложениям приходится так или иначе взаимодействовать с серверной частью или даже с другими сайтами. Для этого в JS применяется технология XMLHttpRequest или fetch Api из ES6. В JQuery реализована удобная обёртка над первой в виде функции $.ajax и нескольких упрощений над ней.
Подробнее о функции тут.
Более сложный пример. Напишем маленькое приложение, который будет на основании данных из формы запрашивать несколько предложений lorem ipsum через сервис Bacon Ipsum.
Расширение возможностей
Очень часто нужно иметь возможность сделать какое-то сложное действие, которое не предусмотрено в JQuery из коробки, например, отобразить импровизированное модальное окно с формой, оформить галерею с картинками или стилизовать select. Для очень многих подобных вещей хорошие люди уже написали плагины JQuery. К примеру, практически все интерактивные компоненты Bootstrap являются плагинами JQuery.
Конкретные инструкции разнятся от плагина к плагину. В общем случае это мало чем отличается от всех остальных функций JQuery для элементов. Например, плагин модальных окон Bootstrap позволяет таким образом в ручном режиме управлять поведением окон, а так же перехватывать их события.
Разумеется, при необходимости можно самому написать собственный плагин. Процесс не сложный, но его рассмотрение выходит за рамки мануала.
Закрепление
В качестве завершения запишем небольшое приложение, которое будет получать при помощи ajax по api каталоги с картинками, просматривать, редактировать заголовки, добавлять новые каталоги и картинки, удалять старые.
В качестве api для примера используется сервис JSONPlaceholder с зеркалом, на случай, если он будет глючить. Для диалоговых окон используется библиотека Bootbox.js.
JSONPlaceholder предоставляет простой api на основе REST. Основные использованные запросы следующие:
- GET /albums/ - получение каталогов
- GET /albums/
/photos/ - получение картинок в каталоге - POST /albums/
/photos/ - добавление картинки в каталог - PUT /photos/
- изменение [подписи] картинки - DELETE /photos/
- удаление картинки
План работы
- Создадим страницу с двумя большими блоками: для каталогов и для картинок
- В блоке каталогов будет кнопка добавления нового каталога и таблица со списком ссылок на каталоги и кнопками удаления и изменения названия.
- При клике на ссылку каталога будет загружаться список картинок в нём и отображаться на странице, а сам список каталогов - скрываться.
- В списке картинок аналогично должна быть кнопка добавления новой, при клике на которую открывается модальное окно с двумя полями: подписью к картинке и ссылкой на неё.
- При клике на картинку должно открываться модальное окно с увеличенной версией и кнопками удаления и изменения подписи.
Макет
Страница состоит из двух частей: блока каталогов и блока картинок.
Список каталогов выведем в виде обычной таблицы с названием-ссылкой в первой колонке и кнопками управления во второй. Над таблицей будет кнопка создания нового каталога.
Список картинок представим в виде сетки с предпросмотром. Для экономии места воспользуемся карточками с оверлеем и будем выводить подпись к картинке прямо поверх превью. При клике на карточку будет открываться модальное окно с увеличенной копией и кнопками удаления картинки и изменения подписи. Над сеткой картинок поместим кнопки возвращения к списку каталогов и добавления новой картинки.
Каталоги
Сразу после загрузки страницы нужно запросить у сервера список каталогов и вывести его в таблицу (функция loadFolders()). Согласно документации JSONPlaceholder, для этого нужно выполнить операцию GET /albums/ после чего получим массив каталогов. Этот массив нужно превратить в html-код, прежде чем добавлять на страницу. Для этого воспользуемся функцией $().map(). Чтобы было удобнее и понятнее, вынесем кусок шаблона, соответствующий строке таблицы, в отдельную функцию, в которой и будем вставлять в него недостающие части: id и название каталога.
При нажатии на кнопку удаления сперва нужно показать предупреждение. Проще всего воспользоваться функцией confirm() из библиотеки Bootbox, которая в качестве параметра принимает обработчик результата. Если нажата кнопка "OK", то выполняем запрос DELETE /albums/<id>, где <id> является id каталога. Для удобства мы можем его перенести в шаблон в виде атрибута data-id="<id>". При завершении запроса просто удаляем строку из таблицы.
Для редактирования названия так же воспользуемся библиотекой Bootbox, а точнее
функцией prompt. Изменение названия делаем запросом
PUT /albums/
Создание нового каталога практически полностью аналогично редактированию за исключением того, что не нужно передавать id каталога и тип запроса меняется с PUT на POST. При успехе создаём фрагмент html-кода из шаблона, как делали это при загрузке списка, и добавляем новый элемент в начало списка каталогов.
Реализацию перехода к списку картинок пока отложим на будущее.
Картинки
Здесь всё то же самое, что и с каталогами. Для начала сделаем загрузку картинок. По документации это запрос GET /albums/<id>/photos/. Для примера вместо id будем просто передавать 1, но при объединении это нужно будет исправить. Для шаблона, кроме id картинки и подписи, ещё нужны будут ссылки на превью и полную версию.
При клике на картинку, прежде чем показать модальное окно, нужно выполнить несколько действий. Во-первых, поменять заголовок окна. Подпись уже есть в тексте заголовка карточки, оттуда и возьмём. Во-вторых, нужна ссылка на картинку. В шаблоне карточка была создана в виде ссылки, вместо div, чтобы картинку можно было открыть в новой вкладке браузера. В href как раз ссылка на полную версию, что нам и нужно. В-третьих, чтобы не потерять id картинки, сохраним его в data-id окна. Так нам не придётся долго думать над тем, какую же картинку нужно редактировать или удалять. В качестве дополнения можно было бы сделать очистку заголовка и картинки при закрытии окна, чтобы при следующем открытии не мерцала старая, но это оставляю в качестве домашнего задания.
Добавление картинки работает просто. Сначала нужно показать диалог добавления, при клике на кнопку создания, взять значения из полей заголовка и ссылки, а потом сделать запрос POST /albums/1/photos/, передавая в его теле полученные значения, и, наконец, создать фрагмент html-кода для картинки из шаблона и добавить её в начало списка. В общем, отличие от создания каталога только в том, что мы не пользуемся Bootbox.
Удаление и редактирование аналогичны каталогам, но есть нюанс. Поскольку кнопки находятся не непосредственно в блоке с превью, её ещё нужно потом найти. Проще всего использовать селектор по data-id карточки. Во всём остальном процесс не отличается: для редактирования делаем запрос PATCH /photos/<id> с новым заголовком, для удаления - просто DELETE /photos/<id>.
Реализацию возврата к списку каталогов пока отложим, хотя сама по себе она тривиальна.
Собираем всё в одну кучу
Дело за малым: нужно добавить крутилку прогресса, перенести переход к списку картинок в клик по ссылке каталога, сделать переход обратно к списку каталогов.
Самым правильным способом добавить спиннер, будет использование стандартных функций JQuery для обработки событий начала и конца ajax-запросов: $().ajaxStart(), $().ajaxStop() и $().ajaxError().
В процедуру загрузки картинок нужно добавить один важный шаг: скрытие каталога и отображение блока картинок. Собственно, возврат обратно к списку будем выполнять тем же самым методом, то есть скрывать картинки и обратно показывать блок каталога.