Ryan Peschel
Ryan Peschel

Reputation: 11756

How to eliminate HTML code duplication when using the same SVG icon in multiple places?

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>

Upvotes: 2

Views: 2044

Answers (3)

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.

Upvotes: 2

kmoser
kmoser

Reputation: 9273

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>

Upvotes: 2

Ygor Rolim
Ygor Rolim

Reputation: 74

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="..."/>

Upvotes: 2

Related Questions