Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SVG 'use' tag in Chrome broken

There is SAP (AngularJS and Angular Route) with icon-based navigation made by svg-sprite. So, I hava inline code like this:

<div style="height: 0; width: 0; position: absolute; visibility: hidden">
<svg xmlns="http://www.w3.org/2000/svg">
    <symbol id="icon-grid-32" viewBox="0 0 32 32">
        <g stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" stroke-linejoin="round">
            <path d="M2 2h11v11H2zM19 2h11v11H19zM2 19h11v11H2zM19 19h11v11H19z"/>
        </g>
    </symbol>
</svg>
</div>

And the icons in navigation like this:

<a href=""><svg class="icon icon-32 outline black"><use xlink:href="#icon-grid-32"></use></svg></a>

All that I can see in this navigation is nothing, <use> has size 0 × 0 pixels. I know about Firefox bug with xml:base, but adding xml:base didn't help me. You can try this example: http://css.yoksel.ru/assets/demo/svg-in-firefox/svg-has-base.html

It works in Firefox, Safari and other browsers but not in Chrome 49+ (48 version doesn't have this problem).

like image 750
Vitaly Ovsyannikov Avatar asked Mar 16 '16 12:03

Vitaly Ovsyannikov


People also ask

How to display SVG image in Chrome browser?

Peoples are facing issues that they aren’t able to see their images in Chrome browser because the images are in SVG format. So I found a solution to display SVG image in Chrome browser with <object> tag. <object data="/images/yourlogo.svg" type="image/svg+xml" width="100" height="100" class="mylogo"></object>

How does the <use> element work in SVG?

The <use> element takes nodes from within the SVG document, and duplicates them somewhere else. The effect is the same as if the nodes were deeply cloned into a non-exposed DOM, and then pasted where the use element is, much like cloned template elements in HTML5.

Can I use SVG attributes in CSS?

Note: Starting with SVG2, x, y, width, and height are Geometry Properties, meaning those attributes can also be used as CSS properties for that element. Tip: you can click/tap on a cell for more information. Deprecated. Not for use in new websites.

What are inlined SVG sprites?

Inlined SVG sprites are the icons of tomorrow. If you haven’t used them. You should. Read this post for more info on using inline SVGs as your Icons.


2 Answers

This is caused by a combination of AngularJS' dependency of <base href="/" /> and UI routing, when the application is not at its "root" state, the relative hash link in the <use> element would not correctly resolve.

To get around this, you would need to resolve the xlink:href to use absolute path. You may do the following:

angular-icon-template.html

<svg class="c-icon" viewBox="0 0 32 32">
    <use xlink:href="" ng-attr-xlink:href="{{baseUrl + '#' + iconName}}" />
</svg>

angular-icon.js

angular.module('angularIcon', [])
    .directive('angularIcon', {
        templateUrl: 'angular-icon-template.html',
        scope: { iconName: '@' },
        controller: function($scope) {
            $scope.baseUrl = window.location.href.replace(window.location.hash, '');
        }
    });
like image 118
Tianzhen Lin Avatar answered Sep 27 '22 20:09

Tianzhen Lin


I was experiencing really similar issue to what you describe with a difference that I would generate my icons <svg> and <use> in a directive.

I have been looking for an answer for a better part of today and came up with a workaround to the <use> and xlink:href question. Which simply recreates the functionality by inlining the wanted svg.

For the sake of simplicity let's say i have <angular-icon> directive that receives the name of the icon by an attribute icon-name

<angular-icon icon-name="{{someObject.iconName}}">

working directive now looks as follows:

angular.module('angularIcon', [])
.directive('angularIcon', IconDirective);

function IconDirective(){
    return{
        template:'',
        templateNamespace:'svg',

        link:function(scope, element, attributes){

            // my icon name comes from $http call so it doesnt exist when initialising the directive, 
            attributes.$observe( 'iconName', function(iconName){

                // let's grab the icon from the sprite
                var icon = angular.element( document.getElementById( iconName ) ); 
                // let's clone it so we can modify it if we want
                var iconClone = icon.clone();

                var namespace = "http://www.w3.org/2000/svg";

                // manually create the svg element and append the inlined icon as no other way worked
                var svg = document.createElementNS( namespace, 'svg' );
                svg.setAttribute( 'viewBox', '0 0 32 32' );
                svg.setAttribute( 'xml:space', 'preserve' );

                svg.appendChild( iconClone[0] );
                // let's add the newly created svg+icon to the directive's element
                element[0].appendChild( svg );

            });

        },
        scope:{
            iconName: '@'
        }
    }
}
like image 27
nathanel Avatar answered Sep 27 '22 18:09

nathanel