📜 ⬆️ ⬇️

To the issue of cross-browser SVG usage

For vector graphics on the Internet, the SVG format is the most. First, it supports scaling of any degree. Secondly, it is possible to refer to any constituent elements of such a picture - to address them, to stylize and script. Thirdly, with the exception of very small files, this format wins in compactness before any raster representations, especially if gzip compression is used. Fourth, this is the W3C standard .

But how to put the SVG- picture in the HTML- document?

How to embed SVG in HTML


')
It is known that there are three ways:



The first surprise gives us ... guess which browser? Well, of course, this IE doesn’t allow the SVG image embedded by the object tag to be scripted and fills its transparent background with white! Fortunately, it provides its proprietary embed tag for this purpose. Through conditional comments and style management, you can easily and validly ensure that normal browsers will display SVG via an object element, and IE through an embed . But will it?

The second surprise: will not be until we install the plugin - for example, Adobe SVG Viewer . True, Adobe , it seems, has stopped supporting it since January 1, 2009, but what has been done so far should suffice, and, in the end, there is also RENESIS Player 1.0 for Internet Explorer and Ssrc SVG Plugin . In addition, no matter how hard Microsoft slashes over its VML , with which the W3C threw it in 1999, introducing native support for the SVG is being ruined by authorities such as Google and the Wikimedia Foundation . The encouraging news that “ Microsoft has submitted an application to join the working group of the development of the SVG format in the W3C organization ” on Habré has already been discussed . So there is every reason for optimism.

Further. In the descriptions of the embed tag in many places on the Internet, you can read that it has such a wonderful pluginspage attribute that indicates the address that supposedly " will be sent to the user if his browser does not support SVG graphics ."

Do not believe! This is the third surprise: in reality, IE ignores this attribute.

And in order to make it work, there is a very little known (judging by the total lack of references in thematic materials on the Internet) a clever way using scripts in JScript and VBScript . In principle, nothing terrible, but you need to know about it.

Adobe Upgrade Solution



The solution is, however, not quite good. Firstly, it is not valid, and secondly, it is designed for some antediluvian browsers that no one uses, but, thirdly, it does not work with modern browsers (strictly speaking, it does not work at all in the form what is given). However, the basic idea of ​​the solution is correct, so we will try to make it workable and up-to-date.

To begin with, there is a problem with this line:

 <script language="JavaScript"><!-- checkAndGetSVGViewer(); // --> </script> 


The function in it is not executed, unless we transfer its call to a new line. Only in XHTML screening will be performed even more difficult, therefore, we make a function call to an external script:

 <script language="JavaScript" src="viewSVJ.js"></script> 


Go ahead. The current code believes that Firefox, Chrome and Safari need a plugin to display SVG , and Opera is completely hopeless in this respect. However, Opera has native support for SVG from version 8.0 (April 19, 2005), Firefox from version 1.5 (November 30, 2005), Safari from version 3.0 (June 11, 2007), and Chrome from birth ( September 2, 2008). In fact, all the tricks are needed only for the Internet Browser, that is, Internet Explorer, whose support is expected only in version 9.

Then all three connected scripts can be hidden from other browsers in a conditional comment (well, as for the language, we stop pretending that this is JavaScript):

 <!--[if IE]> <script language="JScript" src="svgcheck.js"></script> <script language="VBScript" src="svgcheck.vbs"></script> <script language="JScript" src="viewSVJ.js"></script> <![endif]--> 


Where the SVG image is directly connected, we can conclude the recommended code in the same comment, and then write something like this:

 <!--[if !IE]>--> <object type="image/svg+xml" data="hello.svg" height="200" width="600"></object> <!--<![endif]--> 


In sum, a rather cumbersome construction is obtained, and, moreover, a confusing presentation with behavior. In fact, you can simply write:

 <!--[if IE]> <embed src="hello.svg" height="100" width="100" type="image/svg-xml" pluginspage="http://www.adobe.com/svg/viewer/install/"> <![endif]--> <!--[if !IE]>--> <object type="image/svg+xml" data="hello.svg" height="100" width="100"></object> <!--<![endif]--> 


And in the viewSVJ.js plugin script, if the SVG plugin is still not installed, we will go through the elements of the embed and replace them with warnings with links:

 checkAndGetSVGViewer(); window.attachEvent( "onload", function(){  if(window.svgInstalled)// SVG-    return;  var embeds=document.getElementsByTagName("embed");  for(var embedNumber=0, embedTypeAttr; embedNumber<embeds.length; embednumber++){   embedtypeattr=embeds[embedNumber].attributes["type"];   if(embedtypeattr="image/svg-xml" /*MSIE 5*/ || embedtypeattr.value="image/svg-xml")    embeds[embednumber].outerhtml="<p>To view this page you need an SVG viewer. <a href=\""+getSVGInstallPage()+"\">Click here</a> for more information.</p>";  } } ); 


But the HTML code still looks overly bloated. After all, a conditional comment with an embed element should be written for each SVG picture, and links to several scripts (and also with a conditional comment) for each page containing them! Of course, there is a standardization, but ... In addition, these scripts litter the global namespace, and I don’t want to go into sorting out their obsolete code.

Tame the scripts first. We will hook fixSVG.js page one fixSVG.js script, where for IE we will write our branch, in which we will load into a dynamically created floating frame (yes, I snorted at their address, but this is the same solution for the browser, which has not been better yet!) scripts from "Edoubi" (Adobe); use them to check for the presence of an installed SVG plugin; if it is not installed, replace the images with the corresponding notifications with a link; and, finally, delete this frame (for the sake of purity of the page). Implementation details see below.

Finally, you can get rid of the heavy construction in place of each image by donating IE to the browser with the SVG plug-in installed but JScript disabled. I tend to resolutely sacrifice. Everyone knows how much this browser requires crutches to comply with standards, so if a person disables scripts in him, he tacitly agrees to see the Internet "conditionally."

You can write simply:

 <object type="image/svg+xml" data="hello.svg"></object> 


However, if someone described the above option of roads, as a memory, it is possible and so:

 <object type="image/svg+xml" data="hello.svg">     <a href="http://www.adobe.com/svg/viewer/install/">SVG viewer</a>.</object> 


Then for IE, we have to replace the object element with an embed element if the SVG plugin is installed, and with a notification with a link if it is not already installed.

In the header of the page, as already indicated, we write:

 <script type="text/ecmascript" src="fixSVG.js"></script> 


The fixSVG.js file looks like this (using an already scripted conditional comment):

 /*@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);   }  ); @*/ 


The contents of fixSVG_IE_5-8.html , loaded into a dynamically created floating frame:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head>  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>  <title>Fix SVG for IE 5-8</title>  <script type="text/jscript" src="svgcheck.js"></script>  <script type="text/vbscript" src="svgcheck.vbs"></script>  <script type="text/jscript" src="checkSVGViewer.js"></script> </head> <body></body> </html> 


The svgcheck.js and svgcheck.vbs are adware, intact. And fixSVG_IE_5-8.js is ours:

 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="<p>"+message+" <a href=\""+getSVGInstallPage()+"\">SVG viewer</a>.</p>";  } } window.frameElement.parentNode.removeChild(window.frameElement);//  ,    


SVG graphics in Webkit browsers



Google Chrome and Safari, however, reveal two problems. For a start, they do not provide proportional scaling of the SVG picture in general. The 4th version for this required that the svg element be given the width and height attributes, and the 5th, on the contrary, that they were not set! But at the same time scaling is not quite satisfactory.

The second problem is more serious: if the SVG-pictures inserted through the object SVG-images have a transparent background, it is filled with white (this is a bug ! And it is very old, more than two years old, and in 5 versions of these browsers it is not fixed), as in IE (but there we can simply add an embed element to the wmode attribute with the value transparent ).

If you use not object , but img , then everything is fine, but so far Firefox does not understand this way of inserting the SVG image. Therefore, we write a script for Webkit-browsers in the above-mentioned fixSVG.js file (accepting the white background in the abnormal situation of disabled Javascript), replacing the loaded object document with the SVG-image on img :

 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 ); 


Alas, the scripts stop working on the images inserted in this way, so the solution is not universal!

Like this. Just remember when styling and scripting that in Firefox, Opera and, obviously, IE 9 will be an object element, in Safari and Google Chrome (I hope sometime everywhere) - img , and in IE 5—8 - embed .

Scripting embedded SVG images



In advanced browsers, accessing an SVG document from an HTML document is trivial. On the load event, the object element is available to us, we can also hang a load event on it, and in its handler we will get the required one in a remarkable way. With jQuery, this could be done like this:

 $( function(){  $("object#SceletonObject").load(   function(){    var SVGDocument=$(this)[0].getSVGDocument();    …   }  ); } ); 


Problems arise in Internet Explorer. After we load the page, we can get an embed element in it and it even has a SVG document already available, but that's the point! - it is completely empty. I could not embed load event to the embed element; it seems that it is not supported at all (however, those of the supposedly supported events that I have tried do not work either).

In addition, another creep has emerged: the contents of elements such as object and embed loaded asynchronously , in a separate thread. This means, as I understand that, on the one hand, until the parsing of the main page is completed, the element is not yet available for attaching an event handler to it, and when it is finished, the element load event may already work and then the handler is already useless. It seems that it was the last case that I occasionally observed in experiments without catching any regularity in the glitch.

And by the way, by the way, the load event for the object element is missing - it is in HTML only for the body element. So the fact that it even works somewhere is wrong!

The output is found as follows (not very elegant, but ... let's still think!): We write a load event inside the SVG code of the svg element, calling the function HTML document from there. Somehow:

 <svg xmlns="http://www.w3.org/2000/svg" onload="InitSVG(document);"> 


Well, or, so that, so, consistently worked in all browsers:

 <svg xmlns="http://www.w3.org/2000/svg" onload="if('InitSVG' in window) InitSVG(document); else parent.InitSVG(document);"> 


Or even simply (since parent in the top-level window points to this window itself):

 <svg xmlns="http://www.w3.org/2000/svg" onload="parent.InitSVG(document);"> 


In the InitSVG function we can already get the SVG document and from it all its elements. Lepote!

Only it should be borne in mind that working with SVG elements using jQuery will not work (maybe there is some workaround, but I haven’t been able to dig up or invent it so far and I personally thought that it wasn’t worth making a game). The fact is that its functions expect to get from the getElementsByTagName method a result of the HTMLCollection type, from which you can select elements by index, as from a regular array. And here we get (everywhere except Firefox) a result of the NodeList type, from which the item can only be pulled out using the special item( ) method item( ) . The framework does not count on this and breaks off (in any case, it was so in the fall, when I experimented with it). So you have to work directly with DOM methods, keeping cross-browser compatibility in mind. This is stressful, but possible.

Two private observations on the scripting SVG -document. First, it turned out that the styles specified in it cannot be changed by the script; All dynamics should be implemented through attributes (which, of course, is not very correct when it comes to design, for example, the color fill - fill ). Secondly, if the SVG element does not have any fill, then the mouseover and mouseout work only on the frame and may well not have time to work at all (fortunately, you can write the fill attribute once on the ancestor element and mouseout ).

It's funny that this method stopped working locally in Chrome 5.0 due to some of its security problems. It’s not at all funny that, as already indicated, it doesn’t work in Webkit browsers at all if we use the img tag.

How to give from SVGZ server



SVG graphics are recommended to gzip . It is correct, the file size can then become three to four times smaller. If someone, like me, does not want to mess around with the gzip utility launched from the command line, he can use the 7-Zip archiver for this purpose, which is not less free, but endowed with a graphical interface (in the archive settings, you must select the Gzip method!).

Archived, changing the extension from the automatically .svg.gz to .svgz , then with amazement we see that the resulting file is adequately perceived only by Opera and Internet Explorer, and Firefox, Safari and Chrome turn up the nose, cursing the syntax of XML . Well, of course, what is XML there until you unzip it? And why, in fact, they do not unzip?

It turns out apache is to blame. Until now (version 2.2.12), in the default configuration, it sends the Content-Type: image/svg+xml header required for SVG format to the browser Content-Type: image/svg+xml , but the Content-Encoding: gzip header Content-Encoding: gzip not required for its gzip version. Therefore, we will fix the server configuration or simply write the line “ AddEncoding gzip .svgz ” to the .htaccess file.

I also met another advice to add the following (for the sake of Firefox) the following:

 <FilesMatch \.svgz$> <IfModule mod_gzip.c>  mod_gzip_on No </IfModule> </FilesMatch> 


Perhaps it makes sense if the server is configured to automatically gzip the output files; I do not know.

Recently, I had on this issue a funny butt with tech support from my host, which lasted a month. I understand that there is a nginx front-end in front of Apache, which in some way limits the ability to use Apache directives. More specifically, the AddType directive works, but AddEncoding does not. Of course, the “Help” section on the provider's website does not contain a word about this quirk, or even even a single mention of nginx. Technical support stubbornly did not want to listen to me and give out their secrets, giving me insane replies: “The title is not given, because the server does not have the necessary settings for
the work of the AddEncoding directive is the Apache Mod_gzip module, “nginx is responsible for compression, the other Apache directives are fully supported” (not true, by the way), “We apologize. You can transfer the svgz format only if there is a mod_deflate module, which, unfortunately, is not available on the servers you use the services ”(and then there is an offer to buy a five more expensive plan!). In the end, they didn’t explain to me what they wanted, but they gave up and began to give the correct title themselves.

Source: https://habr.com/ru/post/99143/


All Articles