skip to Main Content

According to this page How SVG Fragment Identifiers Work, the SVG <view> element…

can actually wrap other elements, in which case that viewBox takes hold when the ID matches

However, I cant get it to work.

This is a simple SVG file:

<svg viewBox="0 0 500 100" width="100" height="100"
      xmlns="http://www.w3.org/2000/svg">

  <view id="one" viewBox="0 0 100 100">
    <circle cx="50" cy="50" r="40" fill="red" />
  </view>
  <view id="two" viewBox="0 0 100 100">
    <circle cx="50" cy="50" r="40" fill="green" />
  </view>
  <view id="three" viewBox="0 0 100 100">
    <circle cx="50" cy="50" r="40" fill="blue" />
  </view>
  <view id="four" viewBox="200 0 100 100" >
    <circle cx="250" cy="50" r="40" fill="yellow" />
  </view>
  <view id="five" viewBox="300 0 100 100" />
  <circle cx="350" cy="50" r="40" fill="cyan" />
  <circle cx="50" cy="50" r="20" fill="black" />
</svg>

This is the calling HTML:

<!DOCTYPE html>
<html><head>
  <style>
    img {border:1px solid #080;}
    img.red {content:url("foo.svg#one");}
    img.green {content:url("foo.svg#two");}
    img.blue {content:url("foo.svg#three");}
 </style>
</head><body>
  <img class="red" />
  <img class="green" />
  <img class="blue" />
  <img src="foo.svg#four" />
  <img src="foo.svg#five" />
</body></html>

The only circle actually shown is the cyan one. Anyone knows what is going on here?

2

Answers


  1. I have never seen <view> work (the way I wanted it to work)

    Every browser supports native Web Components to create HTML/SVG dynamically.
    And can replace <use> and <symbol> with JS code.

    <svg-circle cx="50%" cy="50%" fill="red"></svg-circle>
    <svg-circle cx="50"  cy="50"  fill="green"></svg-circle>
    <svg-circle></svg-circle>
    <svg-circle cx="100%" r="80" fill="blue"></svg-circle>
    
    <script>
      customElements.define("svg-circle", class extends HTMLElement {
        connectedCallback() {
          this.style.display    = "inline-block";
          this.style.background = "beige";
          let cx   = this.getAttribute("cx")   || "50%";
          let cy   = this.getAttribute("cy")   || "50%";
          let r    = this.getAttribute("r")    || "45%";
          let fill = this.getAttribute("fill") || "hotpink";
          this.innerHTML = 
            `<svg viewBox="0 0 500 500" width="100" height="100">` +
              `<circle cx="${cx}" cy="${cy}" r="${r}" fill="${fill}" />` +
              `<circle cx="50%" cy="50%" r="40" fill="black" />` +
            `</svg>`;
        }
      });
    </script>
    Login or Signup to reply.
  2. Unlike <symbol> elements <view> must not contain geometry/rendered elements.
    That’s why your first circle don’t show up.

    <view> elements are used to specify predefined views.
    So you can pan a svg sprite sheet to a specific region via target ID – whereas you would otherwise no need to adjust background positions.

    (To some extent you might compare them to HTML <a> anchor targets, helping you to jump to a specific section of a page)

    The view element can only be used for sprite sheets with elements spread across the spritesheet area. Overlapping elements won’t work.

    In your case your first elements are overlapping.
    A <view> doesn’t change any visibility properties – so you’ll see the topmost element. You can delete the <view> elements for these graphics

    For overlapping elements we you can hide not targeted elements by CSS.

    *[id] {
      display: none;
    }
    *:target {
      display: block;
    }
    

    Here’s a modified example svg:

    <svg viewBox="0 0 100 100" width="100" height="100"
          xmlns="http://www.w3.org/2000/svg">
    
    <style>
    /**
    * show only targeted element
    * as definded by fragment identifier
    * e.g: sprite.svg#use_up_down
    */
        *[id] {
          display: none;
        }
        *:target {
          display: block;
        }
    </style>
    
      <!-- displayed via fragment identifier -->
      <circle id="one" cx="50" cy="50" r="50" fill="red" />
      <circle id="two" cx="50" cy="50" r="50" fill="green" />
      <circle id="three" cx="50" cy="50" r="40" fill="blue" />
      
      <!-- displayed via view identifier-->
      <circle cx="250" cy="50" r="40" fill="yellow" />
      <view id="viewfour" viewBox="200 0 100 100" />
      
      <circle cx="350" cy="50" r="40" fill="cyan" />
      <view id="viewfive" viewBox="300 0 100 100" />
    
    </svg>

    Basically we’re combining two fragment identifier techniques

    • adjust the svg viewBox to viewBox="0 0 100 100" to match the desired 100×100 "icon" size.
    • delete unnessecary view elements for overlapping assets
    • add css rules to hide not targeted elements
    img {
      border: 1px solid #ccc;
      width: 100px;
      height: 100px;
    }
    <h3>Overlapping target</h3>
    <img src="data:image/svg+xml,%3Csvg viewBox='0 0 100 100' width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E%0A/**%0A* show only targeted element%0A* as definded by fragment identifier%0A* e.g: sprite.svg%23use_up_down%0A*/ *%5Bid%5D %7B display: none; %7D *:target %7B display: block; %7D%0A%3C/style%3E%3C!-- displayed via fragment identifier --%3E%3Ccircle id='one' cx='50' cy='50' r='50' fill='red' /%3E%3Ccircle id='two' cx='50' cy='50' r='50' fill='green' /%3E%3Ccircle id='three' cx='50' cy='50' r='40' fill='blue' /%3E%3C!-- displayed via view identifier--%3E%3Ccircle cx='250' cy='50' r='40' fill='yellow' /%3E%3Cview id='viewfour' viewBox='200 0 100 100' /%3E%3Ccircle cx='350' cy='50' r='40' fill='cyan' /%3E%3Cview id='viewfive' viewBox='300 0 100 100' /%3E%3C/svg%3E#one">
    
    <img src="data:image/svg+xml,%3Csvg viewBox='0 0 100 100' width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E%0A/**%0A* show only targeted element%0A* as definded by fragment identifier%0A* e.g: sprite.svg%23use_up_down%0A*/ *%5Bid%5D %7B display: none; %7D *:target %7B display: block; %7D%0A%3C/style%3E%3C!-- displayed via fragment identifier --%3E%3Ccircle id='one' cx='50' cy='50' r='50' fill='red' /%3E%3Ccircle id='two' cx='50' cy='50' r='50' fill='green' /%3E%3Ccircle id='three' cx='50' cy='50' r='40' fill='blue' /%3E%3C!-- displayed via view identifier--%3E%3Ccircle cx='250' cy='50' r='40' fill='yellow' /%3E%3Cview id='viewfour' viewBox='200 0 100 100' /%3E%3Ccircle cx='350' cy='50' r='40' fill='cyan' /%3E%3Cview id='viewfive' viewBox='300 0 100 100' /%3E%3C/svg%3E#two">
    
    
    <h3>View element - distributed positions</h3>
    <img src="data:image/svg+xml,%3Csvg viewBox='0 0 100 100' width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E%0A/**%0A* show only targeted element%0A* as definded by fragment identifier%0A* e.g: sprite.svg%23use_up_down%0A*/ *%5Bid%5D %7B display: none; %7D *:target %7B display: block; %7D%0A%3C/style%3E%3C!-- displayed via fragment identifier --%3E%3Ccircle id='one' cx='50' cy='50' r='50' fill='red' /%3E%3Ccircle id='two' cx='50' cy='50' r='50' fill='green' /%3E%3Ccircle id='three' cx='50' cy='50' r='40' fill='blue' /%3E%3C!-- displayed via view identifier--%3E%3Ccircle cx='250' cy='50' r='40' fill='yellow' /%3E%3Cview id='viewfour' viewBox='200 0 100 100' /%3E%3Ccircle cx='350' cy='50' r='40' fill='cyan' /%3E%3Cview id='viewfive' viewBox='300 0 100 100' /%3E%3C/svg%3E#viewfour">
    
    <img src="data:image/svg+xml,%3Csvg viewBox='0 0 100 100' width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E%0A/**%0A* show only targeted element%0A* as definded by fragment identifier%0A* e.g: sprite.svg%23use_up_down%0A*/ *%5Bid%5D %7B display: none; %7D *:target %7B display: block; %7D%0A%3C/style%3E%3C!-- displayed via fragment identifier --%3E%3Ccircle id='one' cx='50' cy='50' r='50' fill='red' /%3E%3Ccircle id='two' cx='50' cy='50' r='50' fill='green' /%3E%3Ccircle id='three' cx='50' cy='50' r='40' fill='blue' /%3E%3C!-- displayed via view identifier--%3E%3Ccircle cx='250' cy='50' r='40' fill='yellow' /%3E%3Cview id='viewfour' viewBox='200 0 100 100' /%3E%3Ccircle cx='350' cy='50' r='40' fill='cyan' /%3E%3Cview id='viewfive' viewBox='300 0 100 100' /%3E%3C/svg%3E#viewfive">

    Advantage of <view> spritesheets

    Although the view concept seems to be very limited, the main advantage: you can easily edit all your sprites in a graphic editor as all items remain visible.

    enter image description here
    If you need to add a new icon: just place it next to the previous ones and define a new <view> area (illustrated by red boxes).

    You can also compare alignment, size, stroke width and other visual properties at a glance to ensure consistent icon design.

    Whereas when working with <symbol> or <defs> elements you need to unwrap the hidden icons to see them in your editor.

    Drawbacks: you can’t change any style properties like fill/stroke (just like any other image/background-image usage)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search