HTML5-компоненты: Реализация стандартных компонентов

Использование API-интерфейсов спецификации HTML5 и полифилов с открытым исходным кодом

В этом цикле, статей специалист по HTML5 Дэвид Джири демонстрирует реализацию HTML5-компонентов. В этой, заключительной статье демонстрируется, как осуществить реализацию компонентов с использованием разрабатываемых спецификаций на HTML5-компоненты и при содействии со стороны проектов Polymer и Mozilla X-Tags.

Дэвид Джири, президент, Clarity Training, Inc.

David GearyАвтор, лектор и консультант Дэвид Джири является президентом компании Clarity Training, Inc., Он обучает разработчиков создавать Web-приложения с использованием JSF и Google Web Toolkit (GWT). Он участвовал в экспертных группах JSTL 1.0 и JSF 1.0/2.0, был соавтором сертификационного экзамена Web Developer Certification Exam компании Sun, а также принимал участие в проектах с открытым кодом, в том числе Apache Struts и Apache Shale. Книга Дэвида Graphic Java Swing стала одной из самых продаваемых книг о Java, а Core JSF (в соавторстве с Кэем Хорстманом) - одна из самых продаваемых книг о JSF. Дэвид регулярно выступает на конференциях и встречах пользовательских групп. Он является регулярным участником конференций NFJS с 2003 года, проводил курсы в университете Java и дважды удостаивался звания JavaOne rock star.



22.10.2013

Об этом цикле статей

Модель стандартных компонентов пользовательского интерфейса для платформы HTML5 все еще находится в стадии развития. В этом цикле статей Дэвид Джири, высококвалифицированный специалист по HTML5, показывает, как создавать собственные специальные (ad-hoc) HTML5-компоненты с помощью существующей технологии — и как задействовать спецификации, которые пока еще находятся в процессе определения подлинной системы HTML5-компонентов.

В первых двух статьях этого цикла из трех частей вы познакомились с процедурой реализации специального HTML5-компонента типа ползунок (slider). Этот компонент обладает весьма изощренной функциональностью; например, он пользуется CSS3-переходами для плавного анимирования кнопки ползунка, когда пользователь нажимает на направляющую этого ползунка. Однако (и это наглядно показано в первой статье серии) с точки зрения разработчика использование этого компонента в приложении является не столь простым, как использование встроенных HTML-элементов.

Для использования специального (ad-hoc) компонента типа ползунок необходимо выполнить следующие действия.

  1. Добавить пустой элемент DIV в свой HTML-текст.
  2. Создать новый объект COREHTML5.Slider.
  3. Присоединить ползунок, созданный на шаге 2, к пустому элементу DIV, созданному на шаге 1.
  4. Реализовать обработчиков событий для связывания органов управления с ползунком.

Помимо трудностей в использовании специального ползунка он уязвим для неправильного обращения (намеренного или случайного). Элементы такого ползунка доступны кому угодно, поскольку этот ползунок — как и почти все остальные специальные JavaScript-компоненты, существующие на сегодняшний день — добавляет эти элементы непосредственно в DOM-дерево.

Специальные (ad-hoc) компоненты

Специальный (ad-hoc) компонент — это компонент, не соответствующий стандартному API-интерфейсу для реализации компонентов.

В идеале следовало бы сократить вышеописанную процедуру до одного шага "Добавить тег slider к HTML-коду". Именно это вы и проделаете в этой статье посредством превращения специального ползунка в стандартный HTML5-компонент. Эта задача будет решаться с использованием следующих API-интерфейсов.

  • Shadow DOM: API-интерфейс Shadow DOM позволяет добавлять в DOM-дерево элементы, которые невозможно добавить обычными средствами, такими как document.getElementById(). Элементы Shadow DOM "скрыты во мраке"; они практически невидимы для остальной части DOM-дерева; по умолчанию, на них не влияет CSS-разметка в окружающем их документе.
  • Templates (шаблоны): HTML5-шаблон — это неизменяемая часть документа. При необходимости отображения этой части можно "отштамповать" этот шаблон (в смысле, создать его), в результате чего он начнет "жить" в DOM-дереве. Шаблоны позволяют инкапсулировать код на HTML, JavaScript и CSS, который можно отштамповать и кастомизировать.
  • Custom elements (специальные (пользовательские) элементы): Элементы типа Custom element — это теги для создаваемых разработчиком компонентов. Они ничем не отличаются от встроенных HTML-тегов, за исключением того, что теги для custom-компонентов должны содержать дефис (например, <custom-tag>).
  • HTML imports (HTML-импорты): Этот интерфейс позволяет импортировать один HTML-файл из другого HTML-файла, что предоставляет разработчику возможность для реализации custom-компонентов в его собственных файлах.

На сегодняшний день вышеописанные API-интерфейсы по-прежнему находятся в стадии разработки. На момент написания этой статьи ни один из браузеров не обеспечивал поддержку всех этих интерфейсов. Таким образом, чтобы сегодня приступить к реализации стандартных HTML5-компонентов, необходимо прибегнуть к каким-либо средствам, которые обеспечивают недостающую функциональность. В частности, можно воспользоваться двумя проектами с открытым исходным кодом, а именно: Polymer и X-Tags.

В этой статье рассматриваются следующие задачи.

  • Инкапсуляция разметки компонента в Shadow DOM.
  • Создание действующих DOM-элементов из инертных HTML-шаблонов.
  • Создание custom-элементов и связывание их с JavaScript.
  • Импорт HTM-файлов.
  • Реализация компонентов с помощью Polymer.
  • Реализация компонентов с помощью X-Tags.

Только браузер Chrome

На момент написания данной статьи примеры с Shadow DOM работали только в новейших версиях браузера Chrome.

Shadow DOM

На рисунке 1 показано HTML5-приложение, воспроизводящее видео, а также окно инструмента Chrome Elements inspector Tools->Developer tools), в котором отображаются элементы, находящиеся внутри элемента video:

Рисунок 1. Элемент video— взгляд изнутри
Screen capture from Chrome showing the Elements inspector superimposed over a video.

Обратите внимание, что инструмент Elements inspector демонстрирует лишь один элемент source внутри этого элемента video несмотря на очевидное наличие внутри него и других элементов, таких как кнопка play/pause, индикатор хода воспроизведения и т. д. Вы не видите этих элементов, поскольку они находятся в Shadow DOM, а эта часть документа по умолчанию невидима. На рисунке 2 показано, как включить отображение элементов Shadow DOM в инструменте Chrome Elements inspector.

Рисунок 2. Включение отображения Shadow DOM в браузере Chrome
Screen capture of the settings dialog in Chrome developer tools where you can enable shadow DOM in Chrome by checking the Show Shadow DOM check box

Для перехода к диалоговому окну настроек, показанному на рисунке 2, нажмите на пиктограмму инструментария в правом нижнем углу окна Developer Tools браузера Chrome. Прокрутите экран вниз до контрольного окошка Show Shadow DOM. При наличии флажка в этом контрольном окошке инструмент Chrome Elements inspector показывает элементы Shadow DOM (см. рисунок 3).

Рисунок 3. Просмотр Shadow DOM для элемента video
Screen capture of looking at the video element's shadow DOM in Chrome's Elements inspector

На рисунке 3, как и на рисунке 1, показаны "внутренности" элемента video, но на этот раз внутри него можно увидеть и другие элементы, поскольку браузер Chrome отображает элементы модели Shadow DOM.

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

На рисунке 4 показан пример, иллюстрирующий размещение элементов в Shadow DOM.

Рисунок 4. Изменение shadow root для кнопки
Screen capture of an application in which a button's shadow root is changed

В начале работы приложения его окно выглядит, как на левом снимке экрана на рисунке 4, на котором отображается только заголовок и кнопка с текстом Click the button. Если пользователь нажмет на эту кнопку, то приложение заменит этот текст изображением с названием (см. правый снимок экрана), которые постоянно находятся в области Shadow DOM этой кнопки.

В листинге 1 приведен HTML-код для приложения, показанного на рисунке 4:

Листинг 1. Заполнение Shadow DOM для кнопки
<!DOCTYPE html>
<html>
   <head>
      <title>Shadow DOM</title>

      <style>
        button {
           font: 18px Century Schoolbook;
           border: thin solid gray;
           background: rgba(200, 200, 200, 0.5);
           padding: 10px;
        }
      </style>
   </head>

   <body>
      <h1>Shadow DOM</h1>

      <button id='button'>Click this button</button>

      <script>
         var b = document.getElementById('button');

         b.onclick = function (e) {
            var sr = b.webkitCreateShadowRoot();

            sr.innerHTML = '<p>This content is in the shadow DOM</p>' +
                            '<img src="beach.png">';
         };
      </script>
   </body>
</html>

При загрузке приложения в браузер исполняется JavaScript-код в нижней части листинга 1. Этот код создает обработчика событий onclick, который использует метод webkitCreateShadowRoot(), с целью создания shadow root (корень теневого дерева), т. е. верхнего узла в дереве Shadow DOM. После этого обработчик событий организует в этом shadow root два элемента: параграф и изображение.

Shadow DOM обеспечивает инкапсуляцию

Shadow DOM реализует для custom-компонентов основополагающий принцип объектно-ориентированного программирования — инкапсуляцию. Разработчики компонентов могут использовать Shadow DOM для защиты содержимого своих компонентов от внешнего влияния в форме CSS-стилей или программного доступа. Это необходимо для того, чтобы гарантировать возможность безопасного взаимодействия компонентов.

Всякий раз, когда вы создаете shadow root для элемента, а затем формируете внутренний HTML-код этого элемента, вы перезаписываете исходное содержимое этого элемента, как демонстрирует приложение на рисунке 4.

По умолчанию кнопки не имеют Shadow DOM, поэтому мы делаем это с помощью браузера (см. листинг 1). Однако по отношению к таким элементам, как video, у которых по умолчанию уже есть Shadow DOM, браузер не столь любезен. Как показано на рисунке 5, выражение sr = b.webkitCreateShadowRoot(); вынуждает инструмент Chrome Elements inspector выпустить сообщение об ошибке A Node was inserted somewhere it doesn't belong (Узел был вставлен в место, которому он не принадлежит).

Рисунок 5. Попытка (неудачная) изменить shadow root элемента video
Screen capture from the Chrome Element inspector that shows the error message that appears if you try to create a Shadow DOM for the video element

Templates, Custom elements и HTML imports

Теперь, когда вы увидели Shadow DOM в действии, я буду использовать Shadow DOM в прагматических целях вместе с другими важнейшими представителями API-интерфейсов для веб-компонентов на основе HTML5: Templates, Custom elements и HTML imports.

На рисунке 6 показан простой неупорядоченный список мероприятий типа meetup (встреча в неформальной обстановке, далее просто "встреча") в группе под названием HTML5 Denver Meetup group.

Рисунок 6. Неупорядоченный список встреч
Screen capture of an unordered list of meetups

Разметка, создающая неупорядоченный список на рисунке 6, показана в листинге 2:

Листинг 2. HTML-код для формирования неупорядоченного списка встреч
<!DOCTYPE html>
<html>
<head>
    <title>Meetups Component</title>
    
</head>
<body>

   <ul>
     <li class="mobile">Sencha Touch. 
        <span class='date'>Jan 23</span></li>

     <li class="html5-in-general">HTML5 & Semantic Markup. 
        <span class='date'>Jun 18</span></li>

     <li class="html5-in-general">HTML5 & Windows 8. 
        <span class='date'>Jul 23</span></li>

     <li class="javascript">HTML5 JavaScript on Crack. 
        <span class='date'>Aug 20</span></li>

     <li class="javascript">CoffeeScript for Recovering JavaScript Programmers. 
        <span class='date'>Sept 17</span></li>

     <li class="html5-in-general">ClojureScript and CouchDB. 
        <span class='date'>May 21</span></li>

     <li class="html5-in-general">Polyfills for the Pragmatist. 
        <span class='date'>Apr 23</span></li>

     <li class="design">CSS3 for Programmers. 
        <span class='date'>Feb 20</span></li>

     <li class="lightning">Quick-start web apps; 
     Graphics; Data visualization; Adaptive patterns; JSON. 
        <span class='date'>Oct 22</span></li>

     <li class="lightning">Web workers; CSS3; HTTP; Audio, video, & canvas; 
     Charting; JS evolution. 
        <span class='date'>March 19</span></li>
   </ul>

</body>
</html>

HTML-код в листинге 2 ничем не примечателен; он просто создает одиночный неупорядоченный список с элементами списка. На рисунке 7 показаны эти же элементы списка, но вместо присутствния в неупорядоченном списке, они находятся внутри custom-компонента, который манипулирует ими.

Рисунок 7. Custom-компонент для списка встреч
Screen capture of a list of meetups generated by a custom meetup-list component

В листинге 3 приведена разметка для приложения, показанного на рисунке 7:

Листинг 3. Custom-компонент meetup-talks вместо неупорядоченного списка
<!DOCTYPE html>
<html>
<head>
    <title>Meetups Component</title>
    <script src="../polymer/polymer.js"></script>
    <link rel="components" href="meetup-component.html">

</head>
<body>

   <meetup-talks>
     <li class="mobile">Sencha Touch. 
        <span class='date'>Jan 23</span></li>

     <li class="html5-in-general">HTML5 & Semantic Markup. 
        <span class='date'>Jun 18</span></li>

     <li class="html5-in-general">HTML5 & Windows 8. 
        <span class='date'>Jul 23</span></li>

     <li class="javascript">HTML5 JavaScript on Crack. 
        <span class='date'>Aug 20</span></li>

     <li class="javascript">CoffeeScript for Recovering JavaScript Programmers. 
        <span class='date'>Sept 17</span></li>

     <li class="html5-in-general">ClojureScript and CouchDB. 
        <span class='date'>May 21</span></li>

     <li class="html5-in-general">Polyfills for the Pragmatist. 
        <span class='date'>Apr 23</span></li>

     <li class="design">CSS3 for Programmers. 
        <span class='date'>Feb 20</span></li>

     <li class="lightning">Quick-start web apps; 
     Graphics; Data visualization; Adaptive patterns; JSON. 
        <span class='date'>Oct 22</span></li>

     <li class="lightning">Web workers; CSS3; HTTP; Audio, video, & canvas; 
     Charting; JS evolution. 
        <span class='date'>March 19</span></li>
   </meetup-talks>

</body>
</html>

Обратите внимание, что разметка в листинге 3 почти идентична разметке в листинге 2, за исключением следующих трех различий.

  • Разметка в листинге 3 содержит файл с именем polymer.js. Этот файл поставляется в составе проекта с открытым исходным кодом Polymer, который предоставляет реализации API-интерфейсов для веб-компонентов на основе HTML5 (Shadow DOM, Templates и т.д.). Проект Polymer, который более подробно рассматривается в следующем разделе (см. Polymer), осуществляет группа по разработке браузера Chrome.
  • В листинге 3 элемент link используется для импорта другого HTML5-файла. Подобное использование элемента link носит название HTML import. На момент написания данной статьи функциональность HTML import не была реализована ни в одном из браузеров; в данном примере эта функциональность базируется на использовании проекта Polymer.
  • Элемент неупорядоченного списка в листинге 2 заменен custom-элементом meetup-talks в листинге 3.

Custom-элемент meetup-talks реализован в виде файла meetup-component.html, показанного в листинге 4.

Листинг 4. Элемент meetup-list
<element name="meetup-list">
  <template>
    <style>
      /* styles that follow only apply to the ShadowDOM of the <meetup-list> element */

      #meetups {
        display: block;
        padding: 15px;
        padding-top: 0px;
        background: lightgray;
        border: thin solid cornflowerblue;
      }

      .title {
        color: blue;
        font-size: 1.5em;
      }
    </style>

    <div id='meetups'>
      <p class='title'>HTML5 in General</p>
      <content select='.html5-in-general'></content>
    
      <p class='title'>JavaScript</p>
      <content select='.javascript'></content>
    
      <p class='title'>Design</p>
      <content select='.design'></content>
    
      <p class='title'>Lightning</p>
      <content select='.lightning'></content>
    </div>
  </template>

  <script>
    Polymer.register(this);
  </script>
</element>

Текст в листинге 4 является более интересным. Во-первых, я декларирую новый элемент с именем meetup-list (не забывая включить дефис в это имя в соответствии с требованиями спецификации HTML). В конце листинга 4 одна строка JavaScript-кода регистрирует элемент meetup-list в глобальном объекте Polymer. Это декларирование и регистрация custom-элемента позволяет использовать его в HTML-страницах.

В элементе element я декларирую шаблон (template). Шаблон определяет Shadow DOM декларативным, а не программным образом. Итак, в листинге 4 я создаю шаблон Shadow DOM. Всякий раз, когда кто-то использует элемент meetup-list в HTML-странице, браузер задействует этот шаблон Shadow DOM для того, чтобы "отштамповать" (создать) новый экземпляр Shadow DOM для требуемого элемента.

Внутри этого шаблона я определяю два стиля CSS, которые применяются только к элементам в этом шаблоне. И наоборот — шаблон представляет Shadow DOM, поэтому CSS-стили, определенные в окружающем документе, не влияют на элементы внутри этого шаблона. Например, если нам потребуется добавить стиль для элементов параграфа в HTML-коде в листинге 3, этот стиль не повлияет на параграфы в шаблоне.

Помимо CSS-стилей этот шаблон определяет элементы в последующих экземплярах Shadow DOM. Элемент content использует CSS-селектор с целью выбора элементов списка из исходного контента элемента meetup-list. Таким образом, шаблоны (помимо декларативного задания экземпляров Shadow DOM) способны выборочно вставлять исходный контент элемента внутрь себя. Обратите внимание на следующий момент: конструкцию <content></content> можно использовать для того, чтобы вставить весь исходный контент элемента в его шаблон.


Реализация веб-компонентов в настоящее время

Все API-интерфейсы для веб-компонентов, рассматриваемые в этой статье, являются сравнительно новыми и в различной степени поддерживаются поставщиками браузеров. Для эффективной реализации веб-компонентов в настоящее время вы не можете просто написать код и загрузить его в свой браузер; для этого вам потребуется решение, которое в мире HTML5 может носить название polyfill (полифил) или shim. Такое решение позволяет задействовать новую функциональность, если это возможно, но в противном случае оно отступает обратно, к поддерживаемому альтернативному варианту.

В настоящий момент полифилы для веб-компонентов предоставляют два проекта, а именно: Polymer и Mozilla X-Tags. Средства Polymer работают только в браузере Chrome, а также требуют наличия веб-сервера для использования HTML-импорта, вследствие чего они непригодны для "производственного" применения. Средства X-Tags работают почти со всеми современными браузерами и не требуют веб-сервера; однако они реализует лишь API-интерфейс Custom Elements (т. е. API-интерфейсы Templates и Shadow DOM остаются "за бортом").

В следующих разделах я покажу, как использовать средства Polymer и X-Tags, соответственно, для представления специального ползунка, реализованного в предыдущих статьях этого цикла, в виде стандартного компонента.


Polymer

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

Figure 8. Ползунок на основе Polymer
Screen capture of slider implemented with Polymer

В листинге 5 показано, как использовать Polymer-версию ползунка.

Листинг 5. Использование Polymer-версии ползунка
<!DOCTYPE html>
<html>
   <head>
      <title>Slider with Polymer</title>
    <script src="../polymer/polymer.js"></script>
    <script src="lib/slider.js"></script>
    <link rel="import" href="./slider.html" />
   </head>
   
   <body>
     <x-slider id='slider'
                fillColor='goldenrod'
                strokeColor='red'>
     </x-slider>
   </body>
</html>

При сравнении листинга 5 с листингом 2 в первой статье этого цикла, в котором демонстрируется использование специального ползунка как такового, несложно заметить поразительное обстоятельство —листинг 5 существенно проще. Применение Polymer-элемента x-slider позволяет создать ползунок буквально одной строкой HTML-кода.

Реализация элемента x-slider показана в листинге 6.

Листинг 6. Компонент типа ползунок на основе Polymer
<element name="x-slider" attributes="strokeColor fillColor">
  <template>
    <style>
       x-slider {
          display: block;
       }

       .x-slider-button {
          float: left;
          margin-left: 2px;
          margin-top: 5px;
          margin-right: 5px;
          vertical-align: center;
          border-radius: 4px;
          border: 1px solid rgb(100, 100, 180);
          background: rgba(255, 255, 0, 0.2);
          box-shadow: 1px 1px 4px rgba(0,0,0,0.5);
          cursor: pointer;
          width: 25px;
          height: 20px;
       }

       .x-slider-button:hover {
          background: rgba(255, 255, 0, 0.4);
       }

       #xSliderDiv {
          position: relative;
          float: right;
          width: 80%;
          height: 65%;
       } 
    </style>

    <div id='x-slider-buttons-div' 
         style='width: {{width}}px; height: {{height}}px;'>

       <button class='x-slider-button' id='xSliderMinusButton'>-</button>
       <button class='x-slider-button' id='xSliderPlusButton'/>+</button>
       <div id='xSliderDiv'></div>

    </div>
  </template>

  <script>
    Polymer.register(this, {
        width: 350,
        height: 50,
        strokeColor: 'blue',
        fillColor: 'cornflowerblue',

        ready: function () { 
          var self = this,
              slider = new COREHTML5.Slider(this.strokeColor, 
                                            this.fillColor, 0);

          setTimeout( function () { // This timeout is a hack
            slider.appendTo(self.$.xSliderDiv);
            slider.draw();
          }, 200);

          this.$.xSliderMinusButton.onclick = function () {
            if (slider.knobPercent >= 0.1) {
              slider.knobPercent -= 0.1;
              slider.erase();
              slider.draw();
            }
          };

          this.$.xSliderPlusButton.onclick = function () {
            if (slider.knobPercent <= 0.9) {
              slider.knobPercent += 0.1;
              slider.erase();
              slider.draw();
            }
          };
        }
    });

  </script>
</element>

Листинг 6 подобен листингу 4. В каждом из этих Листингов задается новый элемент, который затем регистрируется в глобальном объекте Polymer. Кроме того, в каждом из них описывается шаблон, содержащий CSS-стили и разметку. Листинг 6 отличается от листинга 4 тем, что в листинге 6 используется связывание данных Polymer и обработчик события ready.

Нотация с двойными фигурными скобками — напр., {{width}}— обозначает свойство элемента. Вы сможете использовать эту нотацию в разметке своего элемента. В листинге 6 я установил ширину и высоту элементаDIV, охватывающего новый элемент, в соответствии с шириной и высотой, которые автор страницы задал с помощью свойств элемента.

Мы декларируем свойства в объекте, который передаем в метод Polymer.register() например, в листинге 6 декларируется четыре свойства для элемента x-slider element: width, height, strokeColor и fillColor. Если вы зададите свойства с помощью атрибута attributes элемента element element — т. н. публикация свойств— то авторы страницы смогут использовать эти свойства в своих элементах, как имеет место у элемента x-slider в листинге 5.

Согласно документации Polymer, когда компонент закончил инициализировать себя, он вызывает свой метод ready(), если таковой существует. Эта документация не уточняет, когда именно компоненты инициализируют себя, однако очевидно, что это происходит до того, как браузер будет готов отобразить компонент. Поскольку компоненты инициализируются до того, как они будут готовы к отображению, и поскольку метод ready()является на данный момент единственным методом в жизненном цикле, мне пришлось добавить метод setTimeout(), который рисует ползунок через 200 мс после вызова метода ready(). Несомненно, подобные шероховатости будут сглажены с течением времени (кроме того, спецификация Web Components предусматривает обратный вызов inserted, который осуществляется браузером после того, как он вставляет элемент в DOM, однако на момент публикации данной статьи проект Polymer не поддерживал этого механизма).

Polymer также предоставляет для custom-элементов атрибут $, который ссылается на карту атрибутов соответствующего элемента. Я использую эту карту для обращения к элементу DIV, который в конечном итоге и содержит наш специальный ползунок.


X-Tags

На рисунке 9 показана версия ползунка на основе X-Tags.

Рисунок 9. Компонент типа ползунок на основе X-Tags
Screen capture of the slider implemented with X-Tags

Реализации ползунка на основе Polymer и X-Tags визуально неразличимы; я использовал различающиеся цвета лишь для иллюстрации того обстоятельства, что они были созданы на основе разных платформ. В листинге 7 показано, как приложение на рисунке 9 использует версию ползунка на основе X-Tags (также реализованную как x-slider):

Листинг 7. Использование компонента типа ползунок на основе X-Tags
<!DOCTYPE html>
<html>
   <head>

      <title>Slider with x-tag</title>
      <link rel="stylesheet" type="text/css" href="x-slider.css" />
   </head>
   
   <body>
      <x-slider id='slider'
                slider-fill-color='rgba(50, 105, 200, 0.8)'
                slider-stroke-color='navy'>
      </x-slider>
   </body>

   <script type="text/javascript" src="lib/x-tag.js"></script>
   <script type="text/javascript" src="lib/slider.js"></script>
   <script type="text/javascript" src="x-slider.js"></script>
</html>

Листинг 7 подобен листингу 5.В каждом из этих Листингов используется простой элемент x-slider для размещения ползунка на странице. Различие между этими Листингами состоит в том, что Polymer-версия использует HTML-импорт для включения фрагмента HTML-кода, описывающего ползунок, а версия на основе X-Tags включает с этой целью фрагмент JavaScript-кода, поскольку X-Tags не поддерживает HTML-импорт.

JavaScript-код для реализации компонента показан в листинге 8.

Листинг 8. JavaScript-код для ползунка
function getFirstAncestor(name, startingElement) {
   var  element = startingElement
      , localName = element.localName;

   while (localName !== name) {
      element = element.parentNode;
      localName = element.localName;
   }

   return element;
};
   
function getSlider(element) {
   return getFirstAncestor('x-slider', element).slider;
};
   
xtag.register('x-slider', { 
   onCreate: function () { 
      var content =
             "<div id='x-slider-buttons-div'>"                                          +
                "<button class='x-slider-button' id='x-slider-minus-button'>-</button>" +
                "<button class='x-slider-button' id='x-slider-plus-button'>+</button>"  +
             "</div>"                                                                   +
             ""                                                                         +
             "<div id='x-slider-slider-div'></div>"                                     +
             ""                                                                         +
             "<div id='x-slider-readout-div'></div>";

          stroke = this.getAttribute('slider-stroke-color'),
          fill   = this.getAttribute('slider-fill-color');
 
         // 'this' is the x-slider HTML element

         this.max = 100;

         this.innerHTML = content;
         this.slider = new COREHTML5.Slider(stroke, fill, 0);

         this.slider.appendTo('x-slider-slider-div');
         this.slider.draw();
      },

      events: {
         'click:touch:delegate(#x-slider-plus-button)': function(event, slider) {
            var slider = getSlider(this)  // 'this' is the button
               , value = getFirstAncestor('x-slider', this).getValue();
               
            if (slider.knobPercent <= 0.9) {
               slider.knobPercent += 0.1;
            }
            slider.erase();
            slider.draw();

            console.log(value);
         },

         'click:touch:delegate(#x-slider-minus-button)': function(event, slider) {
            var slider = getSlider(this)  // 'this' is the button
               , value = getFirstAncestor('x-slider', this).getValue();

            if (slider.knobPercent >= 0.1) {
               slider.knobPercent -= 0.1;
            }
            slider.erase();
            slider.draw();

            console.log(value);
         },
      },

      methods: {
         getValue: function () {
            // 'this' is the x-slider HTML element
            return this.slider.knobPercent * this.max; 
         }
      }
});

Задание компонента с помощью X-Tags в листинге 8 сильно отличается от задания компонента с помощью Polymer в листинге 6. В версии на основе X-Tags элементы описываются исключительно на JavaScript, что предусматривает использование конкатенации строк и настройку внутреннего HTML-элемента с целью задания контента для custom-элемента. Этот подход радикально отличается от версии на основе Polymer, в которой контент custom-элемента задается с помощью намного более "читабельного" и удобного в сопровождении HTML-кода.

X-Tags не имеет атрибутов, подобных атрибуту $ у Polymer, который облегчает доступ к элементам внутри custom-элемента, поэтому мне пришлось реализовать функцию getFirstAncestor() для обращения к элементу, содержащему специальный ползунок.

И, наконец, спецификация X-Tags поддерживает реализацию событий для обработки нажатий на кнопки. Я мог бы использовать эту возможность для контроля над кнопкой своего ползунка; однако применительно к данной статье мне это показалось избыточным.

Для полноты картины в листинге 9 показан CSS-код для версии на основе X-Tags.

Листинг 9. CSS-код ползунка
x-slider {
   display: block;
}

.x-slider-button {
   float: left;
   margin-left: 2px;
   margin-top: 15px;
   margin-right: 5px;
   vertical-align: center;
   border-radius: 4px;
   border: 1px solid rgb(100, 100, 180);
   background: rgba(255, 255, 0, 0.2);
   box-shadow: 1px 1px 4px rgba(0,0,0,0.5);
   cursor: pointer;
   width: 25px;
   height: 20px;
}

.x-slider-button:hover {
   background: rgba(255, 255, 0, 0.4);
}

#x-slider-buttons-div {
   width: 25%;
   height: 100%;
} 

#x-slider-slider-div {
   position: relative;

   float: right;
   margin-top: -40px;
   width: 80%;
   height: 65%;
}

Заключение

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

В этом цикле статей было показано, как использовать API-интерфейсы HTML5 для реализации как стандартных, так и специальных компонентов. HTML5-спецификации по реализации компонентов являются сравнительно новыми и в различной степени поддерживаются поставщиками браузеров. Вы можете приступить к реализации стандартных компонентов уже сегодня, однако для этого вам будет недостаточно одного только вашего браузера. В этой статье я кратко рассмотрел два каркаса — Polymer и X-Tags — которые позволяют реализовать стандартные компоненты.

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


Загрузка

ОписаниеИмяРазмер
Учебный программный кодwa-html5-components-3-code.zip1.68 МБ

Ресурсы

Научиться

Получить продукты и технологии

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Web-архитектура
ArticleID=949563
ArticleTitle=HTML5-компоненты: Реализация стандартных компонентов
publish-date=10222013