Should a sprite file be reloaded for each individual sprite?

222 Views Asked by At

I have 1324 different small SVG images, all the same basic size, that I need to load as background images for 1324 different cells in a grid display. The cells into which they load have varying geometry, so the SVG backgrounds need to stretch or compress to fit. The code below creates this effect, but I am stuck when trying to figure out how to do it without excessive network requests, and the point of this posting is to ask whether anyone might be able to advise me about managing the network traffic aspect of the task.

To avoid 1324 separate requests, one per background image (if the images were in separate SVG files), I thought I should be able to combine the images in a single file and refer to them individually by a unique id attribute value (a "sprite stack"). The following minimal examples renders what I want the way I want it to look, but when I load the page while watching in the network monitor in Chrome I see that it fetches the file that contains the images every time I refer to a different id value. With my real data and 1324 images, the browsers (I'm testing with current versions of Chrome, Firefox, Edge, and Safari, all on MacOS) make 1324 requests and crash before they can finish loading everything.

If I have understood correctly, one purpose of a sprite stack is to load a group of small images with a single network request and then refer to different portions of that omnibus file to render a contained image. The minimal sample below illustrates that combining the images into a single file works, as does referring to specific subimages within it, but I've clearly failed to understand how to implement this approach in a way that realizes the goal of reducing network requests.

This is the main HTML file, which loads the background images from an auxiliary SVG file (below):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Gradient embedded background test</title>
        <style>
            div {
              height: 200px;
              width: 400px;
              border: 1px solid black;
              background-size: auto auto;
              background-repeat: no-repeat;
            }
            #d1 {
              height: 300px;
            }
            #d2 {
              height: 500px;
            }</style>
    </head>
    <body>
        <h1>Hi, Mom!</h1>
        <div style="background: url('test-background.svg#or')">Stuff</div>
        <div style="background: url('test-background.svg#bl')">Stuff</div>
        <div id="d1" style="background: url('test-background.svg#gr')">Stuff</div>
        <div id="d2" style="background: url('test-background.svg#vi')">Stuff</div>
    </body>
</html>

This is the auxiliary SVG file with the images:

<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
    <style type="text/css">
        .sprite {display:none;}
        .sprite:target {display:inline;}
    </style>
    <defs>
        <linearGradient id="yellowGradient" x1="0%" x2="0%" y1="0%" y2="100%">
            <stop offset="0%" stop-color="yellow" stop-opacity="1"/>
            <stop offset="6%" stop-color="yellow" stop-opacity="1"/>
            <stop offset="20%" stop-color="yellow" stop-opacity=".6"/>
            <stop offset="35%" stop-color="yellow" stop-opacity=".4"/>
            <stop offset="50%" stop-color="yellow" stop-opacity=".3"/>
            <stop offset="65%" stop-color="yellow" stop-opacity=".4"/>
            <stop offset="80%" stop-color="yellow" stop-opacity=".6"/>
            <stop offset="94%" stop-color="yellow" stop-opacity="1"/>
            <stop offset="100%" stop-color="yellow" stop-opacity="1"/>
        </linearGradient>
        <linearGradient id="dodgerblueGradient" x1="0%" x2="0%" y1="0%" y2="100%">
            <stop offset="0%" stop-color="dodgerblue" stop-opacity="1"/>
            <stop offset="6%" stop-color="dodgerblue" stop-opacity="1"/>
            <stop offset="20%" stop-color="dodgerblue" stop-opacity=".6"/>
            <stop offset="35%" stop-color="dodgerblue" stop-opacity=".4"/>
            <stop offset="50%" stop-color="dodgerblue" stop-opacity=".3"/>
            <stop offset="65%" stop-color="dodgerblue" stop-opacity=".4"/>
            <stop offset="80%" stop-color="dodgerblue" stop-opacity=".6"/>
            <stop offset="94%" stop-color="dodgerblue" stop-opacity="1"/>
            <stop offset="100%" stop-color="dodgerblue" stop-opacity="1"/>
        </linearGradient>
        <linearGradient id="violetGradient" x1="0%" x2="0%" y1="0%" y2="100%">
            <stop offset="0%" stop-color="violet" stop-opacity="1"/>
            <stop offset="6%" stop-color="violet" stop-opacity="1"/>
            <stop offset="20%" stop-color="violet" stop-opacity=".6"/>
            <stop offset="35%" stop-color="violet" stop-opacity=".4"/>
            <stop offset="50%" stop-color="violet" stop-opacity=".3"/>
            <stop offset="65%" stop-color="violet" stop-opacity=".4"/>
            <stop offset="80%" stop-color="violet" stop-opacity=".6"/>
            <stop offset="94%" stop-color="violet" stop-opacity="1"/>
            <stop offset="100%" stop-color="violet" stop-opacity="1"/>
        </linearGradient>
        <linearGradient id="orangeGradient" x1="0%" x2="0%" y1="0%" y2="100%">
            <stop offset="0%" stop-color="orange" stop-opacity="1"/>
            <stop offset="6%" stop-color="orange" stop-opacity="1"/>
            <stop offset="20%" stop-color="orange" stop-opacity=".6"/>
            <stop offset="35%" stop-color="orange" stop-opacity=".4"/>
            <stop offset="50%" stop-color="orange" stop-opacity=".3"/>
            <stop offset="65%" stop-color="orange" stop-opacity=".4"/>
            <stop offset="80%" stop-color="orange" stop-opacity=".6"/>
            <stop offset="94%" stop-color="orange" stop-opacity="1"/>
            <stop offset="100%" stop-color="orange" stop-opacity="1"/>
        </linearGradient>
        <linearGradient id="peruGradient" x1="0%" x2="0%" y1="0%" y2="100%">
            <stop offset="0%" stop-color="peru" stop-opacity="1"/>
            <stop offset="6%" stop-color="peru" stop-opacity="1"/>
            <stop offset="20%" stop-color="peru" stop-opacity=".6"/>
            <stop offset="35%" stop-color="peru" stop-opacity=".4"/>
            <stop offset="50%" stop-color="peru" stop-opacity=".3"/>
            <stop offset="65%" stop-color="peru" stop-opacity=".4"/>
            <stop offset="80%" stop-color="peru" stop-opacity=".6"/>
            <stop offset="94%" stop-color="peru" stop-opacity="1"/>
            <stop offset="100%" stop-color="peru" stop-opacity="1"/>
        </linearGradient>
        <linearGradient id="limegreenGradient" x1="0%" x2="0%" y1="0%" y2="100%">
            <stop offset="0%" stop-color="limegreen" stop-opacity="1"/>
            <stop offset="6%" stop-color="limegreen" stop-opacity="1"/>
            <stop offset="20%" stop-color="limegreen" stop-opacity=".6"/>
            <stop offset="35%" stop-color="limegreen" stop-opacity=".4"/>
            <stop offset="50%" stop-color="limegreen" stop-opacity=".3"/>
            <stop offset="65%" stop-color="limegreen" stop-opacity=".4"/>
            <stop offset="80%" stop-color="limegreen" stop-opacity=".6"/>
            <stop offset="94%" stop-color="limegreen" stop-opacity="1"/>
            <stop offset="100%" stop-color="limegreen" stop-opacity="1"/>
        </linearGradient>
    </defs>
    <svg xmlns="http://www.w3.org/2000/svg" id="or" class="sprite" viewBox="0 0 100 50"
        preserveAspectRatio="none" width="100%" height="100%">
        <g>
            <path d="M 10,0 L 90, 50" stroke="url('#orangeGradient')" stroke-width="6" fill="none"/>
        </g>
    </svg>
    <svg xmlns="http://www.w3.org/2000/svg" id="bl" class="sprite" viewBox="0 0 100 50"
        preserveAspectRatio="none" width="100%" height="100%">
        <g>
            <path d="M 10,0 L 90, 50" stroke="url('#dodgerblueGradient')" stroke-width="6"
                fill="none"/>
        </g>
    </svg>
    <svg xmlns="http://www.w3.org/2000/svg" id="gr" class="sprite" viewBox="0 0 100 50"
        preserveAspectRatio="none" width="100%" height="100%">
        <g>
            <path d="M 10,0 L 90, 50" stroke="url('#limegreenGradient')" stroke-width="6"
                fill="none"/>
        </g>
    </svg>
    <svg xmlns="http://www.w3.org/2000/svg" id="vi" class="sprite" viewBox="0 0 100 50"
        preserveAspectRatio="none" width="100%" height="100%">
        <g>
            <path d="M 10,0 L 90, 50" stroke="url('#violetGradient')" stroke-width="6" fill="none"/>
        </g>
    </svg>
</svg>

I would be grateful for any advice anyone might be able to provide about how to avoid reloading the same SVG file once for every unique fragment identifier: 4 in the sample, but an unmanageable 1324 in the real data that led me to explore this approach. Thank you!

3

There are 3 best solutions below

0
Danny '365CSI' Engelman On

Are you very sure the file is reloaded?

Retrieval from cache will/can show in the Network tab:

(in this image I am doing client-side generation of SVG) but that is basically the same)

0
obdurodon On

Thanks, Danny! Apologies for "answering" instead of "commenting", but I don't seem to be able to get an image into a comment. Apologies also that I haven't been able to get @ to work for a proper tag.

In your image reloads from the same resource report "from memory cache" in the Size column and show 0 time in the Time and Waterfall columns. My results show a real size and time, and the Waterfall column seems to confirm that the times are cumulative. This leads me to think that the browser reloads the same SVG document with each new fragment identifier.

Chrome developer tools network panel

0
obdurodon On

OP here with a partial answer to my question. The report below is based on my real data, which has 1324 small SVG background images, combined in a “sprite stack” arrangement in a single SVG file.

TL;DR:

  1. Browsers treat SVG sprite files differently when loaded from the file system than when loaded from an http server. My original posting was loading from the filesystem, where all browsers reload the SVG sprite file once for each fragment identifier. When loading from an http server the SVG sprite file is loaded only once in Chrome, Edge, and Safari, with full rendering time hovering around 1–2 seconds. This is the behavior I wanted and expected—except that I want it from the file system, too.
  2. Firefox alone seems to reload the SVG sprite file once per fragment identifier, even when loading from an http server, when it is not already in the cache. Full rendering time takes more than 2 minutes.
  3. Firefox loads the SVG sprite file from an http server only once when it is already in the cache, but full rendering time nonetheless takes more than 2 minutes.

Details:

Perhaps this should have occurred to me originally (= I’ll know better next time), but the behavior is different when loading from the file system vs from an http server. When I access the files on the file system, which is what I was doing when I posted the original inquiry, all of the browsers I tested reload the (identical) SVG file once for each fragment identifier, which defeats the purpose of using sprites in the first place. When I access the files on an http server, though, Chrome, Edge, and Safari load the SVG file only once and then render quickly, which is the behavior I wanted and expected—except that I also want it when loading from the file system.

Firefox seems to behave differently in two ways. To compare behavior first with everything in the cache, I loaded the main HTML file (which loads one external CSS stylesheet and the one SVG sprite file) from an http server (to get it into the cache) and then loaded it again in a different browser tab by pasting the URL into the address bar and hitting Enter. Here are the reports from the browser development tools (note the total load time, which I’ve highlighted):

  • Chrome: 3 requests; Finish 214ms; DOMContentLoaded 206ms; Load 1.09s. The SVG and CSS are loaded from memory (0ms); HTML is retrieved.
  • Edge: 2 requests; Finish 106ms; DOMContentLoaded 909ms; Load 973ms. The SVG is loaded from memory (0ms); HTML is retrieved; external CSS file does not show up in the network report.
  • Safari: 3 resources; 2.94MB total; 1.51MB transferred; Loaded in 123ms. SVG and CSS are loaded from memory (0ms); HTML is retrieved.
  • Firefox: 1327 requests; Finish 7.75s; DOMContentLoaded 825ms; Load 2.37min. SVG and CSS are loaded from memory (0ms); HTML is retrieved.

I’ve highlighted the total load time because it’s an extreme difference: Firefox finishes loading and rendering in minutes, while the other three take roughly a second.

When I load the file when it is not in the cache, Firefox again behaves differently from the other three browsers. To test this I selected “Disable cache(s)” in the developer tools for all browsers and then loaded the file from an http server by pasting the address into the address bar and hitting Enter. I’ll skip the details this time, but the important differences are that Chrome, Edge, and Safari all load from the server and render with total reported times of 3.72s (Chrome), 2.09s (Edge), and 255ms (Safari). None report accessing the cache and all show load times for the SVG file just once in the waterfall. Meanwhile, Firefox shows a total load time of 2.14min and the waterfall shows that it loads the SVG file once per fragment identifier. To ensure that the “Disable cache” setting wasn’t preventing caching even within a request, I reran the Firefox test without checking the “Disable cache” box, and the total load time was (again) 2.14min.

I referred to the above as a “partial answer” to my question because I still don’t understand two details:

  1. Why should an SVG sprite file load only once from an http server but reload on every fragment identifier when loaded from the file system, and is there a way to get avoid the reloads from the file system? Being able to load quickly from the file system, without having to run the data through an http server, is important for my application because end-users will run the program that creates this report on their own data repeatedly during their development work, and I don’t want to require them to interject an http server into the workflow in order to see the results.
  2. Is the much slower rendering by Firefox (alone), even when it doesn’t reload the SVG sprite file repeatedly, a problem with Firefox, or can Firefox be persuaded to load the SVG sprite file only once?

Thanks again to those who responded earlier!