Статьи

Создание галереи изображений с помощью Flickr API — стиль и логика

В первой части этого мини-сериала о том, как разработать простую галерею с использованием Flickr API, мы обсудили требования проекта, разметку, необходимую для структурирования HTML-страницы, и два из пяти CSS-модулей.

Во второй и последней части мы рассмотрим оставшиеся модули CSS и код JavaScript, который обеспечивает работу проекта. Без дальнейших церемоний, давайте начнем.

Стили (продолжение)

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

Модуль галереи определяет стили галереи и ее компонентов. Он состоит из простых объявлений, и я выделю несколько интересных моментов.

Во-первых, элемент с классом gallery , который выступает в качестве контейнера для фотографии, показанной в натуральном размере, имеет фиксированную высоту 500 пикселей. Затем элемент img внутри него, используемый для отображения выбранного изображения, ограничивается установкой его свойств max-height и max-width равным 100% . Тем самым мы гарантируем, что изображение не переполняет контейнер.

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

Если вы считаете себя новичком в CSS, вы можете также изучить, как кнопки сделаны круглыми и как рисуются стрелки.

Полный код этого модуля представлен ниже:

 .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } тем .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } того, как .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } тем .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } того, как .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } тем .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } тем .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } того, как .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } того, как .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } тем .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } того, как .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } тем .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } того, как .gallery { position: relative; height: 500px; border: 1px solid #FFFFFF; } .gallery img { display: block; margin: 0 auto; max-width: 100%; max-height: 100%; } .gallery__arrow { position: absolute; top: 50%; display: block; width: 60px; height: 60px; border: none; border-radius: 50%; background-color: #000000; opacity: 0.7; cursor: pointer; } .gallery__arrow:hover, .gallery__arrow:focus { opacity: 1; } .gallery__arrow:before, .gallery__arrow:after { content: ''; position: absolute; width: 10px; height: 40%; background-color: #FFFFFF; } .gallery__arrow:before { bottom: 12px; } .gallery__arrow:after { top: 12px; } .gallery__arrow:hover:before, .gallery__arrow:focus:before, .gallery__arrow:hover:after, .gallery__arrow:focus:after { background-color: #FCB712; } .gallery__arrow--left { left: 0.5em; } .gallery__arrow--left:before { transform: rotate(-40deg); left: 35%; } .gallery__arrow--left:after { transform: rotate(40deg); left: 35%; } .gallery__arrow--right { right: 0.5em; } .gallery__arrow--right:before { transform: rotate(40deg); right: 35%; } .gallery__arrow--right:after { transform: rotate(-40deg); right: 35%; } 

Модуль миниатюр

Модуль миниатюр не содержит ничего особенного. Это заставляет миниатюры быть пятью подряд, устанавливая свойство width на 19% , margin-right на 1% и свойство display inline-block . Другой момент, о котором стоит упомянуть, — это эффект, возникающий при наведении или фокусировке миниатюры для улучшения доступности, как обсуждалось в предыдущем разделе.

Полный код этого модуля выглядит следующим образом:

 .thumbnails__list, .thumbnails__pager { margin: 0; padding: 0; list-style-type: none; } .thumbnails__list li { display: inline-block; width: 19%; margin-top: 1%; margin-right: 1%; } .thumbnail { width: 100%; } .thumbnail:hover, .thumbnail:focus { border: 1px solid #FCB720; opacity: 0.7; } .thumbnails__pager { text-align: right; margin: 0.5em 0; } .thumbnails__pager li { display: inline; } .thumbnails__pager a { margin: 0 0.2em; color: #FFFFFF; text-decoration: none; } .thumbnails__pager a.current, .thumbnails__pager a:hover, .thumbnails__pager a:focus { color: #FCB720; text-decoration: underline; } 

Модуль домашней страницы

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

Полный код файла homepage.css представлен ниже:

 .form-search { margin: 0.5em 0; text-align: right; } .form-search #query { padding: 0.2em; } .form-search input { color: #000000; } .thumbnails { border-bottom: 3px solid #FFFFFF; } .copyright { margin-top: 0.5em; margin-bottom: 0.5em; text-align: right; } 

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

Деловая логика

Бизнес-логика проекта также организована в небольшие модули с одним файлом — main.js, который служит связующим звеном между разметкой и модулями JavaScript. В этом файле мы определим обработчики событий для кнопок галереи, что происходит, когда пользователь нажимает одну из ссылок в пейджере, и что делать, когда пользователь ищет какой-то заданный текст.

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

Имея в виду эти моменты, давайте посмотрим на определенные модули.

Сервисный модуль

Первый модуль, который мы обсудим, — это служебный модуль. Он содержит методы, представляющие общий интерес и используемые нашими модулями JavaScript. Он определяет только два метода: extend и buildUrl .

Метод extend является упрощенной версией своего тезки в jQuery и используется для объединения свойств двух или более объектов в один (первый параметр). Если вы не ниндзя JavaScript, вы можете узнать, как я разрешил этому методу принимать произвольное количество объектов с помощью arguments . arguments — объект в стиле массива, соответствующий аргументам, переданным функции.

Метод buildUrl используется для создания действительного URL-адреса, содержащего строку запроса, начиная с URL-адреса и объекта имен и значений для использования в строке запроса.

Код служебного модуля определяется следующим образом:

 (function(document, window) { 'use strict'; function buildUrl(url, parameters){ var queryString = ''; for(var key in parameters) { if (parameters.hasOwnProperty(key)) { queryString += encodeURIComponent(key) + '=' + encodeURIComponent(parameters[key]) + '&'; } } if (queryString.lastIndexOf('&') === queryString.length - 1){ queryString = queryString.substring(0, queryString.length - 1); } return url + '?' + queryString; } function extend(object) { for(var i = 1; i < arguments.length; i++) { for(var key in arguments[i]) { if (arguments[i].hasOwnProperty(key)) { object[key] = arguments[i][key]; } } } return object; } window.Utility = { buildUrl: buildUrl, extend: extend }; })(document, window); 

Модуль gallery определяет объект Gallery отображаемый в глобальной области видимости. Его конструктор принимает два параметра: список фотографий (т.е. массив, содержащий URL-адреса фотографий), принадлежащих галерее, и элемент DOM, который покажет изображение в натуральном размере. Этот объект определяет функции нашей галереи, такие как возможность показать предыдущее (метод showPrevious ) или следующее (метод showNext ) изображение или создать список миниатюр (метод createThumbnailsGallery ).

Этот модуль демонстрирует интересную технику для решения общей проблемы замыкания, которая возникает при работе с циклами и обработчиками событий. Я обсудил эту проблему и ее решение в моей статье 5 «Дополнительные упражнения по JavaScript» (пункты 1 и 2). Здесь функция, определенная вне цикла, называется clickHandler() .

Теперь, когда вы знаете хитрости, используемые в этом модуле, вы готовы прочитать его полный исходный код:

 (function(document, window) { 'use strict'; function Gallery(photos, container) { this.currentIndex = 0; this.photos = photos; this.container = container; this.showPhoto(this.currentIndex); } Gallery.prototype.showPhoto = function(index) { if (index >= 0 && index < this.photos.length) { this.currentIndex = index; this.container.src = Flickr.buildPhotoLargeUrl(this.photos[this.currentIndex]); } }; Gallery.prototype.showPrevious = function() { if (this.currentIndex > 0) { this.currentIndex--; } this.showPhoto(this.currentIndex); }; Gallery.prototype.showNext = function() { if (this.currentIndex < this.photos.length - 1) { this.currentIndex++; } this.showPhoto(this.currentIndex); }; Gallery.prototype.createThumbnailsGallery = function(container) { function clickHandler(index, gallery) { return function (event) { event.preventDefault(); gallery.showPhoto(index); }; } container.textContent = ''; var image, link, listItem; for (var i = 0; i < this.photos.length; i++) { image = document.createElement('img'); image.src = Flickr.buildThumbnailUrl(this.photos[i]); image.className = 'thumbnail'; image.alt = this.photos[i].title; image.title = this.photos[i].title; link = document.createElement('a'); link.href = image.src; link.addEventListener('click', clickHandler(i, this)); link.appendChild(image); listItem = document.createElement('li'); listItem.appendChild(link); container.appendChild(listItem); } }; window.Gallery = Gallery; })(document, window); 

Модуль Flickr

В некотором смысле модуль Flickr является ядром нашего приложения, поскольку он определяет код, который использует Flickr API. В отличие от других модулей, которые мы рассмотрели ранее, вы можете расширить этот модуль, чтобы предоставить больше возможностей. Например, вы можете расширить его для поиска фотографий на основе имени пользователя или на основе расположения фотографий. По этой причине вместо того, чтобы просто выставлять объект Flickr в глобальной области видимости, я буду использовать метод Utility.extend() , как показано ниже:

 window.Flickr = Utility.extend(window.Flickr || {}, { /* methods of this module defined here */ }); 

Метод Utility.extend() используется в другой части этого модуля и, в частности, в первом операторе метода searchText() . В этом случае он используется для объединения параметров, передаваемых вызывающей searchText() метода searchText() с частной информацией о модуле, которую вызывающая searchText() не должна знать (и которая, таким образом, остается конфиденциальной), такой как вызываемый метод API ( flickr.photos.search ).

Этот модуль нуждается в ключе API для связи с Flickr API. Я не могу поделиться своим ключом API со всем миром, поэтому вам нужно вставить свой собственный как значение переменной apiKey чтобы получить полностью рабочий проект. Если вы не предоставите такой ключ, все ваши запросы к Flickr не будут выполнены.

Имея это в виду, вот полный код этого модуля:

 (function(document, window) { 'use strict'; var apiKey = 'YOUR-API-KEY-HERE'; var apiURL = 'https://api.flickr.com/services/rest/'; function searchText(parameters) { var requestParameters = Utility.extend(parameters, { method: 'flickr.photos.search', api_key: apiKey, format: 'json' }); var script = document.createElement('script'); script.src = Utility.buildUrl(apiURL, requestParameters); document.head.appendChild(script); document.head.removeChild(script); } function buildThumbnailUrl(photo) { return 'https://farm' + photo.farm + '.staticflickr.com/' + photo.server + '/' + photo.id + '_' + photo.secret + '_q.jpg'; } function buildPhotoUrl(photo) { return 'https://farm' + photo.farm + '.staticflickr.com/' + photo.server + '/' + photo.id + '_' + photo.secret + '.jpg'; } function buildPhotoLargeUrl(photo) { return 'https://farm' + photo.farm + '.staticflickr.com/' + photo.server + '/' + photo.id + '_' + photo.secret + '_b.jpg'; } window.Flickr = Utility.extend(window.Flickr || {}, { buildThumbnailUrl: buildThumbnailUrl, buildPhotoUrl: buildPhotoUrl, buildPhotoLargeUrl: buildPhotoLargeUrl, searchText: searchText }); })(document, window); 

Связывая все вместе: основной модуль

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

Пейджер отображает до шести страниц плюс специальные «кнопки» (на самом деле все они являются элементами) для перехода на первую и последнюю страницу и для перехода на предыдущую и следующую страницы. При щелчке по одному из элементов пейджера сервис должен показать миниатюры, принадлежащие этой странице. Например, если пользователь нажимает на страницу 3 (и помните, что каждая страница содержит 15 миниатюр), служба должна показывать пользователю фотографии, которые принадлежат этой странице, с 31 по 45 найденные фотографии (если есть) , Чтобы выполнить это действие, мы могли бы добавить слушателя к каждой ссылке на пейджер плюс специальные кнопки, но это было бы пустой тратой памяти. Мы можем сделать это намного эффективнее, используя технику, называемую делегированием событий . Таким образом, вместо добавления слушателя для каждого дочернего элемента пейджера, мы добавим только одного слушателя к самому пейджеру. Затем, основываясь на элементе, по которому сработало событие click, мы выполним ожидаемое действие. (Если вы не знакомы с этой темой, вы можете прочитать статью Дэвида Уолша « Как работает делегирование событий JavaScript» .)

Второй момент, который я хочу упомянуть, состоит в том, что вместо добавления прослушивателя событий на две стрелки только для события click , я также добавил прослушиватель для события keydown . Таким образом, я могу определить, нажал ли пользователь клавишу на клавиатуре, когда фокус был на стрелке. Затем, если нажатой клавишей была клавиша ENTER , я выполняю то же действие, которое ожидал бы пользователь, если вместо этого было инициировано событие щелчка. Этот простой подход позволяет нам улучшить доступность сервиса для тех пользователей, которые перемещаются по сайту с клавиатуры.

Обе эти интересные части можно найти в функции init() , которая показана ниже вместе с полным кодом основного модуля:

 (function(document, window) { 'use strict'; var gallery; var lastSearch = 'London'; function searchPhotos(text, page) { if (text.length === 0) { alert('Error: the field is required'); } page = page > 0 ? page : 1; Flickr.searchText({ text: text, per_page: 15, jsoncallback: 'Website.Homepage.showPhotos', page: page }); } function createPager(element, parameters) { var pagesToShow = 5; var url = '/search/' + parameters.query + '/'; element.textContent = ''; var previousLinks = { '<<': 1, '<': (parameters.currentPage - 1 || parameters.currentPage) }; for (var key in previousLinks) { link = document.createElement('a'); link.href = url + previousLinks[key]; link.innerHTML = '<span class="js-page-number visually-hidden">' + previousLinks[key] + '</span>' + key; var listItem = document.createElement('li'); listItem.appendChild(link); element.appendChild(listItem); } // Avoid showing less than 6 pages in the pager because the user reaches the end var pagesDifference = parameters.pagesNumber - parameters.currentPage; var startIndex = parameters.currentPage; if (pagesDifference < pagesToShow) { startIndex = parameters.currentPage - (pagesToShow - pagesDifference - 1) || 1; } var link; for(var i = startIndex; i < parameters.currentPage + pagesToShow && i <= parameters.pagesNumber; i++) { link = document.createElement('a'); link.href = url + i; link.innerHTML = '<span class="js-page-number">' + i + '</span>'; if (i === parameters.currentPage) { link.className += ' current'; } listItem = document.createElement('li'); listItem.appendChild(link); element.appendChild(listItem); } var nextLinks = { '>': (parameters.currentPage === parameters.pagesNumber ? parameters.pagesNumber : parameters.currentPage + 1), '>>': parameters.pagesNumber }; for (key in nextLinks) { link = document.createElement('a'); link.href = url + nextLinks[key]; link.innerHTML = '<span class="js-page-number visually-hidden">' + nextLinks[key] + '</span>' + key; var listItem = document.createElement('li'); listItem.appendChild(link); element.appendChild(listItem); } } function showPhotos(data) { createPager( document.getElementsByClassName('js-thumbnails__pager')[0], { query: lastSearch, currentPage: data.photos.page, pagesNumber: data.photos.pages } ); gallery = new Gallery(data.photos.photo, document.getElementsByClassName('js-gallery__image')[0]); gallery.createThumbnailsGallery(document.getElementsByClassName('js-thumbnails__list')[0]); } function init() { document.getElementsByClassName('js-form-search')[0].addEventListener('submit', function(event) { event.preventDefault(); lastSearch = document.getElementById('query').value; if (lastSearch.length > 0) { searchPhotos(lastSearch, 1); } }); var leftArrow = document.getElementsByClassName('js-gallery__arrow--left')[0]; leftArrow.addEventListener('click', function() { gallery.showPrevious.bind(gallery)(); }); leftArrow.addEventListener('keydown', function(event) { if (event.which === 13) { gallery.showPrevious.bind(gallery)(); } }); var rightArrow = document.getElementsByClassName('js-gallery__arrow--right')[0]; rightArrow.addEventListener('click', function() { gallery.showNext.bind(gallery)(); }); rightArrow.addEventListener('keydown', function(event) { if (event.which === 13) { gallery.showNext.bind(gallery)()(); } }); document.getElementsByClassName('js-thumbnails__pager')[0].addEventListener('click', function(event) { event.preventDefault(); var page; var currentLink = this.getElementsByClassName('current')[0]; if (event.target.nodeName === 'SPAN') { page = event.target.textContent; } else if (event.target.nodeName === 'A') { page = event.target.getElementsByClassName('js-page-number')[0].textContent; } // Avoid reloading the same page if (page && page !== currentLink.getElementsByClassName('js-page-number')[0].textContent) { searchPhotos(lastSearch, page); } }); // Kickstart the page searchPhotos(lastSearch, 1); } window.Website = Utility.extend(window.Website || {}, { Homepage: { init: init, showPhotos: showPhotos } }); })(document, window); Website.Homepage.init(); 

С кодом этого последнего файла мы наконец-то завершили наш проект.

Вывод

В этой статье, состоящей из двух частей, я провел вас через создание простого сервиса, основанного на внешнем API. Используя API-интерфейс Flickr, мы позволили пользователю создавать галерею фотографий Flickr с помощью поиска по их заголовкам и описаниям. Надеюсь, вам понравилось, и вы узнали некоторые новые и интересные методы или подходы.

Исходный код проекта доступен на моей учетной записи GitHub в репозитории с именем Flickr gallery demo .