Giter VIP home page Giter VIP logo

svg-inject's Introduction

minified size gzip size npm version

SVGInject

A tiny, intuitive, robust, caching solution for injecting SVG files inline into the DOM.

Developed and maintained by INCORS, the creators of iconfu.com.

What does it do?

SVGInject replaces an <img> element with an inline SVG. The SVG is loaded from the URL specified in the src attribute of the <img> element.

SVG Injection

Element before injection:

<img src="image.svg" onload="SVGInject(this)" />

Element after injection (SVG loaded from image.svg):

<svg version="1.1" ...> ... </svg>

Why should I use it?

An SVG can only be properly styled with CSS and accessed with Javascript on element level if the SVG is inline in the DOM. With SVGInject you can keep your SVGs as individual files, but still access them with CSS and Javascript.

How to install SVGInject?

Manually

Include the SVGInject Javascript file in the <head> element of the HTML document, or anywhere before the first usage of SVGInject

<head>
  ...
  <script src="svg-inject.min.js"></script>
  ...
</head>

Download plain version (v1.2.3): svg-inject.js

Download minified version (v1.2.3): svg-inject.min.js

npm

If you are using npm

$ npm install @iconfu/svg-inject

Yarn

If you are using Yarn

$ yarn add @iconfu/svg-inject

Basic usage

Option 1 - Call SVGInject from the onload attribute

Add onload="SVGInject(this)" to any <img> element where you want the SVG to be injected.

For most use cases this approach is recommended and provides nice advantages.

<html>
<head>
  <script src="svg-inject.min.js"></script>
</head>
<body>
  <img src="image1.svg" onload="SVGInject(this)" />
  <img src="image2.svg" onload="SVGInject(this)" />
</body>
</html>

Option 2 - Call SVGInject from anywhere

<html>
<body>
  <img src="image1.svg" class="injectable" />
  <img src="image2.svg" class="injectable" />

  <script src="svg-inject.min.js"></script>
  <script>
    SVGInject(document.querySelectorAll("img.injectable"));
  </script>
</body>
</html>

Hooray 🎉 - The SVGs get injected and are styleable!!!


What are the advantages?

  • Wide browser support: Works on all browsers supporting SVG. Yes, this includes Internet Explorer 9 and higher! (full list)

  • Fallback without Javascript: If Javascript is not available the SVG will still show. It's just not styleable with CSS.

  • Fallback if image source is not available: Behaves like a normal <img> element if file not found or not available.

  • Prevention of ID conflicts: IDs in the SVG are made unique before injection to prevent ID conflicts in the DOM.

What are additional advantages when using onload?

The recommended way to trigger injection is to call SVGInject(this) inside the onload attribute:

<img ... onload="SVGInject(this)" />

This provides additional advantages:

  • Intuitive usage: Insert SVG images into HTML code just as PNG images, with only one additional instruction. It's very clear to understand what it does by looking at the pure HTML.

  • Works with dynamic content: If <img> elements are added dynamically, injection still works.

  • Built-in prevention of unstyled image flash: SVGInject hides <img> elements until injection is complete, thus preventing a brief flicker of the unstyled image (called unstyled image flash).

  • Early injection: The injection can already start before the DOM content is fully loaded.

  • Standard-conform: The onload event handler on <img> elements has long been supported by all browsers and is officially part of the W3C specification since HTML5.0.

  • Great load performance: Browers load the SVG right when the <img> element is parsed in the DOM. After the onload event the SVG is available from the browser cache in the subsequent XHR request and therefor each SVG is only loaded once.

If you do not want to use the onload attribute but prefer to inject SVGs directly from Javascript, you can find more information here.

What are the limitations?

SVGInject is intended to work in production environments however it has a few limitations you should be aware of:

  • The image source must conform to the same-origin policy, which basically means the image origin must be where the website is running. This may be bypassed using the Cross-Origin Resource Sharing (CORS) mechanism.
  • Due to the same-origin policy SVGInject does not work when run from the local file system in many browsers (Chrome, Safari), yet in Firefox it works.

How are attributes handled?

All attributes are copied from the <img> element to the injected <svg> element before injection with the following exceptions:

  • The src, title, alt, onerror and onload attributes are not copied
  • The title attribute is transformed to a <title> element and inserted as the first child element of the injeted SVG. If there is an existing <title> element as first child element it gets replaced.

You can disable the previously described attribute handling by setting the copyAttributes option to false. You may also implement your own attribute handling in the beforeInject options hook.

Additionally, after loading the SVG, the value of the src attribute of the <img> element is transformed to an absolute URL and inserted as a data-inject-url attribute.

API

Function Description
SVGInject(img, options) Injects the SVG specified in the src attribute of the specified img element or array of elements. The optional second parameter sets the options for this injection. Returns a Promise object which resolves if all passed in img elements have either been injected or failed to inject (Only if a global Promise object is available like in all modern browsers or through a Promise Polyfill).
SVGInject.setOptions(options) Sets the default options for SVGInject.
SVGInject.err(img, fallbackSrc) Used in onerror event of an <img> element to handle cases when loading of the original source fails (for example if the file is corrupt or not found or if the browser does not support SVG). This triggers a call to the option's onFail hook if available. The optional second parameter will be set as the new src attribute for the img element.

Options

Property name Type Default Description
useCache boolean true If set to true the SVG will be cached using the absolute URL. The cache only persists for the lifetime of the page. Without caching images with the same absolute URL will trigger a new XMLHttpRequest but browser caching will still apply.
copyAttributes boolean true If set to true attributes will be copied from the img to the injected svg element. You may implement your own method for copying attributes in the beforeInject options hook.
makeIdsUnique boolean true If set to true all IDs of elements in the SVG are made unique by appending the string "--inject-X", where X is a running number which increases with each injection. This is done to avoid duplicate IDs in the DOM. If set to false, all IDs within the SVG will be preserved.
beforeLoad function(img) undefined Hook before SVG is loaded. The img element is passed as a parameter. If the hook returns a string it is used as the URL instead of the img element's src attribute.
afterLoad function(svg, svgString) undefined Hook after SVG is loaded. The loaded svg element and svgString string are passed as a parameters. If caching is active this hook will only get called once for injected SVGs with the same absolute path. Changes to the svg element in this hook will be applied to all injected SVGs with the same absolute path. It's also possible to return an new SVG string or SVG element which will then be used for later injections.
beforeInject function(img, svg) undefined Hook directly before the SVG is injected. The img and svg elements are passed as parameters. The hook is called for every injected SVG. If an Element is returned it gets injected instead of applying the default SVG injection.
afterInject function(img, svg) undefined Hook after SVG is injected. The img and svg elements are passed as parameters.
onAllFinish function() undefined Hook after all img elements passed to an SVGInject() call have either been injected or failed to inject.
onFail function(img, status) undefined Hook after injection fails. The img element and a status string are passed as an parameter. The status has one of the values: 'SVG_NOT_SUPPORTED' - the browser does not support SVG, 'SVG_INVALID' - the SVG is not in a valid format or 'LOAD_FAIL' - loading of the SVG failed.

If SVGInject is used with the onload attribute, onerror="SVGinject.err(this)" must be added to the <img> element to make sure onFail is called.

How does SVGInject prevent "unstyled image flash"

Before an SVG is injected the original unstyled SVG may be displayed for a brief moment by the browser. If a style is already applied to the SVG at runtime, the styled SVG will look different from the unstyled SVG, causing a brief “flashing” of the unstyled SVG before injection occurs. We call this effect unstyled image flash.

If SVGInject is used with the onload attribute, SVGInject has a built-in functionality to prevent unstyled image flash. A <style> element with one CSS rule is added to the document to hide all injectable <img> elements until injection is complete.

When using Javascript directly SVGInject has no build in functionality to prevent unstyled image flash. You can find a custom solution for this in the example for using SVGInject without the onload attribute.

How to use SVGInject directly from Javascript?

Instead of using the onload attribute on the <img> element you can also call SVGInject directly from Javascript.

Examples:

// inject by class name
SVGInject(document.getElementsByClassName('myClassName'));

// inject by id
SVGInject(document.getElementById('myId'));

You need to make sure the images are already inside the DOM before injection like this:

document.addEventListener('DOMContentLoaded', function() {
  // call SVGInject() from here
});

If you dynamically insert new <img> elements you need to call SVGInject() on these elements after their insertion.

Fallback for old browsers

If the browser does not support SVG (IE8 and IE7), this simple fallback solution replaces the src attribute with an alternative image URL.

<img src="image.svg" onload="SVGInject(this)" onerror="SVGInject.err(this, 'image.png')" />

More detailed information on implementing fallback solutions for old browsers can be found on our Fallback solutions WIki page.

What about some examples?

Here are some examples which cover the most common use cases.

Basic Example

This is the standard usage of SVGInject which works on all modern browsers, and IE9+.

<html>
<head>
  <script src="svg-inject.min.js"></script>
</head>
<body>
  <img src="image1.svg" onload="SVGInject(this)" />
  <img src="image2.svg" onload="SVGInject(this)" />
</body>
</html>

Example using options

This example shows how to use SVGInject with multiple options.

<html>
<head>
  <script src="svg-inject.min.js"></script>

  <script>
    SVGInject.setOptions({
      useCache: false, // no caching
      copyAttributes: false, // do not copy attributes from `<img>` to `<svg>`
      makeIdsUnique: false, // do not make ids used within the SVG unique
      afterLoad: function(svg, svgString) {
        // add a class to the svg
        svg.classList.add('my-class');
      },
      beforeInject: function(img, svg) {
        // wrap SVG in a div element
        var div = document.createElement('div');
        div.appendChild(svg);
        return div;
      },
      afterInject: function(img, svg) {
        // set opacity
        svg.style.opacity = 1;
      },
      onFail: function(img) {
        // set the image background red
        img.style.background = 'red';
      }
    });
  </script>
</head>
<body>
  <img src="image.svg" onload="SVGInject(this)" onerror="SVGInject.err(this)" />
</body>
</html>

Example without using the onload attribute

This example shows how to use SVGInject directly from Javascript without the onload attribute. After the DOM content has loaded, SVGInject is called on all elements with class injectable. It also implements a method to prevent unstyled image flash.

<html>
<head>
  <script src="svg-inject.min.js"></script>

  <!-- hide images until injection has completed or failed -->
  <style>
    /* hide all img elements until the svg is injected to prevent "unstyled image flash" */
    img.injectable {
      visibility: hidden;
    }
  </style>

  <script>
    SVGInject.setOptions({
      onFail: function(img, svg) {
        // if injection fails show the img element
        img.classList.remove('injectable');
      }
    });

    document.addEventListener('DOMContentLoaded', function() {
      // inject all img elements with class name `injectable`
      SVGInject(document.querySelectorAll('img.injectable'), {
        onAllFinish: function() {
          // the SVG injection has finished for all three images

        }
      });
    });
  </script>
</head>
<body>
  <img src="image_1.svg" class="injectable" />
  <img src="image_2.svg" class="injectable" />
  <img src="image_3.svg" class="injectable" />
</body>
</html>

Browser support

Full support for all modern browsers, and IE9+ (full list)

Support for legacy browsers with optional PNG fallback method

License

MIT License

svg-inject's People

Contributors

dasboe avatar iconfu avatar waruyama avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

svg-inject's Issues

SVG not showing correctly in Firefox

I've added the script in my project, and it's working fine in all browsers, except in Firefox, and I don't know why. My image-clickable.svg has clickable areas, the areas loads correctly in the correct position, but the image itself doesn't.
Nothing appear's on the console and the svg file is loading correctly. I'm using the script in an Angular app on version 6.1.4. I've tested on firefox version 64.0.2, 65.0 and Firefox Dev 66.0b3.
The problem occurs in development server, and on production.

I've added the script inside the <head> tag
<script src="svg-inject.min.js"></script>

I'm using it like the example in the README inside the <body>
<img src="image-clickable.svg" onload="SVGInject(this)">

IDs not replaced in <style> elements

Some graphics programs like Corel Draw export SVGs with the style embedded into a CDATA section inside a style element. Currently IDs inside style elements are not converted.

Here is an example SVG:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1024px" height="1024px" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 102400 102400"
 xmlns:xlink="http://www.w3.org/1999/xlink">
 <defs>
  <style type="text/css">
   <![CDATA[
    .fil0 {fill:url(#id0)}
   ]]>
  </style>
  <linearGradient id="id0" gradientUnits="userSpaceOnUse" x1="30009.4" y1="28336" x2="72690.6" y2="59764">
   <stop offset="0" style="stop-color:#1E87E3"/>
   <stop offset="1" style="stop-color:#E33834"/>
  </linearGradient>
 </defs>
 <g id="Ebene_x0020_1">
  <ellipse class="fil0" cx="51350" cy="44050" rx="31400" ry="31850"/>
 </g>
</svg>

Twice request

Option 1 - Call SVGInject from the onload attribute

Does this method cause the file to request twice?

Use same random ID suffix for injection of one SVG

Within an SVG the IDs must be unique. ID collision can only happen between two injected SVGs. Therefore, when making IDs unique, it is sufficient to use the same random suffix for each ID withing one injected SVG, which has the following benefits:

  • Only one random string has to be created for each injection, instead of creating a random string for each ID (slightly improved performance)
  • We can store the random suffix as an attribute in the injected SVG, which can then be accessed through Javascript. After injection the randomized IDs can easily be found by using their original ID and adding the suffix that can be read from the <svg> element. This way it is still possible to access the elements through Javascript after injection.

Chrome scrollbar issue

Recently noticed an issue on a website with SVGInject on Chrome (Mac & Windows). Occasionally the scrollbar disappears as soon as a page loads, and reappears scrolling down. Removing SVGInject resolves the issue. Any idea what might be causing this?

Implement a promise

Since I'm already suggesting stuff, I would also implement a promise.

I.e.
DOMContentLoaded > SVGInject > init or function (but only on a resolved callback).

Because currently the issue with both inits is that the svg inject takes around 10ms to inject.

Partly img effective

hi,guys,

i got some problem,please help me.

.tree-node-icon {
	width: $imgSize;
	margin-right: $imgMarginR;
}
.svg-color{
      g[transform] {
         fill: red;
      }
}

const iconClassName='tree-node-icon  svg-color'
	<img
			className={iconClassName}
			src={iconType}
			onError={noFind}
			onLoad={(event) => {
				SVGInject(event.target)
			}}
		/>

result:
image

calling SVGInject multiple times on an img tag when src is updated

So my vue component gets updated data, and tries to call SVGInject on a previously SVGInjected element.

However, nothing happens.

The first time I call SVGInject (from vue's mounted event), everything works fine and the afterInject handler was invoked just fine.

However, when the component needs to change the src of the image, what I did was replace children with a cloned HTMLElement img node, and then call SVGInject to them.

This time, afterInject was not invoked, even though the return value promise finished, and onAllFinish also was invoked.

I end up with the img component with svg source, which means they don't follow CSS rules I set on the page.

Note the black icons:
image

What am I missing?

(thanks in advance)

Does this work with React?

I'm trying to do something like:

import SVGInject from "@iconfu/svg-inject";

function Test() {
  return (
    <div>
             <img
                src={MySvg}
                onLoad={SVGInject(this)}
              />
    </div>
  );
}

Doesn't seem to be doing anything. I don't get an error, but the svg is still the same, and if I inspect it in console, it's still img instead of svg

makeIdsUnique not working properly

Hi
I am facing some issue.
reproduce step:

  • I embed svg by makeIdsUnique : false so svg file embeded correctly without id change.
  • reload browser, then id changed (--inject-- number attached to id)
    so when I clear cache, and first run is okay but second run after reload browser make id unique even makeIdsUnique:false
    what's wrong?
    Thanks

makeIdsUnique does not replace IDs for <path> references

I'm making use of this library in when dealing with SVG's exported directly from Sketch. We've had trouble with this in the past since Sketch uses very predictable ID names. I was really happy to see that you already support making IDs unique, but this (currently) does not apply to path elements.

SVGs exported from sketch often contain definition for a path in the <defs> section:

<defs>
  <path d="..." id="path-1"></path>
</defs>

These are then referenced later:

<mask id="mask-1" fill="white">
  <use xlink:href="#path-1"></use>
</mask>

I've modified the library to support this case and I'll submit a PR to accompany this issue shortly. Happy to discuss the best way to achieve this but I believe this would add to the already massive value of this project!

Inject SVG's in a bundle build

I'm also wondering if it's possible to make the SVG-inject available as a plugin for a bundler (e.g. ParcelJS) and upon bundling, the svg's are already injected.

This would be a big advantage as the bundled files already contain the SVG, and we don't need to use SVG-inject anymore to inject them every time.

First ID stays the same

I noticed, that when I use this super nice tool, the first IDs in the SVG is not changed. For example I have this SVG:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128" viewBox="0 0 128 128" data-inject-url="http://us.localhost.com:3000/assets/application/home/us-chat-in-development.svg">
  <title>Ribbon - Remove Chat from US Platform</title>
  <defs>
    <circle id="a" cx="64" cy="64" r="64"></circle>
  </defs>
  <g fill="none" fill-rule="evenodd">
    <mask id="b--inject-1" fill="#fff">
      <use xlink:href="#a"></use>
    </mask>
    <use fill="#D7DEE2" xlink:href="#a"></use>
    <circle fill="#82C0D2" mask="url(#b--inject-1)" cx="64" cy="64" r="54"></circle>
    <g mask="url(#b--inject-1)">
      <g transform="translate(36.44 23)">
        <path d="M2.157 48.825a1.94 1.94 0 0 1-.02-2.795l29.24-29.243a12.967 12.967 0 0 1 1.037-8.816c2.767-5.519 8.922-8.235 14.719-6.934l-6.058 6.485a1.984 1.984 0 0 0 .109 2.809l6.945 6.397c.811.747 2.08.698 2.828-.103l5.962-6.38a12.958 12.958 0 0 1-.87 9.408c-2.92 5.824-9.615 8.53-15.678 6.679L11.595 55.1a2.054 2.054 0 0 1-2.853.038l-6.585-6.313z" fill="#555"></path>
        <path d="M1.437 48.11a1.94 1.94 0 0 1-.02-2.795l29.24-29.243a12.967 12.967 0 0 1 1.037-8.816C34.46 1.737 40.616-.98 46.412.32l-6.057 6.485a1.984 1.984 0 0 0 .108 2.81l6.945 6.396c.812.748 2.08.699 2.829-.102l5.961-6.381a12.958 12.958 0 0 1-.87 9.408c-2.919 5.825-9.614 8.53-15.678 6.68L10.874 54.384a2.054 2.054 0 0 1-2.852.038L1.437 48.11z" fill="#D7DEE2"></path>
        <circle fill="#D7DEE2" cx="36.284" cy="16.223" r="2.5"></circle>
        <circle fill="#D7DEE2" cx="41.517" cy="20.685" r="2.5"></circle>
      </g>
      <g fill-rule="nonzero">
        <path fill="#FFF" d="M39.297 39.09l9.745-9.745 33.6 33.599-9.746 9.745z"></path>
        <path fill="#F6D7AA" opacity=".94" d="M35.51 25.762l13.397 3.613-9.703 9.69z"></path>
        <path fill="#555" d="M70.774 70.414l9.745-9.745 4.22 4.22-9.746 9.745z"></path>
        <path d="M74.983 74.592l9.758-9.634 4.852 4.817c.661.657.661 1.697 0 2.3l-7.388 7.335c-.661.657-1.709.657-2.315 0l-4.907-4.818z" fill="#D7DEE2"></path>
        <path d="M36.061 25.543l3.473 1.314-2.866 2.846-1.268-3.448c-.22-.438.22-.876.661-.712z" fill="#555"></path>
      </g>
    </g>
    <path d="M.183 84.805C-.303 83.808.196 83 1.305 83H126.45c1.106 0 1.77.874 1.486 1.941l-5.086 19.118c-.285 1.072-1.416 1.941-2.517 1.941H12.521c-1.105 0-2.393-.805-2.88-1.805L.183 84.805z" fill="#D7DEE2" mask="url(#b--inject-1)"></path>
    <path d="M17 96.894v-7.772h1.27v7.772H17zm9.887 0H25.33L21.508 90.7h-.043l.027.346c.05.659.074 1.261.074 1.807v4.04h-1.153v-7.772h1.542l3.81 6.161h.033a36.05 36.05 0 0 1-.043-.89 30.766 30.766 0 0 1-.032-1.199v-4.072h1.164v7.772zm11.382-3.96c0 1.282-.356 2.263-1.069 2.942-.712.678-1.738 1.018-3.077 1.018h-2.174v-7.772h2.402c1.237 0 2.2.333 2.887 1 .687.666 1.03 1.603 1.03 2.812zm-1.34.042c0-1.86-.87-2.79-2.61-2.79h-1.1v5.64h.904c1.87 0 2.806-.95 2.806-2.85zm7.496 3.918h-4.396v-7.772h4.396v1.074h-3.126v2.116h2.929v1.063h-2.929v2.44h3.126v1.079zm6.198-7.772h1.318l-2.721 7.772h-1.34l-2.71-7.772h1.307l1.621 4.832c.085.23.176.527.271.89.096.364.158.634.186.811.046-.27.117-.581.213-.935.096-.355.174-.617.234-.787l1.621-4.81zm6.831 7.772h-4.396v-7.772h4.396v1.074h-3.126v2.116h2.93v1.063h-2.93v2.44h3.126v1.079zm1.77 0v-7.772h1.27v6.682h3.291v1.09h-4.56zm12.673-3.897c0 1.265-.316 2.249-.949 2.95C70.316 96.65 69.42 97 68.261 97c-1.173 0-2.074-.348-2.703-1.045-.629-.696-.943-1.685-.943-2.968s.316-2.268.949-2.956c.632-.687 1.535-1.031 2.708-1.031 1.155 0 2.048.35 2.679 1.047.63.698.946 1.682.946 2.95zm-5.932 0c0 .957.193 1.683.58 2.177.386.494.958.742 1.716.742.755 0 1.325-.245 1.71-.734.384-.489.576-1.217.576-2.185 0-.953-.19-1.676-.571-2.168-.381-.493-.95-.74-1.704-.74-.762 0-1.337.247-1.725.74-.388.492-.582 1.215-.582 2.168zm12.838-1.525c0 .811-.266 1.433-.798 1.866-.531.432-1.288.648-2.27.648h-.808v2.908h-1.27v-7.772h2.238c.97 0 1.698.199 2.182.596.484.397.726.981.726 1.754zm-3.876 1.446h.676c.652 0 1.13-.114 1.435-.34.305-.227.457-.582.457-1.064 0-.446-.137-.78-.41-.999-.272-.22-.698-.33-1.275-.33h-.883v2.733zm8.878 3.976l-2.254-6.49h-.042c.06.963.09 1.867.09 2.71v3.78h-1.154v-7.772h1.792l2.158 6.182h.032l2.222-6.182h1.796v7.772h-1.222V93.05c0-.386.01-.889.029-1.51.02-.62.036-.995.05-1.126h-.042l-2.334 6.48h-1.121zm11.174 0h-4.396v-7.772h4.396v1.074h-3.126v2.116h2.929v1.063h-2.929v2.44h3.126v1.079zm8.245 0h-1.558L97.844 90.7h-.042l.026.346c.05.659.075 1.261.075 1.807v4.04h-1.154v-7.772h1.542l3.81 6.161h.033a36.044 36.044 0 0 1-.043-.89 30.765 30.765 0 0 1-.032-1.199v-4.072h1.165v7.772zm4.826 0h-1.27v-6.682h-2.286v-1.09h5.842v1.09h-2.286v6.682z" fill="#333" mask="url(#b--inject-1)"></path>
  </g>
</svg>

Here You can see that the id="a" in the circle tag is not touched at all, but the following IDs in the mask tags are changed. I have another example, where all the IDs stay the same:

    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="StarNoGradient" fill-rule="nonzero">
            <polygon id="Combined-Shape-Copy" fill="#E3B500" points="12 0 12 19.9 4.584 24 6 15.317 0 9.167 8.292 7.9"></polygon>
            <polygon id="Combined-Shape-Copy-2" fill="#999999" points="12 0 15.708 7.9 24 9.167 18 15.317 19.416 24 12 19.9"></polygon>
        </g>
    </g>
</svg>

I am wondering now, if this is a tag related problem or if I just miss something here? I am kind of clueless. Also I copied this SVGs from the DOM and in both pages the library is implemented the same way.

Question about webpack

I'm a VueJS developer, and most of us use webpack to bundle their apps. (usually via default config of a CLI framework like vue-cli etc.)

When it comes to inlining svg's, the go-to library is usually:
https://github.com/oliverfindl/vue-svg-inline-loader

This is a simple "webpack extension" that swaps out <img> for the actual <svg> upon build.

I wonder what the difference is between this library and a webpack loader like I mentioned above.
If there are benefits for this library over a webpack loader, I might consider swapping out the webpack loader for this library!

This might be an interesting Q&A to add to the Readme! ;)

SVG's not always loading in

I've been trying to get this to work in my project, but it was been really spotty. I have the script included in my html and the minified js file in my project. I can style it, but sometimes the image doesn't get turned into an SVG. Sometimes it decides to inject and then other times it just doesn't seem to want to. I can simply refresh the page and it seems pretty random that some will load into an svg and others won't. If I can possibly get a solution for this, that'd be awesome! I'm using the latest version and I followed the example snippet on the projects homepage to implement it.

Under scripts:
<script src="/scripts/svg-inject.min.js"></script>

In the body of the html:
<a href="#" class="slendr-prev"><img src="images/icons/angle-left.svg" onload="SVGInject(this)"/></a> <a href="#" class="slendr-next"><img src="images/icons/angle-right.svg" onload="SVGInject(this)"/></a>

SVG styling:
svg { fill: white; width: 2rem; height: 2rem; }

Cached SVGs not always injected correctly

Version 1.2.0 introduced a bug where cached images are not always injected.

The problem exists because in Line 540 there is not a distinction between a loadValue Array and a callbackQueue Array.

A test to prevent regression of this this should also be written.

Consider `aria-labelledby` and `aria-describedby` when making IDs unique.

The attributes aria-labelledby and aria-describedby can be used to enhance accessibility for SVG elements. For both the value consists of a space separated list of element IDs. Currently these IDs are not changed when we make IDs unique. As a result the IDs are no longer in the DOM, so the attributes do not work anymore. We should make the IDs in these attribute values unique, too.

aria-labelledby' and aria-describedby` on MSN web-docs:
https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute
https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-describedby_attribute

inline style

Hi
Can we do inline style for the SVG?
thanks for this great tool

how can i use it in typescript?

it seemed that it can only be used in script label and html which can not be satisfied in es6 that will import module.
it it not support typescript,let me know

make addStyleToHead optional to easily comply with Content Security Policy

When setting some strict CSP directive (like "default-src 'self'"), <style> tags are rejected.
I suspect a sane approach to this -without whitelisting, nonce or hash- would be to optionally call the method on the first call to inject, rather than on creation?. The css to prevent image flash would need to be included in an external css, which I feel is fairly straighforward.
Or maybe there's some other approach I'm not seeing.

Problem with title attribute.

I am using Twitter Bootstrap tooltip plugin. To show tooltip, it needs title attribute.
Your plugin copy contents of title attribute to title tag of svg and leaves nothing in attribute title.

Was:

<img ... title="something"/>

Become:

<svg ... title>
    <title>something</title>
...
</svg>

I need:

<svg ... title="something">
    <title>something</title>
...
</svg>

How to keep title value?

Empty SVGs on Internet Explorer

When injecting the same SVG multiple times, only the last one will be displayed on Internet Explorer. The other SVGs are empty, which means they have been injected, but the child elements are missing.

Everything works fine on Chrome and Firefox.

This issue is probably cause by this line: DIV_ELEMENT.innerHTML = '';

It seems this completely resolves the tree hierarchy for DIV_ELEMENT, removing also the children from the SVG element.

Issue with Gulp

I have an issue when using svg inject in gulp, it's not working in dist folder

Access to XMLHttpRequest has been blocked by CORS policy

I was trying to load a local SVG file into my html but it was not successful

<head>
  <script src="svg-inject.js"></script>
</head>
<body>
  <img src="earth.svg" onload="SVGInject(this)">
</body>

I opened the console and found the following warning
SQLInject_issue

How can I deal with this issue?

most basic example isnt working

Pls find my code here. Its not working. I dont know what am I missing. Can you pls provide a working demo?

Code:
<img src="https://upload.wikimedia.org/wikipedia/commons/f/fd/Ghostscript_Tiger.svg" onload="SVGInject(this)" />

Expectation:
In chrome Elements panel above image should be replaced by svg

Reality:
No change is seen in above image.

SSR Friendly

Hi,

Was trying to checkout your library to use in a Next.js project.

However as Next uses SSR and "window" doesn't exist this package fails with:

ReferenceError: window is not defined

I looked through the code, and it seems that the only use window has is to bind to a global namespace?

Would you consider removing this code to make it compatible, or maybe putting a conditional around it to make sure it only binds when a window actually exists?

Thanks :)

Personal opinion about readme

I like this small library, but would maybe suggest to use a different approach as an example.

Example:

<html>
<head>

</head>
<body>
  <img src="image.svg"  class="inject-svg"/>

  <script src="svg-inject.min.js"></script>
  <script>
   let svg = document.querySelector(".inject-svg");

   document.addEventListener("DOMContentLoaded",init);

   function init(){
      SVGInject(svg);
   }
</script>
</body>
</html>

The reason behind this is, of course, it's not as easy and short as the other one, but if you have other scripts running beside it and are only using the previous method, you'll get stuck because you might then have multiple onload's running and the SVG it hasn't been injected yet for the "main.js" script.

Simplify function `makeIdsUnique()`

Since we are not using the ònlyReferencedIds parameter anymore (or always set it to false), the function makeIdsUnique() can be simplified.

Also, the variable referencedIds is declared as an array in function makeIdsUnique().in line160.

var referencedIds = onlyReferenced ? [] : NULL;

This would not work anyways, because it is populated with the elements' id as the key:

referencedIds[id] = 1;

So let's remove the parameter and make the function makeIdsUnique() a litte bit smaller.

IE WrongDocumentError

Hi!

Firstly, thank you for your work!

I have a problem in IE where it throws WrongDocumentError while trying to replace the img element with the svg.

After looking into it, I found that other similar packages solved this same problem with a simple solution:

// Fix for browser (IE, maybe other too) which are throwing 'WrongDocumentError'
// if you replace an element which is not in the document
if (document.importNode) {
    injectElem = document.importNode(injectElem, true);
}

I tried it myself by adding it right before:

// Replace img element with new element. This is the actual injection.
parentNode.replaceChild(injectElem, imgElem);

And it fixed the issue 👍

Any thoughts on this?

Thanks!

Missing TypeScript type definition

I'm trying to use this library in TypeScript but the type definition for this package is missing and I couldn't find anything on @types. Is there one? If there isn't, I'm currently working to write it and would you be open to adding this into the package instead of submitting separately to @types?

Accessibility

Hey there, great plugin btw!

It would be amazing if it could transfer the img alt attribute to the a title attribute on svg tag. That way, we can ensure accessibility, according to W3C.

I know I could just add a title attribute on the .svg file itself, but what if I have multiple languages? So yeah, it needs to transfer from the img to svg as well!

I might try to do a pull request if I have the time.

Thanks!

Consider automatically converting `alt` property to `title` element

To make accessibility easier and more intuitive to implement, we could automatically convert the image's alt property to the SVG's title element. For accessibility both are used very similarily, so we could provide an "auto-accessibility" oprion that will automatically convert alt property to title element and add role="img". This way the injected SVG would automatically be as accesible as an image element.

Css styles removed after injection

Good morning,
I noticed that any css style applied to the svg element (from a css stylesheet) is removed after injection.
Is there a way to prevent this?
I have tried loading the css file after the svg injection javascript file but without succes.
Excuse my question if its too basic, and thanks very much for this plug in,
Sali

Update: excuse my question, my bad, I was attaching wrongly the css file.

IDs not replaced in links

Elements can link to another element with the xlink:href or href attribute. The IDs in these links are currently not replaced by the new IDs. Here is an example how this can look in SVG:

  <linearGradient id="gradient1" href="#gradient2" />
  <linearGradient id="gradient2" xlink:href="#gradient3" />
  <linearGradient id="gradient3" gradientUnits="userSpaceOnUse" x1="0" y1="800" x2="0" y2="800">
   <stop offset="0" style="stop-color:#FFFF00"/>
   <stop offset="1" style="stop-color:#00FF00"/>
  </linearGradient>

In this example a link to #gradient1 would actually use #gradient3

Handle empty and missing `src` attributes gracefully

Currently img elements with empty or missing src attributes do not work, because the way we convert relative URLs to absolute URLs does not work for empty strings.

On Chrome and Firefox an img with an empty src attribute is displayed as a broken image. An img without a src attribute is displayed as an empty image on Chrome and not displayed at all on Firefox.
Here is a codepen: https://codepen.io/anon/pen/KGNepr

We should probably leave the src attribute untouched if it is empty or not provided.

Id not converted if reference is in style attribute

In the SVG below the second rectangle is not displayed after injection

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="256" height="256">
  <defs>
    <linearGradient x1="0" y1="0" x2="256" y2="0" id="color-1" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="red"/>
      <stop offset="1" stop-color="blue"/>
    </linearGradient>
  </defs>
  <g>
    <rect x="0" y="128" width="256" height="128" fill="url(#color-1)"/>
    <rect x="0" y="0" width="256" height="128" style="fill:url(#color-1)"/>
  </g>
</svg>

The reference inside the style attribute is a perfectly normal way of referencing definitions, as can be seen in https://www.w3.org/TR/SVG11/struct.html#Head

Currently, when making IDs unique, only IDs inside references in the form attrName="url(#myId)" are replaced, but references that are part of the style attribute are ignored. Therefore the ID in the style attribute of the second rect is not converted. As a result the rect has no fill at all.

It should be relatively easy to fix this by taking this in lines 164ff into account.

Add beforeLoad hook

Currently there is no beforeLoad hook, but it could be useful to change the src before sending the XHR.

Specifically it could be used to create a super bullet proof fallback that works on any browser, no matter if SVG or Javascript is supported:

SVGInject.setOptions({
  beforeLoad: function(img) {
    img.src = img.srcset;
  }
});

The img tags must have the srcset attribute set like this:

<img src="image.png" srcset="image.svg" width="128" height="128" onload="SVGInject(this)" />

All browsers without SVG support (like IE8 and older) also have no support for the srcset attribute, therefore only the PNG set in the src will be used.

All browsers with Javascript support will replace the src attribute with the srcset attribute before loading the SVG.

The only drawback is on Internet Explorer 9, 10 and 11, which will first load the PNG, and then for the injection load the SVG. This means that there is an unnecessary download of the fallback PNG on these browsers.

'title' attribute not handled properly if namespaced title exists

Currently, if a title attribute is specified in the <img> element and the injected SVG already has a namespaced title element (for example <svg:title>, a new title element is created and inserted before the existing namespaced `<title>´ element.

Instead the existing namespaced title element should be used and the content changed to the value of the img's title attribute.

IDs not replaced for namespaced SVGs

SVGs can be namespaced, which means that tag names can have a namespace prefix like <svg:style>. Currently, IDs are not replaced if elements have a namespace prefix.

Here is a simple example.

index.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:svg="http://www.w3.org/2000/svg">
  <head>
    <script src="svg-inject.js"></script>
  </head>
  <body>
    <img src="svg1.svg" onload="SVGInject(this)"/>
  </body>
</html>

svg1.svg:

<svg:svg xmlns:svg="http://www.w3.org/2000/svg" width="300" height="200">
  <svg:style>
    .cls2 {
      fill: url(#grad2);
    }
  </svg:style>
  <svg:defs>
    <svg:linearGradient id="grad2">
      <svg:stop offset="0" stop-color="red" />
      <svg:stop offset="1" stop-color="green" />
    </svg:linearGradient>
  </svg:defs>
  <svg:circle cx="150" cy="100" r="50" class="cls2"/>
</svg:svg>

Although the SVG is displayed correctly, the IDs are not replaced, which can result in the known problems when multiple SVGs are injected.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.