Автор оригинальной статьи - Osvaldas Valutis
Варианты включения SVG в HTML
Есть два варианта включения SVG в HTML с помощью тега <use>
.
Вариант #1
В первом случае используется внешний файл, из которого мы можем включить SVG-графику следующим образом:
1 |
<svg><use xlink:href="sprite.svg#cart"></use></svg> |
Плюсы:
- файл
sprite.svg
будет закэширован браузером.
Минусы:
- такая техника включения SVG не будет работать в IE11 и ниже (к счастью есть решение JS fallback).
- при каждом включении придется повторять путь к файлу.
Вариант #2
Во втором случае, вся SVG-графика прописана в самом HTML-файле страницы.
Заметьте, что при определении SVG, я использую тег <symbol>
, в котором я один раз устанавливаю свойство viewbox
вместо того чтобы делать это при каждом включении SVG изображения. Также важно, чтобы определения графики в HTML-дереве находилось выше её включения (<use>
), иначе может не сработать в некоторых браузерах.
1 2 3 4 5 6 7 8 |
<body> <svg style="display: none;"> <symbol id="svg-cart" viewbox="0 0 50 50"><path d="..." /></symbol> </svg> <!-- ... --> <svg><use xlink:href="#svg-cart"></use></svg> <!-- ... --> </body> |
Плюсы:
- работает в IE9 и выше.
- не нужно повторять путь и имя файла при каждом включении SVG-изображения.
1 2 3 4 5 |
<!-- так гораздо лучше --> <svg><use xlink:href="#svg-cart"></use></svg> <!-- чем так --> <svg><use xlink:href="theme/something/assets/img/sprite.svg#cart"></use></svg> |
Минусы:
- Определения SVG-графики теперь часть HTML-документа и не кэшируется браузерами как отдельная сущность.
Что если в вашем проекте используется много SVG графики, например на 100кб? Это значит, что при каждой загрузке новой страницы, на ней будет подгружаться дополнительные 100кб данных. Представьте пользователя мобильного браузера, который переходил с одной страницы вашего сайта на другую 10 раз, он потратит дополнительный 1мб траффика, что явно не блещет производительностью.
Так можем ли мы закэшировать SVG-спрайт и в то же время избавиться от постоянных обращений к внешнему файлу при включении SVG-графики?
localStorage
localStorage дает возможность хранить данные веб-страниц в самом браузере пользователя. Ограничение хранилища обычно составляет 5мб на домен, что вполне достаточно для хранения SVG-спрайта.
Далее представляю JavaScript код, который позволяет это осуществить. И работает он следующим образом:
При первой загрузке сайта скрипт:
- Считывает содержание SVG-спрайта;
- Включает считанные данные в HTML-документ;
- Записывает данные в localStorage.
При каждой следующей загрузке сайта:
- Считывает данные из localStorage;
- Включает данные (SVG-графику) в HTML-документ.
Если localStorage не поддерживается браузером, выключен или переполнен, то скрипт всё равно считывает SVG-файл и включает данные в HTML-документ.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
;( function( window, document ) { 'use strict'; var file = 'img/svg.html', revision = 1; if( !document.createElementNS || !document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ).createSVGRect ) return true; var isLocalStorage = 'localStorage' in window && window[ 'localStorage' ] !== null, request, data, insertIT = function() { document.body.insertAdjacentHTML( 'afterbegin', data ); }, insert = function() { if( document.body ) insertIT(); else document.addEventListener( 'DOMContentLoaded', insertIT ); }; if( isLocalStorage && localStorage.getItem( 'inlineSVGrev' ) == revision ) { data = localStorage.getItem( 'inlineSVGdata' ); if( data ) { insert(); return true; } } try { request = new XMLHttpRequest(); request.open( 'GET', file, true ); request.onload = function() { if( request.status >= 200 && request.status < 400 ) { data = request.responseText; insert(); if( isLocalStorage ) { localStorage.setItem( 'inlineSVGdata', data ); localStorage.setItem( 'inlineSVGrev', revision ); } } } request.send(); } catch( e ){} }( window, document ) ); |
Вы можете разместить этот скрипт в любом месте страниц вашего сайта, т.к. считывание SVG-файла происходит асинхронно. Если разместить код в начале страницы, то SVG-графика будет подгружаться заметно быстрее.
Конфигурация скрипта
В скрипте есть лишь две строки для настройки.
1 2 |
var file = 'img/svg.html', // Путь к SVG-спрайту revision = 1; // Номер версии SVG-спрайта |
Я не использую SVG-спрайты с расширением .svg
, вместо этого я храню его как HTML-документ. Мой svg.html
обычно выглядит так:
1 2 3 4 5 6 7 8 |
<svg style="display: none;" aria-hidden="true"> <symbol id="svg-plane" viewbox="0 0 510 510"><path d="..." /></symbol> <symbol id="svg-close" viewbox="0 0 357 357"><path d="..." /></symbol> <symbol id="svg-fav" viewbox="0 0 510 510"><path d="..." /></symbol> <symbol id="svg-share" viewbox="0 0 459 459"><path d="..." /></symbol> <symbol id="svg-cart" viewbox="0 0 510 510"><path d="..." /></symbol> <symbol id="svg-tick" viewbox="0 0 510 510"><path d="..." /></symbol> </svg> |
В переменной revision
вы можете менять номер версии SVG-спрайта. От нее зависит, то когда скрипт должен просканировать файл на изменения и заменить старые данные в localStorage пользователя на новые. Например, при изменении SVG-файла, вы также должны изменить значение переменной revision
, чтобы изменения стали видны на сайте.
Вы можете обновлять версию вручную или автоматизировать это с помощью PHP (Python, Ruby или любого другого серверного ЯП). Если вы поместили код скрипта прямо в HTML код (который находится в PHP файле), то вы можете использовать PHP функцию, которая возвращает время изменения файла (в качестве временной метки Unix).
1 |
revision = <?=filemtime( 'img/svg.html' )?>; |
Если же вы сохранили скрипт в отдельном файле, например setLocalStorage.js, то можно воспользоваться глобальной переменной JavaScript.
1 |
revision = INLINE_SVG_REVISION; |
1 2 3 4 5 6 7 8 9 |
<html> <head> <script>var INLINE_SVG_REVISION = <?=filemtime( 'img/svg.html' )?>;</script> </head> <body> <!-- ... --> <script src="common.js"></script> </body> </html> |
Отсутствие поддержки SVG в браузере
Если браузер не поддерживает SVG, то мы можем рассчитывать на растровые изображения и JS fallback. Нам понадобится атрибут data-img
для определения пути к изображению, например:
1 |
<li><svg><use xlink:href="#svg-cart" data-img="img/cart.png"></use></svg></li> |
JS fallback сконвертирует предыдущую строку в эту:
1 |
<li><img src="img/cart.png" alt="" /></li> |
Затем, если вы не используете html5shiv, вы должны зарегистрировать HTML теги <svg>
и <use>
, чтобы fallback мог работать с ними. Следующий код должен находится в начале HTML-документа, лучше до первого появления тега <script>
:
1 2 3 4 5 |
if( !document.createElementNS || !document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ).createSVGRect ) { document.createElement( 'svg' ); document.createElement( 'use' ); } |
В заключении нужно добавить сам код JS fallback, который должен быть помещен в конце HTML-документа (Я позаимствовал несколько строк со страницы svg4everybody).
1 2 3 4 5 6 7 8 9 10 11 |
;( function( window, document ) { if( document.createElementNS && document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ).createSVGRect ) return true; var uses = document.getElementsByTagName( 'use' ), use; while( ( use = uses[ 0 ] ) ) { var svg = use.parentNode, img = new Image(); img.src = use.getAttribute( 'data-img' ); svg.parentNode.replaceChild( img, svg ); } }( window, document ) ); |