Когда мы в компании экспериментировали с inline SVG пиктограммами , то обнаружили разные браузеры реагировали различным образом на то, как мы ссылались на SVG с набором иконок. Нам хотелось получить универсальный и стабильный метод, который бы работал одинаково предсказуемо во всех браузерах, поддерживающих SVG.
Цепочка проб и ошибок привела нас к такой схеме:
- Преобразуем SVG-файл в строку текста и присваиваем её какой-либо переменной.
- Объединяем JavaScript файл, который мы получили на предыдущем этапе, с другими JS-файлами, которые подключаются в
<head>
. - Вдо первого использования 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>
Замечания
- При формировании JS-файла для простоты полагается, что будет создана глобальная переменная
SVG_SPRITE
. Чтобы не загрязнять глобальную область, я рекомендую сохранить содержимое SVG в пространство имён приложения. - Имена файлов жёстко прописаны в Grunt-задаче. Есть смысл задачу сделать конфигурируемой, если у вас в проекте несколько наборов SVG-пиктограмм.
Демонстрация
У нас в компании есть проект-шаблон , в котором можно детально посмотреть эту технику подключения SVG.