1

I have an image that loads when clicking a div like so:

<img class="svg" src="{{asset('public/images/my_image.svg') }}" alt="" id='image'>

When calling this image from the backend side, it's returned as an image and not vectors (SVG render). How can I render it to SVG using Javascript or any other similar tool?

Thanks!

retro_coder
  • 367
  • 1
  • 9
  • 25

2 Answers2

3

Using an img tag with a src in essentially makes an HTTP request to the server for you. In this case you have to make the request yourself. You can do that with the Fetch API which is native to JavaScript.

// This should be the path to your SVG file.
// Modify if incorrect.
const svgFilePath = 'public/images/my_image.svg';

// Select the current image.
const image = document.querySelector('.svg');

// Create a new dom parser to turn the SVG string into an element.
const parser = new DOMParser();

// Fetch the file from the server.
fetch(svgFilePath)
  .then(response => response.text())
  .then(text => {

    // Turn the raw text into a document with the svg element in it.
    const parsed = parser.parseFromString(text, 'text/html');

    // Select the <svg> element from that document.
    const svg = parsed.querySelector('svg');

    // If both the image and svg are found, replace the image with the svg.
    if (image !== null && svg !== null) {
      image.replaceWith(svg);
    }

  });

And with handling multiple files. In this case I've used an array with objects that hold the id of the image element you want to get and the src of the svg file.

// Array of object containing the id of the image you want to replace
// and the src of the SVG that takes it's place.
const svgFiles = [
  { id: 'image1', src: 'public/images/my_image_1.svg' },
  { id: 'image2', src: 'public/images/my_image_2.svg' },
  { id: 'image3', src: 'public/images/my_image_3.svg' },
];

// Create a new dom parser to turn the SVG string into an element.
const parser = new DOMParser();

// Loop over each object in the array and use the id and src
// with a destructuring assignment.
for (const { id, src } of svgFiles) {

  // Find the image. If it is not there, continue with the
  // loop to the next iteration.
  let image = document.getElementById(id);
  if (image === null) continue;

  // Fetch the file from the server.
  fetch(src)
    .then(response => response.text())
    .then(text => {

      // Turn the raw text into a document with the svg element in it.
      const parsed = parser.parseFromString(text, 'text/html');

      // Select the <svg> element from that document.
      const svg = parsed.querySelector('svg');

      // If the svg is found, replace the image with the svg.
      // Otherwise, continue the loop.
      if (svg === null) continue;
      image.replaceWith(svg);

    });
}
Emiel Zuurbier
  • 19,095
  • 3
  • 17
  • 32
  • wont the SVG image be automatically rendered to vectors? – retro_coder Apr 02 '20 at 07:14
  • The V in SVG stands for vector. So any SVG has vectors, but you probably mean something else. Care to elaborate? – Emiel Zuurbier Apr 02 '20 at 07:19
  • Oh, my bad, you are right. I got the vector points by printing it out (using console.log) but how can I replace the current image with the new rendered svg content? – retro_coder Apr 02 '20 at 07:24
  • I've modified the answer for you to be able to replace the `` with the new `` element. But this method seems tedious if you simply want to replace the image with something else and should be handled from the backend. Consider finding a server side svg rendering approach. – Emiel Zuurbier Apr 02 '20 at 07:39
  • I tried your new approach but when I refreshed the page, the image is only seen in my page but the rest of the HTML content disappeared – retro_coder Apr 02 '20 at 07:49
  • Do you have a `.svg` class on a parent element? Because this script replaces the first encounter of that class with the SVG. – Emiel Zuurbier Apr 02 '20 at 08:08
  • I added a unique id to the image element and replaced .svg with #image. But it still doesn't work. – retro_coder Apr 02 '20 at 09:03
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/210792/discussion-between-emiel-zuurbier-and-danny-boy). – Emiel Zuurbier Apr 02 '20 at 09:27
  • What does .then(text => {}) mean? – retro_coder Apr 02 '20 at 10:32
  • The `text => {}` an arrow function. It's almost the same as doing `function(text) {}`. `.then()` is a promise callback. `fetch` can take a while to complete so it works with promises which can wait for a while before continuing the code. When a promise is *fulfilled* (meaning it has finished without an error) it will call the function in the adjacent `.then()` Read up on [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) and [promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises). – Emiel Zuurbier Apr 02 '20 at 10:41
  • what if there are multiple images? How can you use your code for multiple dynamic images? – retro_coder Apr 15 '20 at 05:23
  • I've added a version that uses a loop to change all the images in the array. Replace the `id` and `src` values with your own image elements and SVG files. – Emiel Zuurbier Apr 15 '20 at 06:05
1

I noticed that you are using asset() function so I'm guessing you don't necessary want it to be JS
You can use Laravel SVG package which provides @svg() blade directive.
it's small and convenient

<a href="/settings">
    @svg('cog', 'icon-lg') Settings
</a>

<!-- Renders.. -->
<a href="/settings">
    <svg class="icon icon-lg">
        <path d="..." fill-rule="evenodd"></path>
    </svg>
    Settings
</a>
AH.Pooladvand
  • 1,944
  • 2
  • 12
  • 26