Мастер-классы по Javascript Екатеринбург Ростов-на-Дону Москва Узнать больше...
Содержание (скрыть) Содержание (показать)

Шаблонизация

  1. Шаблонка «на строках»
    1. Синтаксис шаблона
    2. Шаблонка изнутри
  2. Шаблонка «на узлах»
  3. Использование шаблонок
    1. Точки прикрепления
    2. Автоприкрепление
  4. Итого

Шаблон — это заготовка (обычно строка HTML), которая путём подстановки значений (текст сообщения, цена и т.п.) превращается в сообщение/товар и т.п.

Шаблон позволяет вынести текст из скрипта и изложить его в более поддерживаемом виде.

Шаблонка «на строках»

Шаблонных систем для JavaScript много. Для начала рассмотрим одну из них, предложенную Джоном Ресигом и модифицированную в Underscore.js.

Шаблон состоит из текста, который, как правило, добавляют к HTML в блок SCRIPT с нестандартным type, например text/html:

<script *!*type="text/html"*/!* id="list-template">
<ul>
  <% for (var i=1; i<=count; i++) { %>
    <li><%=i%></li>
  <% } %>
</ul>
</script>

Такие скрипты не выполняются, их содержимое игнорируется браузером, но доступно при помощи innerHTML. Шаблонная система читает его, а затем, получив данные, генерирует строку HTML.

Выглядит это, например, так: (запустите)

<script *!*type="text/html"*/!* id="list-template">
<ul>
  <% for (var i=1; i<=count; i++) { %>
    <li><%=i%></li>
  <% } %>
</ul>
</script>

<!-- файл с кодом шаблонки -->
<script src="/files/tutorial/js/tmpl.js"></script>

<script>
*!*  
  // получить текст шаблона
  var template = document.getElementById('list-template').innerHTML;
  var compiled = tmpl(template); // скомпилировать

  // получить результат, передав объект с данными {count:5}
  var result = compiled( {count:5} );

  document.write( result );
*/!*
</script>

Синтаксис шаблона

В шаблоне используются специальные разделители:

<% code %>
Код между разделителями <% ... %> будет выполнен «как есть»
<%= var %>
Переменная var будет вставлена на это место. Допустимо и выражение: <%= (x+1)*f(5) %>.
<%- var_esc %>
Переменная var_esc будет вставлена с экранированием. Например, если var_esc = "<script>", то в шаблон попадёт <script>.

Почему использован именно нестандартный SCRIPT, а не DIV с display:none? Подумайте перед тем, как идти дальше..

Раскрыть ответ

Причины две.

  1. Даже невидимый DIV будет полностью обработан, в DOM появятся лишние узлы.
  2. DIV обязан содержать корректный HTML. Достаточно оставить незакрытым тег — и могут быть проблемы. А в скрипте может быть почти что угодно, его содержимое полностью игнорируется.

Шаблонка изнутри

Запуск tmpl(строка) производит поиск и замену в строке шаблона, а затем создаёт из него JavaScript-функцию через new Function(obj, 'ТЕКСТ').

Текст функции создаётся динамически из строки и выглядит примерно так:

with(obj) { // obj - данные
  var __p = []; 

  // для обычного текста:
  __p.push("<ul>");

  // код в <%...%> вставляется "как есть"
  for (var i=1; i<=count; i++) {
    // вставляется текст '<li>', затем переменная <%= i %> "как есть", 
    __p.push('<li>', i, </li>');  // затем опять текст '</li>'
  }

  __p.push("</ul>");
}
return __p.join('');
..То есть, делается массив __p, который будет содержать результат. В него добавляются (push) фрагменты HTML. Код копируется в функцию «как есть». Доступ к переменным шаблона обеспечивает with(obj) { .. }.

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

Кеширование и серверная компиляция

Никто не мешает компилировать строку шаблона на сервере.

Используя серверный JavaScript (Node.JS) можно собрать все шаблоны проекта, скомпилировать их и сохранить полученные функции в .js-файле, который уже подключать к HTML.

Есть данные:

var users = [
  {name: "Вася", age: 10},
  {name: "Петя", age: 15},
  {name: "Женя", age: 20},
  {name: "Маша", age: 25},
  {name: "Даша", age: 30},
];

Выведите их в виде таблицы TABLE/TR/TD при помощи шаблонки.

Исходный документ с шаблонкой и данными: tutorial/browser/dom/template-grid-src/index.html

Решение

Шаблонка «на узлах»

Альтернативный подход к шаблонизации — это сделать DOM-узел, а потом клонировать его с потомками вызовом elem.cloneNode(true).

Например:

<!-- *!*шаблон*/!* -->
<div id="user-template" style="display:none">
  <div>
    Имя: <span class="user-name">Имя</span>
  </div>
  
  <div>
    Возраст: <span class="user-age">Имя</span>
  </div>
</div>

<script>
function makeUserNode(data) {
  // /*!*клонировать и записать данные*/!*
  var userTmpl = document.getElementById('user-template');
  var userNode = userTmpl.cloneNode(true);
 
  userNode.id = 'user-' + data.id;
  userNode.querySelector('.user-name').innerHTML = data.name;
  userNode.querySelector('.user-age').innerHTML = data.age;
  
  userNode.style.display = '';
  return userNode;
}

// *!*создать два узла из шаблона*/!*
var userNode = makeUserNode({ id: 1, name: 'Паша', age: 25 });
document.body.appendChild(userNode);

var userNode = makeUserNode({ id: 2, name: 'Василий', age: 29 });
document.body.appendChild(userNode);
</script>

Какой тип шаблонки лучше?

Существенного различия в скорости, как правило, нет. Если интересно — сравнить скорость генерации шаблона user-template из примера выше вы можете в песочнице: tutorial/tmpl/bench.html.

Поэтому используется обычно то, что удобнее.

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

Использование шаблонок

Разберём ситуацию — JavaScript должен показать красивое диалоговое окно.

Чтобы описать его HTML-структуру, будем использовать шаблон:

<script type="text/html" id="dialog-tmpl">
  <div class="dialog-window">
    <div class="title">
      <%=title%>
    </div>
    <div class="content">
      <%=content%>
    </div>

    <button class="ok">OK</button>
  </div>
</script>

При использовании шаблонки, мы получим из этого текста строку с подставленными значениями <%=title%> и <%=content%>. Этого недостаточно, чтобы окно работало: нужны обработчики событий.

Обработчики событий обычно ставятся на внешний элемент и работают через делегирование. Благодаря этому не нужно в сгенерированном HTML искать button с class="OK".

Точки прикрепления

Тем не менее, JS-коду бывают нужны создать ссылки на подэлементы, с которыми виджет планирует работать.

Они, как правило, записываются в переменные виджета при создании DOM. Иногда такие подэлементы называют «точками прикрепления», т.к. именно в этих местах JS-код связан с шаблоном.

Самое простое — при генерации DOM находить их по классу и прикреплять.

Пример ниже создаёт диалог и записывает элемент («точку прикрепления») с классом content в переменную, чтобы позже использовать её в методе setContent(content):

function Dialog() {

  function init() {
    // *!*создать окно dialog*/!* 
    var template= tmpl(document.getElementById('dialog-tmpl'));

    var html = template({ title: "Диалог", content: "Нажмите кнопку" });

    this.element = document.createElement('div');
    this.element.innerHtml = html; 

    // *!*записать в переменную точку прикрепления */!*
    this.contentElem = this.element.querySelector('.content');
  }

*!*
  // использовать точку прикрепления в методе
  function setContent(content) {
    this.contentElem.innerHTML = content;
  }
*/!*

}

Здесь подразумевается, что доступен querySelector. Если у нас IE<8, то используем библиотеки для выборки (jQuery?) — на маленьком шаблоне они работают быстро, либо альтернативные способы навигации по DOM.

Автоприкрепление

В качестве универсального решения можно пометить все узлы, требующие прикрепления, атрибутом вида data-attach="property". Значение атрибута — имя свойство, в котором нужно сохранить ссылку на узел.

При генерации DOM из такого шаблона достаточно один раз найти узлы с таким атрибутом и расписать их по свойствам.

Пример шаблона:

<script type="text/html" id="dialog-tmpl">
<div class="dialog-window">
    <div class="title">
      <%=title%>
    </div>
    <div class="content" *!*data-attach="content"*/!*>
      <%=content%>
    </div>

    <button class="ok">OK</button>
  </div>
</script>

Код, который генерирует DOM и сохраняет .content в переменную:

function Dialog() {

  var attachPoints = {};

  function init() {
    // *!*создать окно dialog*/!* 
    var template = tmpl(document.getElementById('dialog-tmpl'));

    var html = template({ title: "Диалог", content: "Нажмите кнопку" });

    this.element = document.createElement('div');
    this.element.innerHtml = html; 

    attachToTemplate();
  }

*!*
  // прочитать точки прикрепления и сохранить в attachPoints    
  function attachToTemplate() {
    var markedElems = this.element.querySelector('[data-attach]');
    for (var i=0; i<markedElems.length; i++) {
      var el = markedElems[i]
      attachPoints[el.getAttribute('data-attach')] = el;
    }
  }
}
*/!*


  // использовать точку прикрепления в методе
  function setContent(content) {
    *!*this.attachPoints.content.innerHTML = content;*/!*
  }

}

Итого

Типы шаблонок:

  • Шаблонки бывают «на строках» и «на узлах». Первые работают преобразованием строки в функцию, вторые — клонируют готовый узел.
  • Используем тот тип шаблонки, который удобнее. Строки — более универсальны.

Применение шаблонок:

  • Обработчики ставим на внешний элемент, используя делегирование.
  • Точки прикрепления можно искать либо явно из JS-кода, разметив их классами, либо автоматически, выбрав для этого специальный атрибут, например data-attach.

Функция tmpl, использованная в примерах: tmpl.js.

См. также:

Комментарии

  1. Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  2. Если ваш комментарий касается задачи -- откройте её в отдельном окне и напишите там.
  3. Комментарии без смысла, с рекламой или не о статье вообще - удаляются.
Наверх

Содержание

Реклама

Нашли опечатку?

Нашли опечатку на сайте? Что-то кажется странным?
Выделите соответствующий текст и нажмите Ctrl+Enter!

Последние Комментарии

Помоги другим!

Помоги другим узнать о хорошей статье!