ForeignObject in an SVG doesn't work - React - javascript

I'm trying to display a React component inside of an SVG. I used the foreignObject component to display my React object (ToolbarItem) inside of the SVG. However, nothing is displayed. What I did wrong?
Thanks for your help
<svg xmlns="http://www.w3.org/2000/svg" width="222.002" height="119.151" viewBox="0 0 222.002 119.151">
<g id="Margin" transform="translate(-51 -59)">
<path id="Soustraction_10" data-name="Soustraction 10" d="M10914,6398.1h0l-39-38.132v-41.828l39-38.139Z" transform="translate(-10641 -6220.475)" fill="#313c57">
<foreignObject x="40" y="40" width="100" height="100">
<ToolbarItem propKey="marginTop" type='draggableNumber' max={maxTop} />
</foreignObject>
</path>
</g>
</svg>

You can add a body tag
<foreignObject x="40" y="40" width="100" height="100">
<body>
<ToolbarItem propKey="marginTop" type='draggableNumber' max={maxTop} />
</body>
</foreignObject>
EDIT: the above works but generates a warning message.
Do the following instead :
<foreignObject x="40" y="40" width="100" height="100">
<div data-xmlns="http://www.w3.org/1999/xhtml">
<ToolbarItem propKey="marginTop" type='draggableNumber' max={maxTop} />
</div>
</foreignObject>

Related

How can I echo and execute HTML / SVG in rmarkdown without repeating the code?

For teaching purposes I would like to echo and evaluate HTML/SVG in my rmarkdown documents. Is there a way to do this without repeating code? This is my current method with code repeats (syntax highlighting is off so ignore that):
``` html
<h3>This is my SVG.</h3>
```
<h3>This is my SVG.</h3> <!-- repeated -->
``` html
<svg width="200" height="100">
<rect x="0" y="0" width="200" height="100" fill="lightblue"></rect>
<circle cx="75" cy="50" r="20" fill="blue"></circle>
<circle id="c2" cx="125" cy="50" r="20" fill="blue"></circle>
</svg>
```
<svg width="200" height="100"> <!-- repeated -->
<rect x="0" y="0" width="200" height="100" fill="lightblue"></rect>
<circle cx="75" cy="50" r="20" fill="blue"></circle>
<circle id="c2" cx="125" cy="50" r="20" fill="blue"></circle>
</svg>
``` javascript
<script src="https://d3js.org/d3.v5.min.js"></script>
```
<script src="https://d3js.org/d3.v5.min.js"></script> <!-- repeated -->
```{js}
// not repeated: echos and executes
d3.select("circle#c2").attr("fill", "red");
```
The rendered page looks like this:
(Note that I don't have this problem with JavaScript using the {js} engine, though I don't know how to include attributes in the <script> tag without repeating the line.)

How to pack text inside svg rect

I wish to fit svg text inside a rect. I could use an approach to compare the widths and add line breaks to the text, but it's not tedious.
Is there a more elegant way than this? Maybe by using CSS or d3?
UPDATE:the following code appends foreignObject using d3 but the div is not displayed. (it is there in the code inspecter)
var group = d3.select("#package");
var fo = group.append("foreignObject").attr("x", 15).attr("y", 15).attr("width", 190).attr("height", 90);
fo.append("div").attr("xmlns", "http://www.w3.org/1999/xhtml").attr("style", "width:190px; height:90px; overflow-y:auto").text("Thiggfis the dgdexsgsggs wish to fit insidegssgsgs");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<p id="p"></p>
<svg width="220" height="120" viewBox="0 0 220 120" id="package">
<rect x="10" y="10" width="200" height="100" fill="none" stroke="black"/>
</svg>
A namespace cannot be assigned by attr, it's a side effect of element creation. You need an html div so you need to tell d3 that by calling the element xhtml:div, once you do that, d3 will do the rest.
var group = d3.select("#package");
var fo = group.append("foreignObject").attr("x", 15).attr("y", 15).attr("width", 190).attr("height", 90);
fo.append("xhtml:div").attr("style", "width:190px; height:90px; overflow-y:auto").text("Thiggfis the dgdexsgsggs wish to fit insidegssgsgs");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<p id="p"></p>
<svg width="220" height="120" viewBox="0 0 220 120" id="package">
<rect x="10" y="10" width="200" height="100" fill="none" stroke="black"/>
</svg>
Here's a simple example of a foreignObject used to insert HTML markup into an SVG:
<svg width="220" height="120" viewBox="0 0 220 120">
<rect x="10" y="10" width="200" height="100" fill="none" stroke="black" />
<foreignObject x="15" y="15" width="190" height="90">
<div xmlns="http://www.w3.org/1999/xhtml" style="width:190px; height:90px; overflow-y:auto"><b>This</b> is the <i>text</i> I wish to fit inside <code>rect</code></div>
</foreignObject>
</svg>

SVG element not visible on screen, can see element added in console

I have the following SVG code. The SVG with id "nestedsvg" is being appended in the HTML, I can view it on the console. But it's not visible on the screen. I tried assigning it a z-index of 99 but still it's invisible. Where am I going wrong?
<svg data="BusinessRoleFigure" x="144" y="95"
width="128" height="66" id="outer" style="position: relative;">
<rect x="0" y="0" width="100%" height="100%"
stroke="rgb(178,178,126)" stroke-width="1" fill="rgb(255,255,181)"
style="position: relative;"></rect>
<svg id="nestedsvg" x="100%" height="100" width="50">
<rect x="-50" rx="5" ry="5" width="20" height="10" stroke="black"
stroke-width="1" fill="black" z-index="99"></rect>
</svg>
<circle cx="118" cy="13" r="5" fill="none"
stroke-linejoin="round" stroke="black"
z-index="1" stroke-width="1"></circle>
</svg>
Jsfiddle: http://jsfiddle.net/MxHPq/145/
This is because the rectangle you are drawing is outside of the nested SVG viewport.
That SVG has a width and height of 100x50, and you are drawing a 20x10 rectangle at (-50,0). Meaning the rectangle covers the area from (-50,0) to (-30,10). So it is not visible. By default, objects outside a nested SVG viewport are not visible.
There are two ways to fix this:
Make objects outside the viewport visible. You can do this by setting overflow="visible" on the nested SVG.
<svg data="BusinessRoleFigure" x="144" y="95" width="128" height="66" id="outer">
<rect x="0" y="0" width="100%" height="100%" stroke="rgb(178,178,126)" stroke-width="1" fill="rgb(255,255,181)"></rect>
<svg id="nestedsvg" x="100%" height="100" width="50" overflow="visible">
<rect x="-50" rx="5" ry="5" width="20" height="10" stroke="black" stroke-width="1" fill="black"></rect>
</svg>
<circle cx="118" cy="13" r="5" fill="none" stroke-linejoin="round" stroke="black" stroke-width="1"></circle>
</svg>
Move the rectangle inside the SVG viewport and reposition the SVG so that the rectangle ends up in the same place.
I don't know why you wanted the nested SVG to be at x="100%", but you would need to change that if you go with this solution.
<svg data="BusinessRoleFigure" width="128" height="66" id="outer">
<rect x="0" y="0" width="100%" height="100%" stroke="rgb(178,178,126)" stroke-width="1" fill="rgb(255,255,181)"></rect>
<svg id="nestedsvg" x="78" height="100" width="50">
<rect x="0" rx="5" ry="5" width="20" height="10" stroke="black" stroke-width="1" fill="black"></rect>
</svg>
<circle cx="118" cy="13" r="5" fill="none" stroke-linejoin="round" stroke="black" stroke-width="1"></circle>
</svg>
A few other notes about your original SVG:
x and y coordinates on the root <svg> element have no effect.
z-index currently has no meaning in SVGs. Although this may change for the upcoming SVG2 standard.
position: relative has no meaning in SVGs.
I've removed these things from my modified examples.

there's a 'use' tag on the page, but I cannot find the 'def' tag

I saw those html code on this website:
<a href="https://medium.com/" class="siteNav-logo" data-log-event="home">
<svg viewBox="0 0 45 45" width="45" height="45" class="svgIcon svgIcon--logoNew svgIcon-size--45px">
<use class="svgIcon-use svgIcon-use--part0" xlink:href="#svg-logoNew-45px-p0"></use>
<use class="svgIcon-use svgIcon-use--part1" xlink:href="#svg-logoNew-45px-p1"></use>
<use class="svgIcon-use svgIcon-use--part2" xlink:href="#svg-logoNew-45px-p2"></use>
<use class="svgIcon-use svgIcon-use--part3" xlink:href="#svg-logoNew-45px-p3"></use>
</svg>
<svg viewBox="0 0 95 45" width="95" height="45" class="svgIcon svgIcon--logoWordmarkNew svgIcon-size--95x45px">
<use class="svgIcon-use svgIcon-use--part0" xlink:href="#svg-logoWordmarkNew-95x45px-p0"></use>
</svg><span class="u-textScreenReader">Homepage</span>
</a>
Usually when you have a use tag inside svg, you need to use the defs tag to specify the specific shape that use is referred to. Just as follows:
<svg>
<defs>
<g id="shape">
<rect x="50" y="50" width="50" height="50" />
<circle cx="50" cy="50" r="50" />
</g>
</defs>
<use xlink:href="#shape" x="50" y="50" />
<use xlink:href="#shape" x="200" y="50" />
</svg>
But I couldn't find any defs tag on the page, so where does the browser find these external svgs(#svg-logoNew-45px-p0, #svg-logoNew-45px-p1, etc)?
They use the <symbol> tag instead of <defs>:
<div style="display: none;">
<svg xmlns="http://www.w3.org/2000/svg">
<symbol viewBox="-17 18 45 45" id="svg-logoNew-45px-p0">...</symbol>
</svg>
</div>

Is there a way to sprite raster images within an SVG element?

I have the following SVG elements containing markers to be displayed on a map in a web page. The markers are composed of the <image> elements referencing to various small PNG images:
<svg id="OL_105_svgRoot" width="1246" height="373" viewBox="0 0 1246 373">
<g id="OL_105_root" style="visibility: visible;" transform="">
<g id="OL_vroot">
<image id="P115" cx="843" cy="203" r="1" x="827" y="188" width="32" height="32" href="spider.png" ...>
<image id="P119" cx="453" cy="269" r="1" x="437" y="254" width="32" height="32" href="zoo.png" ...>
<image id="P123" cx="628" cy="82" r="1" x="612" y="67" width="32" height="32" href="wild.png" ...>
<image id="P131" cx="10495" cy="69" r="1" x="1034" y="53" width="32" height="32" href="export.png" ...>
...
</g>
</g>
</svg>
Is there any way I can replace those images with a single large image file and use something similar to CSS background-position property to specify the viewing window for each image?
Wrapping the <image> elements into inner <svg> elements, specifying a certain width and height on the <svg> element and the "background-position" on the <image> element using the x and y attributes might be a solution:
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
<svg width="17" height="19">
<image xlink:href="http://s14.postimage.org/f66u116ap/rainbow_Hello_World4.png" width="188" height="19"/>
</svg>
<svg width="17" height="19" x="12" y="19">
<image xlink:href="http://s14.postimage.org/f66u116ap/rainbow_Hello_World4.png" width="188" height="19" x="-17"/>
</svg>
<svg width="17" height="19" x="35" y="6">
<image xlink:href="http://s14.postimage.org/f66u116ap/rainbow_Hello_World4.png" width="188" height="19" x="-34"/>
</svg>
<svg width="17" height="19" x="46" y="14">
<image xlink:href="http://s14.postimage.org/f66u116ap/rainbow_Hello_World4.png" width="188" height="19" x="-51"/>
</svg>
<svg width="17" height="19" x="60" y="24">
<image xlink:href="http://s14.postimage.org/f66u116ap/rainbow_Hello_World4.png" width="188" height="19" x="-68"/>
</svg>
</svg>
(See a test on Tinkerbin.)
You could also use the clip-path attribute, but I guess this is even more tedious than the above solution.

Categories

Resources