Ещё один вариант загрузки набора SVG-изображений

Когда мы в компании экспериментировали с inline SVG пиктограммами, то обнаружили разные браузеры реагировали различным образом на то, как мы ссылались на SVG с набором иконок. Нам хотелось получить универсальный и стабильный метод, который бы работал одинаково предсказуемо во всех браузерах, поддерживающих SVG.

Цепочка проб и ошибок привела нас к такой схеме:

  1. Преобразуем SVG-файл в строку текста и присваиваем её какой-либо переменной.
  2. Объединяем JavaScript файл, который мы получили на предыдущем этапе, с другими JS-файлами, которые подключаются в <head>.
  3. В до первого использования SVG добавляем пустой элемент — плейсхолдер и вставляем в него содержимое SVG-строки.

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

<svg xmlns="http://www.w3.org/2000/svg" class="icon_circle">
  <use xlink:href="#circle"></use>
</svg>

Опишу все проделанные шаги подробнее.

Конвертирование SVG в JS

Этой задачей занимается Grunt.

grunt.registerTask('elements', 'Transform a SVG sprites to a JS file',
  function () {
    var LINE_LENGTH = 100, svg = [], i, l, content;

    content = grunt.file.read('assets/images/elements.svg');
    content = content.replace(/'/g, "\\'");
    content = content.replace(/>\s+</g, "><").trim();
    l = Math.ceil(content.length / LINE_LENGTH);

    for (i = 0; i < l; i++) {
      svg.push("'" + content.substr(i * LINE_LENGTH, LINE_LENGTH) + "'");
    }

    grunt.file.write('assets/_/js/elements.js',
      'var SVG_SPRITE = ' + svg.join('+\n') + ';');
  }
);

Из файла assets/images/elements.svg получается файл assets/_/js/elements.js.

Конкатенация

Файл elements.js должен быть загружен в <head> Наверняка у вас есть ещё несколько скриптов, которые тоже требуется загружать в начале страницы. Их можно объединить между собой чтобы уменьшить количество HTTP-запросов к серверу.

Добавление на страницу

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

<div id="elements-placeholder"
  style="border: 0; clip: rect(0 0 0 0); overflow: hidden;
    margin: -1px; padding: 0; position: absolute;
    width: 1px; height: 1px;"></div>

Плейсхолдер нужен для того, чтобы безопасно модифицировать DOM-дерево в процессе загрузки страницы. У него должен быть уникальный id. Я ещё добавил некоторые inline-стили, чтобы защитить страницу от эффекта «упячки», если контуры случайно окажутся не помещены в <defs>.

Заполняем плейсхолдер.

<script>document.getElementById("elements-placeholder").innerHTML = SVG_SPRITE;</script>

Замечания

  1. При формировании JS-файла для простоты полагается, что будет создана глобальная переменная SVG_SPRITE. Чтобы не загрязнять глобальную область, я рекомендую сохранить содержимое SVG в пространство имён приложения.
  2. Имена файлов жёстко прописаны в Grunt-задаче. Есть смысл задачу сделать конфигурируемой, если у вас в проекте несколько наборов SVG-пиктограмм.

Демонстрация

У нас в компании есть проект-шаблон, в котором можно детально посмотреть эту технику подключения SVG.