Nonstandard view of an external SVG document embedded in HTML via a fragment identifier or view specification: How to manipulate the viewBox? Code Answer

Hello Developer, Hope you guys are doing great. Today at Tutorial Guruji Official website, we are sharing the answer of Nonstandard view of an external SVG document embedded in HTML via a fragment identifier or view specification: How to manipulate the viewBox? without wasting too much if your time.

The question is published on by Tutorial Guruji team.

NOTE: This question has been substantially rewritten, which on the one hand makes everything much more concise, but on the other hand might have robbed some of the comments of their context. I hope the conversation is still understandable.

I am embedding SVG images as <object>sin an HTML page, and make them zoomable and pannable with JavaScript, where all the event handlers are called on the rootElementof each object’s contentDocument, i.e. the <svg>, and manipulate its viewBox.baseVal property.

A minimal working example (reduced to the panning functionality) follows. We have an SVG file called circle_and_rectangle.svg:

<svg style="background: white" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
  <rect x="0.5" y="0.5" width="299" height="299" style="fill:grey"/>
  <circle cx="150" cy="150" r="75" style="fill:red"/>
</svg>

and an HTML file that imports this SVG file in an <object> element:

<!DOCTYPE html>
<title>External SVG embedding</title>
<script>
  window.onload = function () {
    svgRoot = document.getElementById('foo').contentDocument.rootElement
    svgRoot.addEventListener('mousedown', activatePanning,   false);
    svgRoot.addEventListener('mouseup',   deactivatePanning, false);
  }
 
  function activatePanning (event) {
    this.addEventListener('mousemove', panOnMouseMove, false)
  }

  function panOnMouseMove (event) {
    baseVal = this.viewBox.baseVal
    baseVal.x -= (event.movementX / this.clientWidth)  * baseVal.width
    baseVal.y -= (event.movementY / this.clientHeight) * baseVal.height
  }

  function deactivatePanning (event) {
    this.removeEventListener('mousemove', panOnMouseMove, false)
  }
</script>

<object id="foo" style="width:400px;height:300px;border: 1px solid green"
    type="image/svg+xml" data="circle_and_rectangle.svg">
</object>

As intended, this shows a red circle against a grey square (inside a slightly wider green frame) which can be dragged back and forth with the mouse.

NOTE: This only works when the files are accessed via a server, whether on the Web or on the local computer. (See comments for details. As soon as I’ve found one that lets me sign up without Facebook & the like, I’ll put them on a suitable code-sharing website.)

I’ve now found it would be very useful if I could embed just partial views of the SVG images, which SVG makes possible by either of the following two methods, (I’ve left out required attributes for the sake of brevity):

<object ... data="circle_and_rectangle.svg#svgView(viewBox(75,75,150,150))"></object>

<!-- or, the SVG contains: -->
<view id="closeup" viewBox="75 75 150 150" />
<!-- and the HTML, instead of the above: -->
<object ... data="circle_and_rectangle.svg#closeup"></object>

As expected, either results in a close-up (or zoomed-in) view of the red circle. However, the panning no longer works; the image is just fixed. On inspection:

  • the viewBox values as accessed as described above (the contentDocument.rootElement.viewBox.baseVal property of the <object>) are the standard ones of said <svg> root element (i.e. the 0 0 300 300 in its viewBox attribute), not those of the viewBox actually used by the browser and specified directly in the URI resp. in the referenced <view>element.
  • these values do get manipulated by the handlers as before, but that no longer has any visible effect

The described behaviour is identical in Chrome and Firefox (on Linux), regardless of whether a view specification (#svgView(...)) or a fragment identifier (#closeup) with a corresponding <view id="closeup"> element in the SVG is used.

I’ve poked around the DOM of the contentElement and its rootElement and searched JavaScript references and the MDN and the Web for the better part of the afternoon, but nothing tangible has materialised on the following two questions:

  • how can I access the viewBox values that are actually used in the above scenario (since the standard viewBox apparently isn’t)?
  • what exactly causes the nonfunctioning of the handlers? Since they do still manipulate the viewBox values of the <svg> element: is it indeed simply that they now operate on the wrong viewBox?
  • more generally, is there any reliable way to pan and zoom when the SVG is loaded in such a nonstandard view?

Answer

When you use a non default view (either <view> or #svgView()), its parameters will be used instead of the ones of the root element.

So if you set a viewBox parameter through this view, the root element’s viewBox will get discarded, and editing it won’t change anything.

There used to be an SVGSVGElement.currentView property being defined in SVG1.1, but it got dropped in SVG2 and among popular modern browsers, only Safari did implement it.

Since there is no clean API to deal with it, you will need to do something a bit dirty by checking the location.hash if it’s pointing to such a view and edit it or the element it points to.

However if I understand your case correctly, you have full control over the svg and you know for sure you’ll be using such a view, so the easiest is probably to use a <view> element and then to edit its viewBox attribute, except that Chrome is buggy and doesn’t update the view when this happens…

Here is a live snippet where the edition is made from inside the svg document itself since StackSnippets iframes don’t allow accessing the inner doc which only works in Firefox.

const svg_content = `
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" >
  <view id="my_view" viewBox="0 0 400 400" />
  <rect fill="red" x="0" y="0" width="20" height="20"/>
  <script>
    onmousemove = (evt) => {
      const view = document.getElementById("my_view");
      const viewBox = view.viewBox.baseVal;
      viewBox.x = evt.clientX * -1;
      viewBox.y = evt.clientY * -1;
    };
  </script>
</svg>`;
const blob = new Blob([svg_content], { type: "image/svg+xml" });
const url = URL.createObjectURL(blob);
const object = document.getElementById("obj");
object.data = url + "#my_view";
object {
  border: 1px solid;
}
<object id="obj"></object>

And even changing the <object>‘s data with an updated #svgView() won’t work in that browser, apparently crashing the parser…

So I’m sorry to be the one to tell you, but there doesn’t seem to be any cross-browser way to do what you want.

We are here to answer your question about Nonstandard view of an external SVG document embedded in HTML via a fragment identifier or view specification: How to manipulate the viewBox? - If you find the proper solution, please don't forgot to share this with your team members.

Related Posts

Tutorial Guruji