Search code examples
javascriptsvgevent-handlingresize

Capture SVG onresize() in plain JavaScript?


I am writing an application in plain JavaScript and want to define a draw() method that is called whenever an SVG is resized. Unfortunately, simply assigning svg.onresize = draw does not work, nor does the equivalent svg.AddEventListener('resize', draw).

<html><body style="margin:0">

<svg id="svg" width="100%" height="100%"></svg>

<script>

let svg = document.getElementById("svg");

function line(x1, y1, x2, y2)
{
    let e = document.createElementNS(svg.namespaceURI, 'line');
    e.setAttribute('x1', x1);
    e.setAttribute('y1', y1);
    e.setAttribute('x2', x2);
    e.setAttribute('y2', y2);
    e.setAttribute('style', 'stroke:#000');
    svg.appendChild(e);
}

function frame_rect(r)
{
    let e = document.createElementNS(svg.namespaceURI, 'rect');
    e.setAttribute('x', r.x);
    e.setAttribute('y', r.y);
    e.setAttribute('width', r.width);
    e.setAttribute('height', r.height);
    e.setAttribute('style', 'stroke:#000;fill:none');
    svg.appendChild(e);
}

function draw()
{
    svg.innerHTML = ''; // remove all elements from the SVG
    let r = svg.getBoundingClientRect();
    line(r.x,r.y,r.x+r.width,r.y+r.height);
    line(r.x,r.y+r.height,r.x+r.width,r.y);
    frame_rect(r);
}
draw();


// onresize = draw;
// Works if entire page is resized, or developer tools opened.
// But misses other resize causes like splitter dragged, tab expanded, etc.

// svg.onresize = draw;
svg.addEventListener('resize', draw);
// Either of the above *should* work, per:
//
//   https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
//
// But neither does, even if the entire window is resized.

svg.onclick = draw;
// Just to show that draw() can be called in response to an event.

</script></body></html>

As explained in the comment, I can use window.onresize, but this is a hack that will only catch SVG resizes that are caused by the entire window being resized. OK, and it also happens to work if the SVG is resized due to the developer tools being opened, but that is presumably because this also resizes the entire window.

Also, svg.onclick = draw causes the SVG to be redrawn in response to a mouse click, so it seems like I'm attaching the draw() handler to the correct object. Why would onclick be any different from onresize in terms of ability to catch events?

What is the proper way to catch SVG resizes that have been propagated from any source, and not just window resizes?


Solution

  • The browsers don't seem to support the SVGResize event. But it is simple enough to check if the SVG size has changed in a window resize event handler.

    let svg = document.getElementById("svg");
    let lastSize = null;
    
    function line(x1, y1, x2, y2)
    {
        let e = document.createElementNS(svg.namespaceURI, 'line');
        e.setAttribute('x1', x1);
        e.setAttribute('y1', y1);
        e.setAttribute('x2', x2);
        e.setAttribute('y2', y2);
        e.setAttribute('style', 'stroke:#000');
        svg.appendChild(e);
    }
    
    function frame_rect(r)
    {
        let e = document.createElementNS(svg.namespaceURI, 'rect');
        e.setAttribute('x', r.x);
        e.setAttribute('y', r.y);
        e.setAttribute('width', r.width);
        e.setAttribute('height', r.height);
        e.setAttribute('style', 'stroke:#000;fill:none');
        svg.appendChild(e);
    }
    
    function resize(evt)
    {
        let r = svg.getBoundingClientRect();
        if (lastSize && (lastSize.width !== r.width || lastSize.height !== r.height))     {
          draw();
        }
    }
    
    function draw()
    {
        svg.innerHTML = ''; // remove all elements from the SVG
        let r = svg.getBoundingClientRect();
        line(r.x,r.y,r.x+r.width,r.y+r.height);
        line(r.x,r.y+r.height,r.x+r.width,r.y);
        frame_rect(r);
        lastSize = r;
    }
    draw();
    
    window.addEventListener('resize', resize);
    body {
      margin: 0;
    }
    <svg id="svg" width="100%" height="100%"></svg>