resize svg wrapped in div that is wrapped in div

Viewed 770

I'm having a problem coverting inline SVG to be wrapped in divs. I was using nested SVGs before and now I'm told I have to use nested divs with inline SVG.

Basically, I need the SVG to be sized to the "container" - the "container" is size to the browser window.

For an example of what works before I tried the whole div thing:

SVG Only Example - works perfectly

<html>
<body>
    <svg id="container" style="position:relative;border:dashed;width:100%;height:100%;" viewBox="0 0 1000 500">
        <svg id="background" name="Box" x="0" y="0">
            <rect width="1000" height="500" stroke="lime" fill="blue" stroke-width="10" />
            <svg id="shape" name="Triangle" x="275" y="50" fill="red" stroke="yellow" stroke-width="3">
                <path d="M0,378L185,0L371,378Z" />
            </svg>
        </svg>
    </svg>
</body>
</html>

But when I try to wrap divs around them, it just stays the same size as my viewBox, no matter what I've tried. I've looked up a lot on this on SO and elsewhere and nothing seems to work: padding tricks, 100vw, width, height, etc.

Here's the latest of what I've tried:

SVG wrapped in DIV Example - doesn't behave the same

<html>
<body>
    <div id="container" style="position:relative;border:dashed;width:100%;height:0;margin:5px">
        <div id="background" style="position:absolute;left:0px;top:0px;width:1000px;height:500px;">
            <svg name="Box" viewBox="0 0 1000 500">
                <rect width="1000" height="500" stroke="lime" fill="blue" stroke-width="10" />
            </svg>
            <div id="shape" style="position:absolute;left:275px;top:50px;width:371px;height:378px;">
                <svg name="Triangle" viewBox="0 0 371 378" fill="red" stroke="yellow" stroke-width="3">
                    <path d="M0,378L185,0L371,378Z" />
                </svg>
            </div>
        </div>
    </div>
</body>
</html>

I put a "border:dashed;" in the first div, just to make sure it is resizing with the browswer window and it is. It's just that everything inside that div doesn't change.

Any advice on how to get the "wrapped in div" strategy to match the "plain SVG" strategy?

More clarity: I guess what I'm saying is that "background" shape needs to be 1000w x 500h, relative to the "container" size. Any of it's children need to be absolutely positioned inside of that 1000w 500h and relative to it. The "container" size is the available space. So if if the browser window is 3000w x 2000h, then technically the "background" shape should be 3000w x 1500h (and the child shapes resize accordingly too - but the stay in their original relative position - relative to 1000w x 500h). If the window 800 w by 600 h, the "background" and child shapes shrink to fit that, relatively. Just like the SVG example.

It might be helpful to take the SVG example above, save it as an html file, launch locally and resize your browser up and down. That's what I'm to find help with, but divs don't seem to know how to handle this.

3 Answers

There's no real equivalent to viewBox property of SVG on DIV elements. The closest one would transform scale. So if you give your div containers a specific size in pixel without adjusting on resize, you're going to somewhat override the viewbox calculations.

That being said, if your div containers are resized with the window, then it can work. Also with minimal javascript you can get the same result as viewbox.

For the css only solution, you define your div containers so they are 100% the size of the window. The SVG viewbox is then still making the calculations. You'll need to define a preserveAspectRatio to have the same behavior as your SVG example. Like this:

html, body {
  width: 100%;
  height: 100%;
  padding: 0px;
  margin:0px;
  
}

* {
  box-sizing: border-box;
}
<div id="container" style="position:relative;border:dashed;width:100%;height:100%;">

   <svg name="Box" style="position:relative;width:100%;height:100%;" viewBox="0 0 1000 500" preserveAspectRatio="xMinYMin">
     <rect width="100%" height="100%" stroke="lime" fill="blue" stroke-width="10" />
   </svg>
   <div id="shape" style="position:absolute;left:0px;top:0px;height:100%;width:100%;">
     <svg style="position:relative;width:100%;height:100%;" viewBox="0 0 1000 500" preserveAspectRatio="xMinYMin">
       <svg id="shape" name="Triangle" x="275" y="50" width="371" height="378" fill="red" stroke="yellow" stroke-width="3">
         <path d="M0,378L185,0L371,378Z" />
       </svg>
     </svg>
   </div>
 </div>

For the javascript solution, you define the size on your div containers, and your svg can then be relative and not have the positioning info. And on resize you adjust the scale based on the innerWidth. Like this:

window.onload = window.onresize = calculateScale

function calculateScale() {
  var scaleFactor = innerWidth / 1000;
  document.getElementById('container').style.transform = `scale(${scaleFactor})`

}
html,
body {
  width: 100%;
  height: 100%;
  padding: 0px;
  margin: 0px;
}

* {
  box-sizing: border-box;
}
<div id="container" style="position:absolute;border:dashed;width:1000px;height:500px;transform-origin: left top">

  <svg name="Box" style="position:relative;width:100%;height:100%;">
     <rect width="100%" height="100%" stroke="lime" fill="blue" stroke-width="10" />
   </svg>
  <div id="shape" style="position:absolute;width: 371px;height: 378px;top: 50px;left: 275px;">

    <svg style="width: 100%; height: 100%" id="shape" name="Triangle" fill="red" stroke="yellow" stroke-width="3">
         <path d="M0,378L185,0L371,378Z" />
       
     </svg>
  </div>
</div>

I think I got what you are looking for but this is two different svgs not the same vector image.

I'm wrapping both svgs with a div and im overlaying the second div using an absolute position.

<html>
<body>
    <div id="containerouter" style="position:relative;border:dashed;width:100%;height:100%;margin:0;">
        <div id="background" style="position:relative;width:100%;height:100%;">
            <svg name="Box" viewBox="0 0 1000 500">
                <rect width="1000" height="500" stroke="lime" fill="blue" stroke-width="10" />
            </svg>
        </div>
        <div id="shape" style="position:absolute;height:100%; width:auto;left:175px;top:10%;">
            <svg name="Triangle" viewBox="0 0 371 378" fill="red" stroke="yellow" stroke-width="3" x="0" y="0" height="75%">
                <path d="M0,378L185,0L371,378Z" />
            </svg>
        </div>
    </div>
</body>
</html>

Like this?

<html>
<body>
    <div class="container" style="width:100%; height:100%;">
        <div class="container" style="width:100%; height:100%;">
            <svg id="container" style="position:relative;border:dashed;width:100%;height:100%;" viewBox="0 0 1000 500">
                <rect width="100%" height="100%" stroke="lime" fill="blue" stroke-width="10" />
                <foreignObject x="275" y="50" width="371px" height="378px">
                    <div class="container" style="width:100%; height:100%;">
                        <svg id="shape" name="Triangle" x="0" y="0" fill="red" stroke="yellow" stroke-width="3" width="100%" height="100%">
                            <path d="M0,378L185,0L371,378Z" />
                        </svg>
                    </div>
                </foreignObject>
            </svg>
        </div>
    </div>
</body>
</html>

EDIT: Changed the snippet.

This version uses a <foreignObject> to wrap the inner svg with a div element. Outcome is as expected.

If you are trying to have something like this:

<div>
  <div>
    <svg> <!-- rect --> </svg>
  </div>

  <div>
    <svg> <!-- triangle --> </svg>
  </div>
</div>

Then you'll need to overlay the divs on top of one another and you can't manipulate the svg any further

Related