Статьи

Улучшение структурной разметки с помощью JavaScript

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

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

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

Цитаты цитат

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

Единственная проблема с атрибутом cite заключается в том, что браузеры полностью его игнорируют. Пуристы разметки могут ценить это, но, с чисто практической точки зрения, ничего не получится с его использованием, кроме чувства самодовольного удовлетворения от использования правильной разметки. Вот где приходит Javascript. Используя DOM, можно добавить ссылку на источник цитаты внизу любой цитаты, которая имеет атрибут cite. Вот код для функции, которая делает именно это:

function extractBlockquoteCitations() { 
  var quotes = document.getElementsByTagName('blockquote'); 
  for (var i = 0; i < quotes.length; i++) { 
    var cite = quotes[i].getAttribute('cite'); 
    if (cite != '') { 
      var a = document.createElement('a'); 
      a.setAttribute('href', cite); 
      a.setAttribute('title', cite); 
      a.appendChild(document.createTextNode('Source')); 
      var p = document.createElement('p'); 
      p.className = 'blockquotesource'; 
      p.appendChild(a); 
      quotes[i].appendChild(p); 
    } 
  } 
}

Давайте внимательно посмотрим на тело функции.

 var quotes = document.getElementsByTagName('blockquote');

В этой строке используется метод DOM getElementsByTagName

 for (var i = 0; i < quotes.length; i++) { 
  var cite = quotes[i].getAttribute('cite'); 
  if (cite != '') {

Теперь мы перебираем собранные узлы цитат. Каждый раз мы используем метод getAttribute Если атрибут cite был установлен, мы переходим к забавной части: создаем ссылку «Источник» внизу цитаты.

     var a = document.createElement('a'); 
    a.setAttribute('href', cite); 
    a.setAttribute('title', cite);

Когда мы хотим динамически добавлять новые элементы HTML на страницу, используя DOM, правильный способ сделать это — создать элементы программно, используя метод createElement. Вышеприведенные строки создают новый элемент ‘a’ и присваивают ему атрибуты hreftitle
a.appendChild(document.createTextNode('Source'));

Мы хотим, чтобы элемент ссылки содержал некоторый текст, по которому пользователь может щелкнуть, чтобы активировать ссылку. Необработанные текстовые узлы создаются с использованием метода createTextNode DOM рассматривает HTML-элементы как формирование дерева, поэтому для добавления текста в нашу вновь созданную ссылку нам нужно вызвать его метод appendChild

     var p = document.createElement('p'); 
    p.className = 'blockquotesource'; 
    p.appendChild(a);

Чтобы позволить нам гибко стилизовать новую ссылку с помощью CSS, мы можем заключить ее в элемент абзаца. Приведенный выше код создает такой элемент, устанавливает его класс на « blockquotesourceappendChild На этом этапе созданный нами новый фрагмент документа эквивалентен следующему HTML:

 <p class="blockquotesource"> 
<a href="[cite URL]" title="[cite URL]">Source</a> 
</p>

На данный момент наш фрагмент остается невидимым, потому что, хотя мы создали его в памяти, мы еще не прикрепили его к нашему документу. Последняя строка в функции делает именно это:

     quotes[i].appendChild(p);

quotes[i] appendChild

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

 <body onload="extractBlockquoteCitations();">

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

 window.onload = extractBlockquoteCitations;
//

Обратите внимание, что мы предоставили имя функции, но в конце пренебрегли () Javascript поддерживает стиль функционального программирования, что означает, что функции могут обрабатываться как любой другой объект данных и передаваться как аргументы, храниться в структурах данных или даже возвращаться из других функций. Я поговорю об этой теме подробнее в следующей статье, но в результате этого назначение функции для window.onload

Это решение, однако, также имеет недостаток. Если вы хотите использовать на данной странице несколько сценариев, которые выполняются после завершения загрузки страницы, последний сценарий, который зарегистрирует себя в window.onload Что действительно нужно, так это способ присоединить нашу функцию к обработчику загрузки объекта окна без перезаписи того, что уже есть. К сожалению, Internet Explorer и другие браузеры отличаются тем, как должен обрабатываться такой тип динамического вложения; К счастью, Скотт Эндрю выпустил функцию, которая обрабатывает эти различия для вас. Вот функция:

 function addEvent(obj, evType, fn){ 
  if (obj.addEventListener){ 
    obj.addEventListener(evType, fn, false); 
    return true; 
  } else if (obj.attachEvent){ 
    var r = obj.attachEvent("on"+evType, fn); 
    return r; 
  } else { 
    return false; 
  } 
}

А вот код для добавления нашей функции blockquotes к событию load объекта window:

 addEvent(window, 'load', extractBlockquoteCitations);

Последний шаг — стилизация наших цитат с использованием CSS. Вот относительно простой фрагмент CSS для обработки цитат:

 blockquote { 
  border-left: 0.25em solid navy;  
  padding: 0 0.5em;  
  margin: 0.5em 1.5em 0.5em 2.5em;  
} 
blockquote p.blockquotesource { 
  font-weight: bold; 
  font-size: 0.8em; 
  text-align: right; 
  padding-top: 0.5em; 
}

Готовый продукт можно посмотреть здесь .

Переключение панели

Теперь давайте рассмотрим более продвинутый динамический эффект — переключатель панели. Задача здесь состоит в том, чтобы на странице было несколько панелей (размеченных с использованием элементов div), из которых одновременно видна только одна. Набор ссылок, которые остаются постоянно видимыми, можно использовать для выбора того, какая из панелей отображается в данный момент. Это может быть полезно для создания чего-то вроде интерфейса с вкладками для просмотра ряда связанных экранов без необходимости обновления страницы каждый раз, когда выбирается одна из вкладок.

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

Вот самая простая разметка, которая может сработать:

 <a href="#p1">Panel 1</a> | <a href="#p2">Panel 2</a>  

<div id="p1">This is Panel 1</div>  
<div id="p2">This is Panel 2</div>

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

 <a href="#p1" class="toggle">Panel 1</a> |   
<a href="#p2" class="toggle">Panel 2</a>
//

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

Полный сценарий можно посмотреть здесь . Далее следует описание того, как работает код.

 var et_toggleElements = [];

Эта первая строка создает глобальный пустой массив, который будет содержать ссылки на элементы панели на странице. Поскольку у этого скрипта есть глобальная переменная и ряд функций, мы будем ставить перед каждой префикс « et_

 /* Initialisation */  
function et_init() {  
  var i, link, id, target, first;  
  first = true;  
  for (i = 0; (link = document.links[i]); i++) {

До сих пор мы инициализировали некоторые переменные, установили для первого флага значение true и начали перебирать все ссылки в документе. Объявление переменных с помощью var важно, потому что оно гарантирует, что переменные являются локальными для функции. Без этого шага они были бы глобально доступны и могли бы мешать другим сценариям.

     if (/btoggleb/.exec(link.className)) {

Это условно проверяет, что текущая ссылка имеет «переключатель» в своем классе. Мы используем регулярное выражение, а не просто проверяем, если link.className == 'toggle' /btoggleb/ части b

       id = link.href.split('#')[1]; Если ссылка имеет переключатель в своем списке классов, мы предполагаем, что целью ссылки является фрагмент URL.
  link.href.split('#')href[1]
       target = document.getElementById(id);  
      et_toggleElements[et_toggleElements.length] = target;

Здесь мы делаем другое предположение — что элемент, указанный ссылкой, действительно существует. Мы получаем этот элемент с помощью метода getElementById() Это работает, потому что массивы индексируются с 0, но длина массива начинается с 1; следовательно, длина массива также является индексом следующего пустого слота в массиве.

       if (first) {  
        first = false;  
      } else {  
        target.style.display = 'none';  
      }

Вот где первый флаг, который мы определили ранее, становится полезным. Мы хотим, чтобы первая панель на сайте оставалась видимой, а остальные скрыты с помощью Javascript-эквивалента « display: none Флаг позволяет нам сделать это.

       link.onclick = et_toggle;  
    }  
  }  
}

Наконец, мы назначаем функцию et_toggleonclick Следующим шагом является определение этой функции.

 function et_toggle(e) {   
  if (typeof e == 'undefined') {   
    var e = window.event;   
  }   
  var source;   
  if (typeof e.target != 'undefined') {   
    source = e.target;   
  } else if (typeof e.srcElement != 'undefined') {   
    source = e.srcElement;   
  } else {   
    return true;   
  }

Этот первый блок кода существует, чтобы восполнить еще одно различие между тем, как Internet Explorer и другие браузеры обрабатывают события. Нам нужно знать, какая ссылка была активирована при вызове функции, поскольку это позволит нам определить панель, которая должна отображаться. Приведенный выше код идентифицирует элемент, из которого произошло событие, и помещает его в переменную источника, используя код, адаптированный из превосходного объяснения проблемы Питера-Пола Коха в QuirksMode :

   if (source.nodeType == 3) {   
    source = targ.parentNode;   
  }

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

   var id = source.href.split('#')[1];
//

Мы снова извлекаем идентификатор из ссылки, используя метод split.

var elem;
for (var i = 0; (elem = et_toggleElements[i]); i++) {
if (elem.id != id) {
elem.style.display = 'none';
} else {
elem.style.display = 'block';
}
}

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

   return false;   
}

Возвращая значение false, мы предотвращаем фактическое отслеживание ссылки при ее активации, что может привести к нежелательному переходу вниз по странице, просматриваемой в браузере.

Последний шаг — регистрация функции et_init

 addEvent(window, 'load', et_init);

Вы можете увидеть готовый код в действии здесь .

Вывод

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

Дальнейшее чтение

Есть много других превосходных примеров эффектов Javascript, основанных на структурной разметке. Вот лишь некоторые из них, которые стоит проверить: