Идеально! Как создать и переделать свой сайт. Правильный подход и передовые техники разработки

Стокс Элиот Джей

Веру Ли

Эндрю Рэйчел

Фадеев Дмитрий

Балкан Арэл

Хейлманн Кристиан

Боуг Пол

Эдвардс Марк

Уолтер Аарон

Шварц Бен

Кларк Энди

Хей Стивен

Стори Дэвид

Повторное открытие JavaScript: фишки и приемы для замены сложного jQuery

Автор: Кристиан Хейлманн

Рецензент: Пол Айриш

 

 

Когда появилась библиотека jQuery, это было абсолютным открытием. Ее первостепенной и главной задачей было обеспечивать одинаковое поведение браузеров. До этого поддержка основных функций, таких как доступ к частям документа, реагирование на взаимодействие системы с пользователем и даже стиль оформления элементов, очень отличалась в браузерах.

jQuery заменила спецификацию DOM, которая определяла доступный контент на странице со свойствами getElementById() и getElementsByTagName(), более простым методом: используя CSS-селекторы. Перед дизайнерами открылся совершенно новый мир разработки. Ведь они знали CSS, но страдали от ежедневных срывов браузеров, которые не поддерживали сложные селекторы. Другими словами, jQuery позволила нам использовать CSS завтрашнего дня уже сегодня. Упомянутый выше метод сцепления jQuery (это значит, что кодов будет создаваться намного меньше) позволил быстро достичь успеха.

Очень скоро, примерно через несколько лет (и к тому времени, как вы будете читать эту книгу), мы продвинемся еще дальше. У нас есть HTML5, мы владеем поддержкой CSS3, у нас очень много всего, с чем можно развлечься в браузерах, которые установили мы и наши пользователи. Да, IE 6 все еще наш бич, и IE 8 будет с нами еще какое-то время. Но в общем и целом наше положение не такое уж и плохое. Такие библиотеки, как jQuery, дают основные преимущества при исправлении недочетов в старых браузерах, но они также вызывают и некоторую неудовлетворенность. А причина в том, что мы злоупотребляем ими.

Как сообщество, мы стали зависимы от jQuery. Это и понятно, но далеко не хорошо. Библиотека jQuery написана на JavaScript, но это не одно и то же; она исходно не встроена в браузер. С расцветом мобильного Интернета многие отворачиваются от jQuery, потому что она слишком «тормозит» и тяжела для наших модных карманных устройств. Найти хороших разработчиков JavaScript сложно: на каждое объявление о вакансии на должность разработчика JavaScript Вы получите около двадцати резюме от людей, которые в жизни не писали на чем-то, кроме jQuery. Это «обескровливает» наше ремесло.

Давайте посмотрим, что предлагают нам браузеры сегодня, и что мы можем применить для написания невероятно маленьких и удобных решений, не прибегая к jQuery. Многие из этих вещей также помогут нам написать более чистый и быстрый jQuery-код. Из-за того что библиотека jQuery абстрагируется от многих проблем, с которыми мы сталкиваемся как разработчики, чересчур легко написать код, который выглядит просто, но погружает результаты в пучину циклов и сравнений. А это – одна из причин того, что сайты «тормозят».

 

Сила смешивания и сочетания

В Интернете основной хитростью при разработке кода, который будет кратким, неизменяемым, эффективным и легким для поддержания, является разделение и делегирование. С jQuery мы забыли многое из этого. Чрезмерная длина ломает CSS селекторы в наших скриптах, когда код HTML изменяется.

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

• HTML – это структура и база для построения

Ваш HTML должен иметь смысл и обеспечивать базовую функциональность: ссылки на другие сайты, кнопки, которые отсылают формы к скрипту для перезагрузки страницы, и элементы, создающие контент. Дело в том, что когда все «слетает», браузер остается только с HTML. Если HTML несет смысл, вы – победитель. Но если есть кнопка, которая ничего не делает, вы будете раздражать пользователей.

• CSS определяет интерактивность и визуальность.

Мы шагнули за пределы шрифтов и цвета, к анимации и перемещениям. Медиазапросы позволяют нам делать всевозможные макеты для разных устройств. Используя генерирование контента, мы можем создавать элементы для достижения определенного визуального эффекта без «загрязнения» тегами div и span.

• JavaScript привносит дополнительные функции.

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

Хитрость в том, чтобы объять все эти новые возможности и не позволить старым браузерам «задушить» их. В конце концов, старые приемы никому не помогут. Да, вы могли бы анимировать меню в IE 6, но к чему напрягаться и писать эту функцию, которую «не мешало бы иметь», когда она встроена в другие браузеры?

 

Функции, доступные нам сейчас

Давайте посмотрим на некоторые функции браузеров, которые мы можем использовать сейчас. Чтобы получить последнюю информацию о том, какие браузеры их поддерживают, обратитесь к великолепному источнику When Can I Use, который постоянно обновляется. Кое-что из того, что я рассказываю вам о браузерной поддержке сейчас, через несколько недель может устареть. Это темп нашей жизни и мы должны поддерживать его.

 

QUERYSELECTOR и QUERYSELECTORALL

Теперь, когда мы узнали об успехе jQuery, у браузеров есть способ обращения к элементам на странице с помощью селекторов CSS. Метод querySelector обращается к одному элементу, а querySelectorAll – к списку соответствующих элементов. Синтаксис селектора подобен CSS. Поэтому, document.querySelector(‘#content p’) обратится к первому тегу параграфа в элементе с ID content; document.querySelector(‘nav li: last-child’) обратится к последней позиции списка в первом элементе nav; а document.querySelectorAll(‘p’) обратится ко всем параграфам документа. Все просто.

 

Работаем с классами: CLASSLIST

Масштабный вариант использования jQuery – обратиться сразу ко многим элементам, и изменить стили по методу css(). Это удобно, но в тоже время, раздражает, потому что вы вставляете информацию о стиле в JavaScript. Гораздо проще было бы добавлять класс к рассматриваемому элементу, а остальное предоставить CSS. Из-за этого мы часто повторяли селекторы CSS в jQuery и наших таблицах стилей. Мы вынуждены были делать это, потому что браузеры не поддерживали селекторы CSS3, но сейчас-то поддерживают!

Невероятную мощь дает возможность тестировать классы в элементах HTML и динамично добавлять и удалять их. В JavaScript мы сейчас имеем свойство classList в HTML элементах, которое содержит коллекцию применяемых CSS классов. Раньше это делалось через className, который состоял из простой символьной строки и от нас зависело, найдем ли мы другие строки в ней, или добавим и удалим подстроки из нее. С помощью classList у нас для этого есть методы. Мы можем использовать element.classList.add(name) для добавления класса, element.classList.remove(name) – для его удаления, element.classList.contains(name) для проверки, применяется ли класс, и element.classList.toggle(name) для включения и выключения класса. Позже в этом разделе мы увидим, как это действенно. Мы сможем избежать введения многих циклов, просто добавляя класс к элементу родителя.

 

Меняем плавно: CSS TRANSITIONS

Анимация в jQuery в самом деле легкая и смотрится очень ровно. Это из-за того, что jQuery включены easing equations, и сегодня они есть также в CSS. Поэтому, если вы хотите растянуть заголовок и поменять цвет его фона со светло-зеленого на оранжевый, вы можете использовать фрагмент со следующей страницы.

h1 {

background: #c0ffee;

line-height: 1em;

padding: 0.5em 1em;

– webkit-transition: 1s;

– moz-transition: 1s;

– ms-transition: 1s;

– o-transition: 1s;

transition: 1s;

}

h1:hover, h1:focus {

background: goldenrod;

line-height: 3em;

}

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

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

Другой плюс в том, что эти перемещения поддерживаются на аппаратном уровне. А это значит, что они могут происходить более плавно и тратить меньше зарядки в мобильных устройствах. Пока еще не все браузеры способны на это (некоторым нужна 3D-трансформация от 0 на оси Z в виде хака). Но, несомненно, со временем эта функция станет стандартом. Более подробно о перемещениях CSS3 вы можете прочесть в четвертом разделе этой книги.

 

Контент, сгенерированный посредством CSS

Порой разработчикам нужно больше HTML элементов для создания стиля (возьмем, как пример из прошлого, закругленные углы). В большинстве случаев мы используем для этого jQuery. С генератором контента CSS нам это больше не нужно. Мы можем создавать элементы в CSS и одновременно придавать им стиль. Допустим, вы хотите, чтобы все внешние ссылки имели красную стрелку в конце:

a[href^="http"]: after {

content: ' ';

color: #c00;

}

Этот простой CSS код имеет свою «изюминку». Мы определяем (устанавливаем), что для каждой ссылки с атрибутом href, которая начинается с http, текстовой узел – в данном случае это красная стрелка – нужно добавлять в содержимое ссылки.

 

Делегирование событий: от многих до одного

jQuery дает вам одну большую возможность. Вы можете быстро перебирать элементы, чтобы изменять что-нибудь в них или устанавливать обработчики событий. Но на самом деле это и не нужно. Делегирование событий – необычайно мощный инструмент для создания веб-интерфейсов. По существу, вместо того, чтобы устанавливать обработчики событий на каждый элемент внутри главного элемента контента, вы назначаете один обработчик для основного содержимого. А это уже позволит браузерам упорядочить события.

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

jQuery заимствовала этот принцип, когда добавила «живого» обработчика событий. Однако многие решения jQuery будут добавлять «живых» обработчиков к ID селекторам без детей. А по определению ID может появиться только один раз в документе и, поэтому, он не нуждается в делегировании.

 

Техники в примерах

Давайте применим эти техники в нескольких примерах. Начнем со списка дел, в котором используются делегирование событий и сгенерированный контент, потом перейдем к сайту-визитке, который использует перемещения, а в конце начнем применять HTML5 canvas для создания миниатюр в браузере.

 

Пример 1: Простой список дел

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

Используя современные браузерные технологии, мы сможем сделать это в несколько строк кода, без каких-либо элементов цикла. Разметка в HTML достаточно проста:

    placeholder="new item">

    В коде JavaScript также нет ничего особенного:

    var todo = document.querySelector('#todolist'),

    form = document.querySelector('form'),

    field = document.querySelector('#newitem');

    form.addEventListener('submit', function(ev) {

    var text = field.value;

    if (text!== '') {

    todo.innerHTML += '

  • ' + text +'
  • ';

    field.value = '';

    field.focus();

    }

    ev.preventDefault();

    }, false);

    todo.addEventListener('click', function(ev) {

    var t = ev.target;

    if (t.tagName === 'LI') {

    t. parentNode.removeChild(t);

    };

    ev.preventDefault();

    }, false);

    Код, представленный выше, мы начинаем с ввода элементов в документ, который мы хотим создать, применяя querySelector. В данном случае мы будем вводить список, который добавит желаемые элементы, форму, в которой рождаются новые элементы, и поле, в которое вводится новая запись.

    Потом мы добавим слушателя событий к форме, которая считывает значение поля и проверяет, было ли оно заполнено при отправке формы. (Это означает, что пользователь может добавлять новые позиции нажатием клавиши «Enter», помимо непосредственного добавления с помощью мыши. К сожалению, чересчур много jQuery решений используют вместо этого обработчик нажатия кнопку.) Если есть контент, мы добавляем новый элемент в список с помощью innerHTML. После этого мы удаляем текущий текст из поля ввода и ставим на него курсор (чтобы было легко добавлять другие элементы).

    Чтобы пользователь мог удалять из списка завершенные дела, мы добавляем в него обработчик нажатия, считываем цель события и сравниваем его tagName с элементом li. Удаляем мы это с помощью старого метода DOM removeChild(). Это то, что нам нужно сделать для создания списка дел с неограниченным количеством элементов, используя делегирование событий. Ни больше ни меньше.

     

    Продвинутые CSS селекторы и сгенерированный контент для задания стиля

    Теперь мы хотим задать альтернативные цвета элементам списка. Еще мы хотим, чтобы при наведении курсора появлялся чекбокс (галочка), указывающая на то, что по клику список будет помечен как выполненный. Чтобы это сделать, нам не нужны ни JavaScript, ни изображения:

    #todolist li {

    background: #eee;

    min-height: 20px;

    position: relative;

    }

    #todolist li: nth-child(2n) {

    background: #ccc;

    }

    #todolist li: hover: after {

    content: ' ';

    color: #060;

    position: absolute;

    right: 5px;

    }

    Рисунок 5.1. Наш список дел с «галочками»

    Селектор nth-child(2n) указывает браузеру, что нужно окрасить каждую вторую строку в темно-серый цвет, а остальные оттенить светло-серым. Чтобы показать «галочку», когда пользователь проводит курсором над элементами, мы используем селектор: after и создаем «галочку» через таблицу символов UTF-8. Так как каждый элемент списка относительно позиционирован, любой абсолютно позиционированный «впадает» в него. Таким образом, правильное значение покажет зеленую «галочку» внутри блока, когда пользователь наведет курсор над элементом списка.

     

    Делаем удаление элемента списка в два этапа

    Что делать, если мы хотим, чтобы элементы в нашем перечне не только отмечались как завершенные, но и удалялись при втором клике? Все просто: мы всего лишь добавим другой режим и применим классы. Когда пользователь щелкает на элемент в первый раз, добавляется класс done, а когда второй раз, элемент удаляется. Все, что нам нужно сделать, – поменять обработчика событий:

    todo.addEventListener('click', function(ev) {

    var t = ev.target;

    if (t.tagName === 'LI') {

    if (t.classList.contains('done')) {

    t. parentNode.removeChild(t);

    } else {

    t. classList.add('done');

    }

    };

    ev.preventDefault();

    }, false);

    Рисунок 5.2. Список дел с «галочками» и иконкой удаления на втором этапе

    Если элементу, на который мы кликаем, не присвоен класс done, он добавляется. Если же класс есть, мы удаляем элемент.

    Это поддерживает функциональность, а также дает нам дополнительный класс для применения в нашей CSS. Мы можем использовать его для добавления подсказки об удалении (“x”), которая появляется, когда вы наводите курсор над завершенным элементом:

    #todolist li: hover: after,

    #todolist li.done: after {

    content: ' ';

    color: #060;

    position: absolute;

    right: 5px;

    }

    #todolist li.done: hover: after {

    content: 'x';

    font-weight: bold;

    color: #c00;

    position: absolute;

    right: 5px;

    }

     

    Формы с проверкой корректного заполнения полей

    Как вы помните, прежде чем создать новый перечень элементов, мы проверяем содержимое поля. Прямо сейчас это сделано на JavaScript. Но если мы храним верность среде, в которой творим, то можем обойтись и без этого. Добавление атрибута required в HTML гарантирует, что браузер проверит содержимое поля, прежде чем отправит форму:

      placeholder="new item" required>

      Если пользователь попытается отправить форму, не введя информацию, обработчик этого события точно не останется без работы. Это означает, что мы можем сократить код JavaScript очередной строкой:

      form.addEventListener('submit', function(ev) {

      todo.innerHTML += '

    • ' + field.value + '
    • ';

      field.value = '';

      field.focus();

      ev.preventDefault();

      }, false);

      Рисунок 5.3. Firefox демонстрирует ошибку в заполнении поля и подсвечивает поле

      Незаполненные поля автоматически помечаются браузером – что-то такое мы вынуждены были делать в прошлом (см. рис. 5.3). Если браузер не поддерживает атрибут required, форма будет отправлена. Вот что происходит, когда пользователь (или во многих случаях непрофессионал) исключает JavaScript. Тестирование в JavaScript – это удобное средство, но не мера защиты. В любом случае вам необходимо делать проверки и на сервере.

       

      Сохранение состояния списка

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

      Session и localStorage – это, по сути, не HTML5, а скорее, собственные стандарты. Для хранения большого объема данных на стороне клиента существуют базы данных IndexDB и WebSQL. Однако локальное хранилище необычайно удобно в применении и более чем достаточно для наших нужд.

      Для того чтобы сохранить состояние перечня и загружать его, когда пользователь возвращается на страницу, все, что нам нужно сделать, – это написать две функции, storestate() и retrievestate():

      function storestate() {

      localStorage.todolist = todo.innerHTML;

      };

      function retrievestate() {

      if (localStorage.todolist) {

      todo.innerHTML = localStorage.todolist;

      }

      };

      Потом мы должны вызывать эти функции всякий раз, когда меняется перечень:

      form.addEventListener('submit', function(ev) {

      todo.innerHTML += '

    • ' + field.value + '
    • ';

      field.value = '';

      field.focus();

      storestate();

      ev.preventDefault();

      }, false);

      todo.addEventListener('click', function(ev) {

      var t = ev.target;

      if (t.tagName === 'LI') {

      if (t.classList.contains('done')) {

      t. parentNode.removeChild(t);

      } else {

      t. classList.add('done');

      }

      storestate();

      };

      ev.preventDefault();

      }, false);

      Мы извлекаем данные при повторной загрузке страницы:

      document.addEventListener('DOMContentLoaded', retrievestate, false);

      Вот оно! Кэширование всех интерфейсов в локальном хранилище может показаться грязным приемом, но на самом деле в этом нет ничего сомнительного. Так как HTML не слишком тяжеловесен, и у нас есть 5 MB памяти в браузерах, этот способ – простое решение распространенной проблемы.

       

      Пример 2: Анимированные элементы страницы с использованием CSS3

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

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

      У нас есть несколько заголовков, изображений и поясняющих текстов на очень длинной странице – и это все. Код HTML очень прост: навигационное меню указывает на якоря в документе.

      Cafe Vintage

      88 Mountgrove Road, London N5 2LT, England

      […]

      […]

      […]

      […]

      © 2012 Cafe Vintage and Chris Heilmann

      Это HTML старой школы, с несколькими новыми элементами, представленными Беном Шварцом в третьем разделе. Он работает во всех браузерах, а между навигацией и основным контентом существует логическая связь: ID (идентификаторы).

      Когда доступен JavaScript, страница показывает один раздел за один раз. Также существует анимация для перемещения между разделами: последняя страница движется вверх, а новая опускается вниз. А описание перемещается справа налево (см. рис. 5.5–5.7.).

      Чтобы это случилось, все, что нам требуется, это добавить и удалить несколько классов. Остальное происходит в CSS. Вот логика, которой мы придерживаемся:

      • Применяем класс с названием js к «телу» документа и прячем все элементы article в CSS по селектору .jsarticle {…} (в этом случае позиционируем их абсолютно и передвигаем их в экране).

      • К статье, выбранной пользователем, добавляем класс с названием current, который подменяется в CSS на .jsarticle.current {…}.

      • Для того чтобы показать пользователю, где он находится, добавляем класс с названием current к меню ссылки, соответствующей текущей видимой статье.

      Рисунок 5.4. Сайт простой кафешки с названием «Винтаж», где все разделы показаны один за другим

      Сначала мы добавим класс с названием js к «телу» документа. Это позволит нам определить стили для версий с и без JavaScript. Потом мы возьмем элементы, которые нам нужны. В этом случае нам нужны первый элемент article и первая ссылка, потому что они будут доступны первыми по умолчанию.

      Рисунки 5.5–5.7. Если JavaScript доступен, статьи на странице «оживают», когда пользователь нажимает на элемент навигации

      document.body.classList.add('js');

      var nav = document.querySelector('nav'),

      article = document.querySelector('article'),

      link = document.querySelector('nav a');

      Мы установим их классы на current:

      link.classList.add('current');

      article.classList.add('current');

      Остальные функции – это делегирование событий:

      nav.addEventListener('click', function (ev) {

      var t = ev.target;

      if (t.tagName === 'A') {

      article.classList.remove('current');

      link.classList.remove('current');

      article = document.querySelector(t.getAttribute('href'));

      link = t;

      article.classList.add('current');

      link.classList.add('current');

      }

      }, false);

      Теперь мы назначим обработчик нажатия в навигационном меню, проверим, что на ссылку щелкнули и удалим класс current из ссылки и из статьи, которая демонстрируется. Потом мы доходим до новой статьи, которую нужно показать, обращаясь к ней через атрибут href – ссылки, на которую щелкаем – и затем назначаем класс current новым элементам.

      Это почти все, что нам нужно сделать. Другой случай, который нам нужно учесть, это когда пользователь попадает на сайт через ссылку, содержащую хеш (когда ссылка указывает на статью, которая показывается не по умолчанию); в этом случае нам нужно показать правильную статью. Для этого мы можем использовать два селектора:

      if (document.location.hash) {

      var cleanhash = document.location.hash.replace(/^#/, '');

      article = document.querySelector(document.location.hash);

      link = document.querySelector('nav a[href$=' + cleanhash + ']'

      );

      }

      CSS-селектор проверяет, заканчивается ли ссылка тем, что мы передаем. Анимация сделана в CSS с использованием перемещений:

      section {

      overflow: hidden;

      min-height: 340px;

      position: relative;

      }

      article {

      position: relative;

      height: 350px;

      }

      body.js article {

      width: 700px;

      position: absolute;

      top: -700px;

      – webkit-transition: 0.8s;

      – moz-transition: 0.8s;

      – ms-transition: 0.8s;

      – o-transition: 0.8s;

      transition: 0.8s;

      }

      body.js article.current {

      position: absolute;

      top: 0;

      }

      Здесь мы видоизменяем статьи. Мы просто говорим браузеру расположить их относительно в разделе, когда JavaScript недоступен, и разместить их абсолютно на 700 пикселей над верхней частью контейнера, когда JavaScript доступен. Так как в разделе применяется overflow: hidden, они никогда не показываются.

      Если статья текущая, значение top изменяется на 0, и статья перемещается сверху вниз.

      Параграфы действуют по тому же принципу:

      article p {

      position: absolute;

      left: 320px;

      width: 370px;

      }

      js article p {

      left: 900px;

      opacity: 0;

      – webkit-transition: 1s ease 0.7s;

      – moz-transition: 1s ease 0.7s;

      – ms-transition: 1s ease 0.7s;

      – o-transition: 1s ease 0.7s;

      transition: 1s ease 0.7s;

      }

      js article.current p {

      position: absolute;

      left: 320px;

      width: 370px;

      opacity: 1;

      }

      В этом примере мы создали 1-секундное перемещение с задержкой в 0,7 секунды. Обратите внимание, что мы анимируем и положение слева и прозрачность одним махом. И нам не нужно ничего делать в JavaScript.

       

      Пример 3: Создание миниатюры изображения в браузере

      Лучшее, что есть в HTML5, – это элемент canvas (холст). Казалось бы, это просто элемент для рисования (а без JavaScript он и вовсе лишен смысла), но он же является мощным средством для чтения и управления изображениями и видеоданными. Вместе с FileReader и функцией DragandDrop («перетащи и оставь») в современных браузерах мы можем добиться реального успеха. Почему бы нам не создавать миниатюры в браузере?

      Рисунок 5.8. Перетаскивание с использованием canvas и FileReader

      Рисунок 5.9. Созданные миниатюры

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

      Thumbnails

      Давайте добавим разные стили. И, если браузер поддерживает их, мы заменим форму на сообщение, где попросим пользователя перетащить изображения в раздел:

      if (window.FileReader && (('draggable' in document.body) ||

      ('ondragstart' in document.body && 'ondrop' in document.body))) {

      var s = document.querySelector('section'),

      o = document.querySelector('output'),

      c = document.createElement('canvas'),

      cx = c.getContext('2d'),

      thumbsize = 100;

      c. width = c.height = thumbsize;

      document.body.classList.add('dragdrop');

      s. innerHTML = 'Drop images here';

      Здесь мы проверили, как браузер поддерживает функции FileReader и DragandDrop (досадно то, что Safari не поддерживает первое, а Opera – последнее). Если браузер их не поддерживает, то мы берем те элементы, которые нам нужны. Мы создаем элемент canvas и сохраняем его 2-Dcontext. Потом мы определяем размеры миниатюр и, соответственно, меняем размеры canvas. И, наконец, мы добавляем класс dragdrop для задания стиля, и заменяем форму на сообщение, приглашающее пользователей перетаскивать изображения.

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

      s. addEventListener('dragover', function (evt) {

      s. classList.add('active');

      evt.preventDefault();

      }, false);

      Мы удаляем класс, если перетаскивание отменяется:

      s. addEventListener('dragleave', function (evt) {

      s. classList.remove('active');

      evt.preventDefault();

      }, false);

      Все остальные функции действуют в drop-обработчике:

      s. addEventListener('drop', function (ev) {

      s. classList.remove('active');

      var files = ev.dataTransfer.files;

      if (files.length > 0) {

      var i = files.length;

      while (i-) {

      var file = files[i];

      if (file.type.indexOf('image')!== -1) {

      createthumb(file);

      }

      }

      }

      ev.preventDefault();

      }, false);

      Первое, что мы сделали здесь, это убрали класс active, потому что делали его с перетаскиванием. Событие перетаскивания дало нам объект dataTransfer, в котором содержатся «перетащенные» файлы. Далее мы проверяем, был ли хоть один из файлов перемещен и затем начинаем повторять это действие для каждого из них (Цикл while{} – причудливый способ сделать цикл for{}, но не помещая длину в кэш-память или не используя вторую переменную итератора). Еще проверяем, является ли текущий файл изображением, а потом отсылаем его к функции createthumb() (создания миниатюры). В конце мы предотвращаем поведение браузера по умолчанию при перетягивании изображения. И все, с этим мы разделались.

      function createthumb(file) {

      var reader = new FileReader();

      reader.readAsDataURL(file);

      reader.onload = function (ev) {

      var img = new Image();

      img.src = ev.target.result;

      img.onload = function() {

      cx.clearRect(0, 0, thumbsize, thumbsize);

      var thumbgeometry = resize(this.width, this.height,

      thumbsize, thumbsize);

      cx.drawImage(img, thumbgeometry.x, thumbgeometry.y,

      thumbgeometry.w, thumbgeometry.h);

      var thumb = new Image();

      thumb.src = c.toDataURL();

      o. appendChild(thumb);

      };

      };

      }

      Функция createthumb() инициирует новый FileReader и читает изображение как цепочку данных. Если считыватель загружен удачно, то мы создаем новое изображение в браузере и устанавливаем его атрибут src для результата транзакции FileReader.

      Когда изображение успешно загружено, мы очищаем элемент canvas методом clearRect(). Это обязательно. Иначе нам придется создавать миниатюры, которые добавляются друг к другу каждый раз, когда вызывается функция.

      Потом мы задаем размер миниатюры, который хотим получить от функции resize() и вызываем метод drawImage для элемента canvas. Этот метод использует пять параметров: изображение для получения информации о пикселях, координаты левого верхнего угла, откуда рисуется изображение, ширину и высоту. Потом мы создаем новое изображение, сохраняем пиксельный контент холста методом toDataURL() и добавляем новое изображение к выведенному элементу.

      function resize(imagewidth, imageheight, thumbwidth, thumbheight) {

      var w = 0, h = 0, x = 0, y = 0,

      widthratio = imagewidth / thumbwidth,

      heightratio = imageheight / thumbheight,

      maxratio = Math.max(widthratio, heightratio);

      if (maxratio > 1) {

      w = imagewidth / maxratio;

      h = imageheight / maxratio;

      } else {

      w = imagewidth;

      h = imageheight;

      }

      x = (thumbwidth – w) / 2;

      y = (thumbheight – h) / 2;

      return { w: w, h: h, x: x, y: y };

      };

      Функция resize – математический помощник в изменении размера изображения определенной ширины и высоты на меньшее, но с теми же пропорциями. Ничего сверхъестественного, простая практика. И вот так мы получаем миниатюры в нашем браузере.

       

      Заключение

       

      Надеюсь, мне удалось убедить вас в том, что сегодня много отличных фишек стали «родными» для браузеров. Конечно, пока не все из них поддерживают эти функции, но по крайней мере все производители браузеров вместе работают над стандартами, и сейчас не то время, когда между первыми браузерами шла война, а инновации творились наощупь. При смешении и подгонке различных веб-технологий (HTML, CSS, JavaScript) мы можем создавать совершенно потрясающие вещи всего в нескольких строках кода. Все, что нам нужно – это пользоваться теми возможностями, которую дают нам браузеры.

       

      Правильная технология работы

      Взаимодействие CSS перемещений, трансформаций и анимаций с JavaScript – мощный инструмент, и нам следует использовать его гораздо больше. Сейчас, похоже, идет битва между теми, кто везде применяет jQuery или JavaScript, и теми, кто работает исключительно с CSS. Это не помогает нашим пользователям, а нас тормозит с написанием лаконичных, эффективных решений. Хороший веб-разработчик почерпнет достоинства из всех технологий, а не будет приверженцем какой-то определенной. Много ума не надо, чтобы использовать одну технологию на все случаи жизни. А потом оставить продукт работать только в одном браузере или на одном устройстве.

      Если вам нужно, чтобы все необходимые свойства поддерживались в старых браузерах, используйте библиотеку, такую как jQuery. Вы также можете найти «заплаты» для старых браузеров в форме полифилов. А вообще, давайте уже прекратим пытаться заставить устаревшие технологии поддерживать то, чтомы создаем для новых. Никто не перестанет использовать продукт только из-за того, что браузер IE 6 не делает плавных перемещений из одного состояния в другое. А это и есть самое важное свойство хорошего веб-продукта.

       

      Большая игра

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

      Производители браузеров не добавляют каждое свойство, которое хотят видеть разработчики из-за того, что новая технология внедряется недостаточно широко. Мало пожаловаться на то, что что-то не работает.

      Производители браузеров хотят, чтобы эта технология использовалась по полной программе. И еще им нужно получать информацию от потребителей о ее действии. Если обратная связь заключается только в вопросе: «Почему вы не поддерживаете функцию X?» – вам всегда ответят: «Потому что ею никто не пользуется».

      Самый яркий пример тому – новые семантические элементы HTML5. Нам грех жаловаться на недостаточную поддержку алгоритма outline в браузерах, еслимы сами не используем правильные элементы, а потом сообщаем производителю, что работает, а что – нет. У каждого производителя есть механизм обратной связи. От нас как от разработчиков зависит, давать ли быстрее реальные ситуации для решения или ждать безупречного внедрения.

       

      Принимаем и применяем технологии будущего

      Нам нужно ухватить новую технологию и использовать ее, где только возможно. Отбросим мысли о том, что надо ждать, пока все браузеры станут поддерживать определенную технологию и из-за этого не применять ее. Если мы не опробуем новые технологии, внедренные рабочими группами WHATWG и W3C, мы ни к чему не придем.

      И все же мы не должны навязывать технологии людям. Сообщение типа «Обновите браузер X, чтобы увидеть этот контент» может расстроить пользователя, если у него нет права на загрузку нового браузера в свой компьютер или у него медленное соединение. Вместо того чтобы создавать ракетные ранцы, давайте построим эскалаторы. Они позволят людям удобно подняться на более высокие уровни, но при этом будут работать как обычная лестница, если вдруг пропадет электричество или сломается механизм.

       

      Об авторе

      * * *

      Кристиан Хейлманн (1975) родился в Швайнфурте, Бавария. Имеет диплом по немецкому и английскому языкам, истории и астрономии. Его девиз: «Если берешься за что-то, доводи до конца. Не хочешь – тогда брось!» Сейчас Кристиан живет в Северном Лондоне, куда стекаются люди из многих классных местечек. Он работает над тем, чтобы доводить технологии до людей, а людей до технологий. Когда он не занят этим, то переключается на фильмы. Его любимые цвета – синий и зеленый. Из-за того, что он редко бывает дома, его единственные питомцы – это множество резиновых уточек. Послание читателям от Кристиана: «Оставайтесь жаждущими и пытливыми. Новое всегда где-то рядом!»

       

      О рецензенте

      * * *

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

      Пол живет в квартире-студии в районе Мишин в Сан Франциско. Все свое свободное время он любит слушать электромузыку и ходить на фуршеты. Главное, чему он научился на протяжении своей карьеры, – это добавлять причуды во всю тяжелую работу. Личный совет читателям от Пола: «Публикуйте то, чему учитесь». Слова эксперта! Так что стоит попробовать.