3

I'm writing a single HTML page in plain vanilla JS, HTML, and CSS.

Everything so far is fine but I now need to include the same SVG icon in four different places.

Code duplication is bad and if I were using something like React I'd just make it into a component and use that in each of the four places.

Since I'd prefer to stay vanilla and not use any external libraries I took a look at Web Components but they seem like they're a bit of a failed experiment with somewhat limited browser support.

Something that would probably work is just returning a raw string of the HTML contents from a JavaScript function and then using JavaScript again to inject that node into each of the four elements, but that seems very cumbersome and messy.

Is there something obvious that I'm missing here? I'm just looking for a way to include this SVG in four separate locations without copying and pasting the duplicate code in each location, as that is verbose and error-prone. I also do not want to include a build step or process for this simple change.

<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-minimize-2">
  <polyline points="4 14 10 14 10 20"></polyline>
  <polyline points="20 10 14 10 14 4"></polyline>
  <line x1="14" y1="10" x2="21" y2="3"></line>
  <line x1="3" y1="21" x2="10" y2="14"></line>
</svg>
1
  • If you can stand another failed experiment (that pretty much does everything everyone on HN is complaining about), see my <svg-icon> component at iconmeister.github.io FYI, Code Duplication is actually very good, minified size does NOT count because GZip/Brotli very much like duplications. So from a download time perspective, and if you actually just want 4 static SVGs, then use 4 static SVGs Commented Nov 25, 2020 at 13:15

3 Answers 3

2

according to this reference: https://css-tricks.com/use-and-reuse-everything-in-svg-even-animations/

For the given svg:

<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-minimize-2">
  <polyline points="4 14 10 14 10 20"></polyline>
  <polyline points="20 10 14 10 14 4"></polyline>
  <line x1="14" y1="10" x2="21" y2="3"></line>
  <line x1="3" y1="21" x2="10" y2="14"></line>
</svg>

you can apply it an id such like:

 <svg  xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-minimize-2">
   **<g id="your_content_svg">**
      <polyline points="4 14 10 14 10 20"></polyline>
      <polyline points="20 10 14 10 14 4"></polyline>
      <line x1="14" y1="10" x2="21" y2="3"></line>
      <line x1="3" y1="21" x2="10" y2="14"></line>
   **</g>**
</svg>

Then you can refer to it like this:

<use xlink:href="#your_content_svg" width=".." height="..."/>
 ...
<use xlink:href="#your_content_svg" width=".." height="..."/>
Sign up to request clarification or add additional context in comments.

5 Comments

I think you're missing the <defs> tag around the <g> tag.
@kmoser the defs is not required here, it would just make its content not displayed.
I might be wrong but isn't this only for re-using SVG within itself? What I want is to be able to define an svg element once and then re-use it in another place on the same HTML page.
If you not tried, I think that it would be a good idea if you could define your svg in a separated .svg file. Then you can refer to it in HTML files using: <img src="you_svg_file.svg" alt="Legend for it">
Ygor is right. No defs required and IDs in SVG become global references in the document DOM. But you still need to wrap the <use href=#ID/> in a <svg ..> tag with all required attributes to make it a valid SVG
2

This StackOverflow answer describes how you can base64 encode the XML and use it as, say, the background image via an inline data URL: Svg data image as css background?.

Example (using a different SVG):

.carousel-control-prev-icon {
    background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23f00' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E");
}

You can then apply that background (which uses that SVG) to various tags on your page:

<span class="carousel-control-prev-icon"></span>
<span class="carousel-control-prev-icon"></span>
<span class="carousel-control-prev-icon"></span>

2 Comments

Then why not just use an <img>? The advantage of having them inline is that you can still style the svg elements, which you can't when it's an image.
I suppose it all depends on how they will actually be used, and what kind of customization/styling you want to do.
2

With 4 minimal sized SVGs I would just copy/paste those, if your objective is a single HTML file.

GZip loves duplications, so the downloaded file could* actually be smaller than any approach where you create a "component" and reference it 4 times.

* GZip looks for duplications within a 256 Byte range, so keep your 4 SVGs close together in the HTML

Very good deep-dive read: https://blog.usejournal.com/of-svg-minification-and-gzip-21cd26a5d007
(applies to anything you deliver from a Website)


An <svg-icon> Custom Element

Striclty speaking only Custom Elements with shadowDOM are called "Web Components"
You do not need shadowDOM to inject SVG content in the Document DOM with a component that basically does:

 this.innerHTML=`<svg ...></svg>`

Copy/paste this code below in your HTML page and you have a <svg-icon> Custom Element to play with.

In a single HTML file.... eat that React!

<style>
  svg-icon svg {
    width: 121px;
    background: khaki;
    cursor: pointer
  }
  svg-icon:hover path {
    stroke: black;
    stroke-width: 2;
  }
</style>

<svg-icon></svg-icon>
<svg-icon color="green"></svg-icon>
<svg-icon color="blue"></svg-icon>
<svg-icon color="magenta"></svg-icon>

<script>
  customElements.define("svg-icon", class extends HTMLElement {
    static get observedAttributes() {
      return ["color"]; // define attributes that trigger change
    }
    connectedCallback() { // execute on first use in HTML
      this.render();
    }
    attributeChangedCallback() { // handle observedAttributes changes
      this.render();
    }
    render(color = this.getAttribute("color") || "red") {
      this.innerHTML = `<svg viewBox='0 0 24 24'>` +
        `<path stroke='${color}' stroke-width='1.5' stroke-linecap='round' fill='none'` +
        ` d='M4 14 10 14 10 20M20 10 14 10 14 4M14 10L21 3M3 21L10 14'/></svg>`}});
</script>

To experience the power:

  • run the SO snippet above
  • Right click on an Icon to open F12 Developer Tools

  • Change any color attribute and see what happens

  • Do this in Chrome, Edge, Firefox, Safari, Opera, Bravo, Android etc.

  • Then, instead of listening to others,
    conclude for yourself if Custom Elements/Web Components are a "failed experiment, with limited browser support"

PS

The Web Components standard is about 3 distinct technologies

  1. Custom Elements API
  2. shadowDOM
  3. Templates

Each can be used without the other!

The above SO snippet only uses 1.

So applying 2. and 3. (depending on your previous conclusion) could be even more disappointing.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.