HTML-импорт — include для веба: часть 1

http://www.html5rocks.com/en/tutorials/webcomponents/imports/
  • Перевод
  • Tutorial
Перевод статьи «HTML Imports #include for the web», Eric Bidelman.

От переводчика

Недавно я перевел статью по основам HTML Import. Я обещал, что если эта тема заинтересует хабра-сообщество, то переведу более подробную статью. Я решил разбить перевод на две одинаковые по размеру части, так как, по моему, на одну часть слишком много буков. Вторая часть выйдет спустя несколько дней после публикации этой части. Если, конечно, эта часть более-менее понравится хабра-сообществу.

Для чего нужен HTML-импорт?


Давайте поговорим о том, как мы загружаем различные ресурсы. JavaScript мы загружаем при помощи
<script src>
. Для CSS у нас есть
<link rel="stylesheet">
. Для изображений
<img>
. Для видео есть
<video>
. Для аудио —
<audio>
… Давайте ближе к сути! Для большинства видов контента есть простые способы его подгрузки. Но не для HTML. Для HTML у нас есть следующие варианты:
  1. <iframe>
    — испробованный и рабочий, но тяжеловесный способ. Контент iframe'а живет в отдельном от главной страницы контексте. Хоть это и хорошая особенность, она также создает дополнительные трудности: подгонка размера айфрейма к его содержимому, работа с внутренними скриптами и стилями.
  2. AJAX — мне нравится
    xhr.responseType="document"
    , но загрузка HTML при помощи JS выглядит как-то неправильно.
  3. КривыеКостылиTM — html код в виде JS строк или комментариев, например
    <script type="text/html">
    .

HTML код, это самый простой тип контента, но в этом плане, он требует наибольших усилий. Хорошо, что у нас есть Web Components, они помогут нам справиться с этой и другими проблемами.

Начало работы


В наборе стандартов Web Components есть стандарт HTML Imports, который позволяет подключение HTML-документов друг в друга. В подключаемых HTML-документах разрешается Javascript и CSS, словом, все что .html обычно содержит. Это замечательный инструмент для загрузки пакетов HTML/CSS/JS кода.

Основы

Подключение документов происходит при помощи
<link rel="import">
:

<head>
  <link rel="import" href="/path/to/imports/stuff.html">
</head>

Указанный URL, это расположение импорта(import location). Чтобы использовать импорт с другого домена, его расположение должно позволять междоменное разделение ресурсов(CORS):

<!-- Resources on other origins must be CORS-enabled. -->
<link rel="import" href="http://example.com/elements.html">

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

Проверка на поддержку браузером

Чтобы определить, поддерживается ли импорт, проверьте существует ли свойство
import
в элементах
<link>
:

function supportsImports() {
  return 'import' in document.createElement('link');
}

if (supportsImports()) {
  // Good to go!
} else {
  // Use other libraries/require systems to load files.
}

Браузерная поддержка пока на ранней стадии(прим. переводчика: оригинал статьи опубликован 11 ноября 2013, теперь, наверное, другая ситуация с поддержкой). Chrome 31 первый браузер поддерживающий HTML-импорт. Chrome 36 обновлен до последней спецификации этой фичи. Вы можете включить поддержку импорта, отметив флаг «Включить экспериментальные функции веб-платформы» по адресу
about:flags
в Chrome Canary. Для других браузеров это пока не работает.

Примечание: Включение экспериментальных функций позволит использовать и другие полезные фичи веб-компонентов


Бандлинг ресурсов

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

Вы сможете экспортировать целые пакеты веб содержимого всего одной линкой.

Bootstrap это хороший пример того, как мог бы пригодиться импорт компонентов. Бутстрап состоит из различных файлов (bootstrap.css, bootstrap.js и др.), использует JQuery (как импортируемый компонент), а в результате выдает инструменты для верстки. Разработчикам нравится возможность подключать те или иные модули, по мере необходимости. Но обычно мы идем простым путем, подключая все модули бутстрапа сразу.

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

<head>
  <link rel="import" href="bootstrap.html">
</head>

Нужно всего лишь добавить один линк импорта. Больше не нужно подключать кучу файлов, вместо этого весь Bootstrap завернут в файл bootstrap.html:

<link rel="stylesheet" href="bootstrap.css">
<link rel="stylesheet" href="fonts.css">
<script src="jquery.js"></script>
<script src="bootstrap.js"></script>
<script src="bootstrap-tooltip.js"></script>
<script src="bootstrap-dropdown.js"></script>
...

<!-- scaffolding markup -->
<template>
  ...
</template>

События load/error

После загрузки импорта
<link>
запускает событие
load
, а в случае ошибки (например 404), событие
error
.

Импорт всегда загружается сразу же, в порядке нахождения тега
<link>
. Отловить события загрузки можно при помощи атрибутов
onload/onerror
:
<script async>
  function handleLoad(e) {
    console.log('Loaded import: ' + e.target.href);
  }
  function handleError(e) {
    console.log('Error loading import: ' + e.target.href);
  }
</script>

<link rel="import" href="file.html"
      onload="handleLoad(event)" onerror="handleError(event)">

Примечание: обработчики событий загрузки/ошибки нужно объявлять перед импортом, так как браузер загружает импорт в тот момент, когда встречает тег импорта. Если на момент импорта нет обработчика загрузки, в консоли выведется ошибка undefined function.

Вы, также, можете динамически создавать импорт:

var link = document.createElement('link');
link.rel = 'import';
link.href = 'file.html'
link.onload = function(e) {...};
link.onerror = function(e) {...};
document.head.appendChild(link);


Использование содержимого импорта


Элемент импорта на странице не указывает браузеру, где размещать содержимое импорта. Он только говорит браузеру получить документ для его дальнейшего использования. Чтобы использовать содержимое импорта, нам нужно написать немного JS кода.

Вот он момент прозрения, импорт, это всего-лишь документ. На самом деле, содержимое импорта так и называется документ импорта(import document). А использовать результат импорта вы можете стандартными средствами DOM API!

link.import

Для получения содержимого импорта используется свойство .import элемента
link
:

var content = document.querySelector('link[rel="import"]').import;

link.import равен null при следующих обстоятельствах:
  • Браузер не поддерживает импорт.
  • У элемента нет атрибута rel="import".
    Объект не добавлен в DOM.
    Или был удален из DOM
    Ресурс не поддерживает CORS.


    Полный пример

    Допустим у нас есть страница warnings.html:

    <div class="warning">
      <style scoped>
        h3 {
          color: red;
        }
      </style>
      <h3>Warning!</h3>
      <p>This page is under construction</p>
    </div>
    
    <div class="outdated">
      <h3>Heads up!</h3>
      <p>This content may be out of date</p>
    </div>
    


    Вы можете использовать только необходимую вам часть импортированной страницы:
    <head>
      <link rel="import" href="warnings.html">
    </head>
    <body>
      ...
      <script>
        var link = document.querySelector('link[rel="import"]');
        var content = link.import;
    
        // Grab DOM from warning.html's document.
        var el = content.querySelector('.warning');
    
        document.body.appendChild(el.cloneNode(true));
      </script>
    </body>
    



    Скрипты в импорте


    Импорт работает не совсем, как часть документа, который его использует. Но вы, все же, можете работать с подключившей его страницей. Из импортированной страницы можно работать, как с внутренним DOM, так и с главным документом:

    Пример — import.html добавляет один из своих стилей главному документу
    <link rel="stylesheet" href="http://www.example.com/styles.css">
    <link rel="stylesheet" href="http://www.example.com/styles2.css">
    
    <style>
      /* Примечание: <style> Внутренние стили, по умолчанию применяются к импортирующему документу. Их не нужно явно добавлять в главную страницу. */
      #somecontainer {
        color: blue;
      }
    </style>
    ...
    
    <script>
      // importDoc - document импорта
      var importDoc = document.currentScript.ownerDocument;
    
      // mainDoc - главный DOM объект
      var mainDoc = document;
    
      // Копируем первую таблицу стилей из данного документа,
      // и помещаем в главный документ.
      var styles = importDoc.querySelector('link[rel="stylesheet"]');
      mainDoc.head.appendChild(styles.cloneNode(true));
    </script>
    

    Итак, что здесь происходит? При помощи
    document.currentScript.ownerDocument
    мы получаем доступ к внутреннему элементу-корню импортированного документа и добавляем его кусок в главный документ (
    mainDoc.head.appendChild(...)
    ). Это конечно бесполезный код, но нам он нужен, чтобы понять, что мы можем обращаться как к главному, так и ко внутреннему корню DOM.

    Скрипты внутри импорта могут как сами исполнять код, так и предоставлять функции для выполнения в главном документе. Это похоже на модули в Питоне.

    Правила импорта:
    • Код импорта выполняется в контексте содержащего его документа. Из этого исходят две удобные вещи:
      • Функции объявленные в импорте содержатся в главном объекте window.
      • Вам не нужно делать действий вроде добавления в главный документ тегов
        <script>
        из импорта. Так что скрипты внутри импорта выполняются автоматически.

    • Импорт не прерывает загрузку главного кода страницы. В то же время, скрипты выполняются по порядку. То есть, происходит отложенное выполнение скриптов. Дальше об этом поподробнее.
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 26
  • –4
    Можно было просто дополнить сюда.

    З.Ы. На данном этапе развития — бесполезная фича.
    • +11
      Можно было просто дополнить сюда.

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

      В следующей части будут описаны более интересные детали, там и станет понятно, зачем оно все.
      • –1
        Но память освежать иногда надо. Спасибо автору за напоминание.
        • –1
          Memphis23Rus, соболезную вашей памяти, т.к. предыдущая статья была неделю назад.

          jojo97, статья-то другая и автор тоже, а вот материал один, ничего нового. Даже не о чем на эту тему дискутировать.

          <sarcasm>Даешь по статье о HTML Import раз в неделю. Уверен, еще много кто в своих блогах об этом написал.</sarcasm>
      • +4
        Хм, для подобного же как раз и придумали SSI из которого потом и вытек (вперемешку с perl) многострадальный PHP…

        Имхо, ненужная фича даже если бы она поддерживалась со времён IE6+, на моей памяти даже статик сервера имели вышеупомянутый SSI.
        • 0
          Сразу отвечу на предложения использования в качестве разработки «одностраничных» приложений, как раз подумал об этом только что.

          По-моему, вот такое:
          <script type="text/html"><?=require 'path/to/file.html';?></script>
          

          Будет не намного сложнее в использовании (ну и на других серверных языках аналогично).
        • +2
          Господи, ну зачем?!
          Кому нужна эта фича? На дворе 2014 год, все на шаблонизаторах и ajax, где это сделать — 5 секунд.
          Я еще понимаю isolated dom, но ЭТО…
          К тому же, если очень хочется, то на любом сервере можно за пару секунд включить SSI.

          В следующей части будут описаны более интересные детали

          Какие могут быть интересные детали у обычного импорта? :) Возможность в импортнутых страничках использовать импорт? :D
          • +1
            Shadow dom, Templates и пр.
            • +3
              Shadow dom == isolated dom — про это я сказал, однако к inlcude это мало имеет отношение. Там целый стандарт что и как.
              Простой include никому не нужен в принципе — как и написали в первом комментарии — на данном этапе — абсолютно бесполезная фича.
              • 0
                Include это часть стандарта веб-компонентов, которая заменит ajax загрузку. Не то чтобы это давало какие-то невероятные плюсы, но это стандартизация и ей нужно следовать.
                • +1
                  Стандартам, безусловно, надо следовать. Но это не стандарт еще, даже не драфт толком.

                  Но речь не об этом — еще раз говорю — сама по себе include — абсолютно бесполезная фича. И посвещать ей целые страницы — смешно. В контексте веб-компонент — другое дело.
                  • 0
                    Во второй части статьи будет больше про веб-компоненты. Эта часть как вступление. Возможно не стоило разделять статью, так как первая часть кажется не очень-то интересной, но вряд ли кто-то способен осилить две части за один присест.
                    • +1
                      Не знаю, как многие, но лично я не замечаю объемов статьи, если она интересная. Даже радуюсь, что еще много чего осталось прочитать. Единственное, если статья очень большая и нет времени — могу закинуть в избранное и дочитать потом.

                      Возможно, вы ошиблись, не опубликовав всю статью. Все же это Хабр, а не Вконтакте, здесь немного другой уровень читателей и контента, чем в стандартной социальной сети, а значит, что многие решения, вроде урезания размеров статей, не всегда имеют смысл = )
                      • 0
                        Ок, буду знать на будущее.
          • 0
            Как раз вчера вечером вспомнил про этот функционал, прибежал потестировать, все настроил, а потом только в комментарии на хабре увидел, что поддерживается только хромом.
            • +2
              Вы можете включить поддержку импорта, отметив флаг
              «Включить экспериментальные функции веб-платформы»
              по адресу about:flags в Chrome Canary

              Ничего включать не нужно, HTML-импорты уже работают по умолчанию в стабильных версиях Opera 23 и Chrome 36 (все веб-компоненты по умолчанию включены, начиная с этих версий)

              • 0
                Ну да, статья еще немного из прошлого. Я так и указал
                • 0
                  Проблема в том, что люди не читают сноску «статья устарела», а воспринимают всё за чистую монету.
                  • –1
                    Ну мы считаем, что люди на хабре достаточно внимательны, чтобы такое заметить.

                    «статья устарела»

                    Ну-ну, статья не устарела все по прежнему актуально, кроме слабой поддержки хромом.
              • 0
                firefox 30.0 linux x64

                > 'import' in document.createElement('link')
                false

                ничего себе, устарела…
                • НЛО прилетело и опубликовало эту надпись здесь
                  • 0
                    Я как то на сайте видел на сайте, что подключается одна всего строчка с js, а загружается целая страница. Причем страница явно динамическая.
                    Т.е. через просмотр кода страницы смотришь — там всего одна строка — подключение какого-то файла js. Подскажите где порыться почитать про эту технологию. Уже все перерыл — не могу найти что-то.
                    • 0
                      $('body').load('/yourpage.html');
                      

                      Вот и вся технология
                    • 0
                      Кроме трёх перечисленных способов импорта HTML есть еще <object type="text/html">
                      • 0
                        Уже упомянуто как <script type="text/html"> в разделе «КривыеКостыли™», хотя сам этот метод лично мне видится не меньшим костылём, пусть и не таким кривым.
                      • 0
                        Вот и вторая часть, наконец-то перевел. Приятного чтения.

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.