Окончательное кроссбраузерное решение для SVG

Jun 22, 2010 13:05


Я уже касался этой темы и давал ссылку на решение. Оно, однако же, не вполне хорошо. Во-первых, оно не валидно, во-вторых, рассчитано на какие-то допотопные браузеры, коими уже никто не пользуется, но, в-третьих, не работает с современными браузерами (строго говоря, оно вообще не работает в том виде, в каком дано).

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

Для начала, есть проблема вот с этой строчкой:

Функция в ней не выполняется, если только мы не перенесём её вызов на новую строчку. Только в XHTML экранирование будет выполняться ещё сложнее, посему вынесем-ка мы вызов функции во внешний скрипт:

Идём дальше. Нынешний код считает, что Firefox, Chrome и Safari для отображения SVG нуждаются в плагине, а Opera в этом отношении вообще безнадёжна. Однако, Opera имеет нативную поддержку SVG с версии 8.0 (19 апреля 2005 г.), Firefox - с версии 1.5 (30 ноября 2005 г.), Safari - с версии 3.0 (11 июня 2007 г.), а Chrome - с рождения (2 сентября 2008 г.). Фактически, все ухищрения нужны только для «Просмотрщика Интернета», сиречь Internet Explorer, у коего такая поддержка ожидается лишь в 9-й версии.

Тогда все три подключенных скрипта можно скрыть от прочих браузеров в условный комментарий (ну и насчёт языка перестаём притворяться, что это - JavaScript):

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

В сумме получается довольно громоздкая конструкция, да ещё смешивающая представление с поведением. На самом деле можно просто написать:

А в подключаемом скрипте viewSVJ.js, если SVG-плагин всё-таки не установлен, будем перебирать элементы embed и заменять их на предупреждения со ссылками:

checkAndGetSVGViewer(); window.attachEvent( "onload", function(){ if(window.svgInstalled)//если SVG-плагин установлен return; var embeds=document.getElementsByTagName("embed"); for(var embedNumber=0, embedTypeAttr; embedNumberClick here for more information.
"; } } );
Но HTML-код всё равно выглядит чрезмерно раздутым. Ведь условный комментарий с элементом embed надо писать для каждой SVG-картинки, а ссылки на несколько скриптов (и тоже с условным комментарием) - для каждой содержащей их страницы! Конечно, существует шаблонизация, но… Вдобавок, эти скрипты замусоривают глобальное пространство имён, а лезть разбираться в их устаревший код не хочется.

Укротим сперва скрипты. Будем цеплять к странице один скрипт fixSVG.js, куда пихнём весь фикс для Webkit-браузеров, а для IE напишем свою ветку, в которой будем подгружать в динамически созданный плавающий фрейм скрипты от «Эдоуби» (Adobe), выполнять с их помощью проверку на наличие установленного SVG-плагина, если он не установлен, подменять картинки на соответствующие уведомления со ссылкой, и, наконец, удалять этот фрейм. Детали реализации смотрим ниже в окончательном решении.

Наконец, от тяжеловесной конструкции на месте каждой картинки можно избавиться, если пожертвовать браузерами IE с установленным SVG-плагином, но отключенным JScript. Я склонен решительно пожертвовать. Всем известно, насколько этот браузер требует костылей для соблюдения стандартов, так что если человек отключает в нём скрипты, он молчаливо соглашается видеть Интернет «условно».

Можно писать просто:

Впрочем, если кому-то вышеописанный вариант дорог, как память, можно и так:

Затем для IE мы должны подменять элемент object на элемент embed, если SVG-плагин установлен, и на уведомление со ссылкой, если он ещё не установлен.

В заголовочной части страницы, как уже указано, пишем:

Файл fixSVG.js выглядит так (с использованием уже скриптового условного комментария):

/*@cc_on if(@_jscript_version<9) window.attachEvent( "onload", function(){ var iframe=document.createElement("iframe"); iframe.src="js/fixSVG_IE_5-8.html"; document.body.appendChild(iframe); } ); @*/ if(/AppleWebKit/.test(navigator.userAgent)) window.addEventListener( "load", function(){ var objects=document.getElementsByTagName("object"); for(var objectNumber=objects.length, objectNode, codebase, imageNode, attrNumber, attrNode; objectNumber--;){ objectNode=objects[objectNumber]; if(objectNode.getAttribute("type")==="image/svg+xml"){ imageNode=document.createElement("img"); codebase=objectNode.getAttribute("codebase"); imageNode.setAttribute("src", ((null===codebase)?"":codebase)+objectNode.getAttribute("data")); imageNode.setAttribute("alt", "SVG");//совсем без alt нельзя по стандарту HTML for(attrNumber=objectNode.attributes.length; attrNumber--;){ attrNode=objectNode.attributes[attrNumber]; if(-1===["declare", "classid", "codebase", "data", "type", "codetype", "archive", "standby", "tabindex"].indexOf(attrNode.name)) imageNode.setAttribute(attrNode.name, attrNode.value); } objectNode.parentNode.replaceChild(imageNode, objectNode); } } }, false );
Содержимое fixSVG_IE_5-8.html, подгружающееся в динамически созданный плавающий фрейм:

Fix SVG for IE 5-8
Скрипты svgcheck.js и svgcheck.vbs - адоубивские, нетронутые. А fixSVG_IE_5-8.js - наш:

checkAndGetSVGViewer();//проверяем наличие SVG-плагина if(window.svgInstalled){ var objects=window.parent.document.getElementsByTagName("object"); for(var objectNumber=objects.length, objectNode, objectTypeAttr, embedNode, codebase, attrNumber, attrNode; objectNumber--;){ objectNode=objects[objectNumber]; objectTypeAttr=objectNode.attributes["type"]; if(objectTypeAttr=="image/svg+xml" || objectTypeAttr.value=="image/svg+xml"){ embedNode=window.parent.document.createElement("embed"); embedNode.setAttribute("type", "image/svg-xml"); embedNode.setAttribute("pluginspage", "http://www.adobe.com/svg/viewer/install/"); embedNode.setAttribute("wmode", "transparent");//обеспечиваем прозрачность фона codebase=objectNode.getAttribute("codebase"); embedNode.setAttribute("src", ((null===codebase)?"":codebase)+objectNode.getAttribute("data")); for(attrNumber=objectNode.attributes.length; attrNumber--;){ attrNode=objectNode.attributes[attrNumber]; if(//сюрприз! IE не поддерживает indexOf! attrNode.name!="archive" && attrNode.name!="classid" && attrNode.name!="codebase" && attrNode.name!="codetype" && attrNode.name!="data" && attrNode.name!="declare" && attrNode.name!="standby" && attrNode.name!="tabindex" && attrNode.name!="type" && attrNode.name!="usemap" ) embedNode.setAttribute(attrNode.name, attrNode.value); } objectNode.parentNode.replaceChild(embedNode, objectNode); } } } else if(window.svgViewerAvailable){//для проформы (мы знаем, что для IE 5-8 такой плагин есть!) var message; switch(navigator.browserLanguage.substr(0,2)){ case "ru": message="Для просмотра этой страницы нужен"; break; case "en": default: message="To view this page you need an"; } var objects=window.parent.document.getElementsByTagName("object"); for(var objectNumber=objects.length, objectTypeAttr; objectNumber--;){ objectTypeAttr=objects[objectNumber].attributes["type"];//objects[objectNumber].getAttribute("type")===null! if(objectTypeAttr=="image/svg+xml" || objectTypeAttr.value=="image/svg+xml")//тут учтён IE5 objects[objectNumber].outerHTML="
"+message+" SVG viewer.
"; } } window.frameElement.parentNode.removeChild(window.frameElement);//удаляем плавающий фрейм, вызвавший этот скрипт
Вот так. Только помним при стилизации и скриптовании, что в Firefox, Opera и, очевидно, IE 9 будет элемент object, в Safari и Google Chrome (надеюсь, когда-нибудь везде) - img, а в IE 5-8 - embed.

Реализация этого решения развёрнута, для примера, на сайте РГФ. На самом деле, там оно лишено большого практического смысла. Исходные картинки и в GIF-формате были просты и невелики, а стали бы ещё меньше при конвертации в PNG. Выигрыш в размере тут можно было бы получить разве что при использовании сжатых gzip-ом SVGZ, но я до сих пор бодаюсь с провайдером по поводу нежелания сервера отдавать нужный для этого HTTP-заголовок. И вообще, с семантической точки зрения, тут просится не SVG, а внедрённые шрифты, чем я тоже хотел бы заняться в дальнейшем.

Web-вёрстка, ie, jscript, svg

Previous post Next post
Up