Ненавязчивый JavaScript. Глава 1: Операция «Чистота» в действии

Текст ниже — перевод статьи "Unobtrusive Javascript", чей автор, Christian Heilmann, любезно дал мне согласие на перевод. Оригинальный адрес статьи www.onlinetools.org/articles/unobtrusivejavascript/chapter1.html.

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

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

HTML:

<table border="0" cellpadding="0" width="100%" cellspacing="5">
<tr>
 <td><font face="Arial" size="-2">Lorem Ipsum</font></td>
</tr>
</table>

превратился в

CSS:

td.content {font-family:Arial,sans-serif;font-size:12px;}
	
HTML:

<table cellpadding="0" style="width:100%;border:none" cellspacing="5">
<tr>
 <td class="content">Lorem Ipsum</td>
</tr>
</table>

И в конечном итоге:

CSS:

body {font-family:Arial,sans-serif;font-size:12px;}
p {padding:5px;}
	
HTML:

<p>Lorem Ipsum</p>

Javascript может и должно ожидать такое же видоизменение.

Javascript: храним отдельно

Первое правило клуба ненавязчивого Javascript это никому не говорить о клубе ненавязчивого Javascript. Нет, нет, оставайтесь с нами, первое правило это:

1. Никогда, ни при каких обстоятельствах не добавляйте Javascript прямо в документ

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

<script type="text/javascript" src="scripts.js"></script>

Это все что нам когда-либо понадобится, никакого больше встроенного Javascript. Смыть шампунь и повторить.

2. Javascript — это расширение, а не основная функциональность

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

Это не означает, что мы совсем не можем использовать Javascript, это только значит, что мы можем добавлять его лишь как дополнительную возможность.

HTML:

<form action="index.php" onsubmit="return checkform(this)">
 <p><label for="login">Login:</label>
 <input type="text" name="login" id="login" /></p>
 <p><label for="pw">Password:</label>

 <input type="password" name="pw" id="pw" /></p>
 <p><input type="submit" value="send" /></p>
</form>
	
Javascript:

function checkform(f)
{
 var error='';
 error+=f.login.value==''?'\nlogin':'';
 error+=f.pw.value==''?'\npassword':'';
 if (error!='')
 {
  alert('Please enter the following:'+error);
 }
 return error=='';
}

Код совершенно правильный и ограничит пользователей от попыток отправить пустую форму.

HTML:

<form action="index.php">
 <p><label for="login">Login:</label>
 <input type="text" name="login" id="login" /></p>
 <p><label for="pw">Password:</label>

 <input type="password" name="pw" id="pw" /></p>
 <p><input type="button" onclick="checkform()" value="send" /></p>
</form>

Javascript:

function checkform()
{
 var f=document.forms[0];
 var error='';
 error+=f.login.value==''?'\nlogin':'';
 error+=f.pw.value==''?'\npassword':'';
 if (error!='')
 {
  alert('Please enter the following:'+error);
 } else {
  f.submit();
 }
}

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

Давайте повторим: Javascript не надежен, не будем полагаться на него!

3. Проверяйте доступность объекта прежде чем использовать его

Множество ошибок Javascript происходит просто потому, что программист был слишком ленив, чтобы проверить, доступен или нет данный объект или метод.

Javascript:

function color(o,col)
{
 o.style.background=col;
}

может окончиться ошибкой Javascript, если объект о не доступен.

Javascript:
function color(o,col)
{
 if(o)
 {
  o.style.background=col;
 }
}

Данный пример работает всегда.

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

4. Не создавайте Javascript на браузеро-зависимых диалектах

Если нет действительно весомой причины, мы никогда не должны использовать специфичные для браузера расширения веб-стандартов. Времена проверки document.layers (Netscape 4.x) или document.all (Internet Explorer < 5) прошли. Все современные браузеры поддерживают свойство DOM document.getElementsById и именно это мы используем для проверки.

Javascript:

function doDOMstuff()
{
 if(document.getElementById && document.createTextNode)
 {
  [...]
 }
}

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

5. Не похищайте чужие переменные скрипта

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

Javascript:

var i=42; // глобальная переменная
function dothings()
{
 for (i=0;i<200;i++) // i изменяется
 {
  // какой-то код
 }
}
function dothingsright()
{
 var i; // определяем i локально
 for (i=0;i<200;i++)
 {
  // какой-то код
 }
}
alert(i); // = 42
dothingsright()
alert(i) // = 42 (обусловлено локальным определением)
dothings()
alert(i) // = 200, ой-ой!

6. Сохраняйте функциональность независимой от наличия мыши

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

Самая большая проблема с независимостью от наличия мыши — это поля формы, чьи значения мы проверяем, или которые запускают Обработчики события onchange или onblur. Не используйте их, это так просто (as simple as that).

HTML:

<form>
<p>
<label for="go2">Go to:</label>
<select name="go2" id="go2" onchange="send(this)">
 <option value="index.html">Home</option>
 <option value="chapter1.html">Operation Cleanout</option>

 <option value="chapter2.html">Reaching things</option>
</select>
</p>
</form>
	
Javascript:

function send(f)
{
 var chosen;
 chosen=f.options[f.selectedIndex].value;
 self.location=chosen;
}

Проверьте этот пример недоступного выпадающего списка

Видимо, главная проблема в том, что если вы переходите к элементу, используя клавиатуру, и нажимаете стрелку вниз, то для того чтобы выбрать что-либо, вы никогда не пройдете мимо первого пункта. Ведь send() запускается каждый раз, когда вы переходите с первого на второй пункт.

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

HTML:

<form action="send.php" method="post" onsubmit="return send(this)">
<p>
<label for="go2">Go to:</label>

<select name="go2" id="go2">
 <option value="index.html">Home</option>
 <option value="chapter1.html">Operation Cleanout</option>
 <option value="chapter2.html">Reaching things</option>

</select>
<input type="submit" value="go" />
</p>
</form>
	
Javascript:

function send(f)
{
 var chosen;
 chosen=f.go2.options[f.go2.selectedIndex].value;
 self.location=chosen;
 return false;
}    
	
PHP:
<?PHP if(isset($_POST['go2?])){
 header('Location:'.$_POST['go2?]);
}?>

Проверить этот пример доступного выпадающего списка

Это работает сравнительно хорошо и экономит нам один запрос сервера к перенаправляющему скрипту send.php, который отрабатывает, только когда не доступен Javascript.

Как насчет onkeypress?

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

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

  • Используйте onmousedown вместе с onkeydown.
  • Используйте onmouseup вместе с onkeyup
  • Используйте onclick вместе с onkeypress

Это звучит великолепно в теории, но в реальных жизненных ситуациях обрабочик onkeypress плохо поддерживается различными браузерами. Пользователям, зависящим от просмотра с клавиатуры, для имитации клика обычно дается либо клавиша Enter, либо пробел, либо они обе. Используя onkeypress, мы можем украсть другую клавиатурную функциональность нужную пользователю. Примеры опережающий ввод в Мозилле, дополнительные горячие клавиши в Опере (типа A для перехода к следующей ссылке) или клавиатурные средства управления в JAWS.

Ссылки:

  1. The World Wide Web Consortium: www.w3.org
  2. The w3c Document Object Model: www.w3.org/DOM/
  3. Web Content Accessibility Guidelines: www.w3.org/TR/WCAG10-HTML-TECHS/#directly-accessible-scripts
  4. Mozilla's type-ahead: www.mozilla.org/projects/ui/accessibility/typeaheadfind.html
  5. Opera's keyboard controls: www.opera.com/features/keyboard/complete/
  6. JAWS keyboard controls: www.webaim.org/techniques/jaws/keyboard-shortcuts

Читать дальше