SVG childNodes return text - javascript

I have an inline SVG and try to loop over all paths within a group element. When using childNodes I found out that Browsers add an extra text child for every path. I am curious why browsers are doing so and if there is a smart way to just loop over real child elements.
I've created a little JSBin to demonstrate the behaviour: http://jsbin.com/tutisakege/1/edit?html,css,js,console,output
(Check the output of the console)
HTML
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100">
<g id="test">
<path d="M50 0 L0 100 L100 100 Z" />
<path class="red" d="M25 0 L25 25 L75 0 L75 25 Z" />
</g>
</svg>
JS
var group = document.querySelector('#test'),
children = group.childNodes;
The childrenobject now holds 5 entries even though my test group only has 2 paths.
Note: I know I could loop over all entries and check whether I have an instance of SVGPathElement but that seems a but cumbersome to me.
(I've tested it in the latest Chrome and Firefox)

Browsers didn't add it, they are right there in the document source. There is whitespace between the path elements i.e. carriage returns and spaces.
You can skip the text by using element.children but that apparently only works on Firefox and Chrome so if you want it done portably you'll probably need to stick with checking for element instances as you suggest in the question.

Related

Clearing Elements in SVG group

So I have a program that is drawing circles to represent charges, and it can also draw electric field lines and field magnitude lines. However, I want to add in the functionality of being able to erase all of the field lines and leave the charges, or erase all of the elements. Everything that is drawn goes into this svg:
<svg version="1.1" id="svg" x="0px" y="0px"
width="710px" height="510px" viewBox="0 0 710 510" style="enable-background:new 0 0 700 500;" xml:space="preserve" onmouseup="mouseUp(evt)">
<defs>
<marker id="markerArrow" viewBox="0 0 10 10" refX="1" refY="5"
markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</defs>
<g id="gridLineGroup"></g>
<g id="fieldLineGroup"></g>
<g id="chargesGroup"></g>
</svg>
As you can see, there are groups created for each of the different types of drawings. So once I want to clear any one of those three groups, I want it to delete all of the elements in that group but leave it to be filled up again. I don't want the user to have to restart the webpage to redo any mess ups. I've looked around a lot and have been unable to find a solution that works for my program. It is a stock html program.
The latest function I tried to run was a button click that ran this function:
function clearLines() {
document.getElementById("svg").removeChild("fieldLineGroup");
}
Thank you for any help.
I created a fiddle here: https://jsfiddle.net/mattbatman/1vqkgqp4/.
I used remove over the collection instead of removeChild.
I selected all paths within the group, made the selection an array so I could iterate over it with forEach, and called remove on each element:
button.addEventListener('click', () => {
const paths = [].slice.call(document.querySelectorAll('g#gridLineGroup path'));
paths.forEach(path => { path.remove(); });
});

Why is SVG textPath not working in Firefox when rendered by React

I'm trying to render a react component with an inline SVG element that has a text along a path. This is what is returned from the render method:
<div className="textsvg">
<svg width="100%" height="100%" viewBox="-50 -50 100 100">
<defs>
<path id="textPathTop" d={`
M 0 40
A 40,40 0 0 1 0,-40
A 40,40 0 0 1 0,40`}></path>
<path id="textPathBottom" d={`
M 0 -41.8
A 41.8,41.8 0 0 0 0,41.8
A 41.8,41.8 0 0 0 0,-41.8`}></path>
</defs>
<use xlinkHref="#textPathBottom" fill="none" stroke="red"></use>
<text fill="red" fontSize="4.5"><textPath xlinkHref="#textPathBottom">We go up, then we go down, then up again</textPath></text>
</svg>
</div>
This shows the "We go up, then we go down, then up again" text, but just in a straight horizontal line starting from 0,0.
Copying the resulting html into a codepen shows the result as it should look, using the textPath.
Why is the textPath ignored when rendered with ReactJS?
Using React 15.3.1 and checking in FF 52.0.2(32bit)
Already tried using _dangerouslySetInnerHTML for textPath, but that didn't work either.
Check if you have a <base href="..."> tag in your <head> element.
If so, Firefox won't be able to display your text, while Chrome will.
Firefox is searching for your xlink:href attribute at the base href url, it does not find it, so the text is just ignored.
A workaround is to use an absolute url :
<textPath xlink:href="http://pathtoyourpage#textPathBottom">
It is easier if you generate the svg with javascript :
textPath.setAttribute('xlink:href', `${location.href}#textPathBottom`)
Some similar weird behavior happen with mask and filter attributes.

Animate SVG path from <use> tag

I'm trying to animate an svg path using this technique by Jake Archibald
The technique works well when the svg code is pasted in my document, but fails when I'm referencing the svg from my "defs.svg" file.
My defs.svg looks like this:
<svg xmlns="http://www.w3.org/2000/svg">
<symbol viewBox="0 0 48 50.6" id="icon-result" fill="#FFF">
<path d="M19 49.4c-2 1.7-5.1 1.5-6.9-.5l-11-12.8c-1.7-2-1.5-5.1.5-6.9 2-1.7 5.1-1.5 6.9.5l11 12.8c1.8 2.1 1.6 5.2-.5 6.9z"/>
<path d="M13.6 49.4c-2.2-1.5-2.9-4.5-1.4-6.8L39 2.2C40.5 0 43.5-.7 45.8.8c2.2 1.5 2.9 4.5 1.4 6.8L20.3 48.1c-1.4 2.2-4.5 2.8-6.7 1.3z"/>
</symbol>
</svg>
In my code I do the following:
<svg class="Dashboard__nav-icon">
<use xlink:href="/svg/defs.svg#icon-result" />
</svg>
When trying to animate the code this way I get null when trying to get the path by using Jake Archibalds technique linked above. Is there a way to get the path while still using a separate defs.svg file and the <use> tag?
You won't be able to get the DOM path element via the second <svg> element. That is because elements referenced via a <use> do not appear in the DOM tree where they are used. So the following will not work:
var path = document.querySelector('.Dashboard__nav-icon path');
The symbols elements are not visible in the main document's DOM tree.
However you would be able to reference the paths directly via their definition. But for that to work you would need to inline the defs.svg file.
So, if the defs.svg is in the same file, then you would be able to use:
var path = document.querySelector('#icon-result path');

Movement in html5 canvas

I am trying to figure out the right way to do movement in canvas. From my research it appears that a lot of tutorials use redrawing. I got movement to work by clearing my shapes and recreating them (redrawing?): http://pastebin.com/VuYdtM2U
I am wondering though if that is the way it is supposed to be done or if there is a better way. I can imagine clearing an image and creating it a pixel over every fraction of a second would get resource intensive.
Generally, It is done this way only. Most of the Browser Games are coded this way and a lot of clearing and re rendering takes place seamlessly. Modern Browsers can handle a lot of rendering without any delay.
The main reason behind this is that, Canvas is just a sort of image for the browser and it just draws on it and don't have to retain any Element DOM Structure.
Whereas in case of SVG, all elements have to be appended / deleted /added to the DOM Structure which takes toll if there are a lot of elements.
There are several factors which can help us decide which technique is better as per the case.
Generally for Page with DOM elements less than 10,000... Both are efficient.
You can also use mixture of Canvas , SVG and Multiple Canvas (like you may want to show a hover canvas, over another canvas)..
Try this
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height=400 width=500>
<g>
<rect transform="translate(-20,-20)" height="40" width="40" style="fill:#777; stroke:none;"/>
<animateMotion fill="freeze" path="M 0 0 Q 190 160 150 70 T 200 150 T 300 200 T 200 200" dur="3.14159s" repeatCount="indefinite"/>
</g>
<path d="M 0 0 Q 190 160 150 70 T 200 150 T 300 200 T 200 200" style="fill:none;stroke:#F00;stroke-width:5"/>
</svg>

Can't read SVG element contents in mobile Safari with Javascript

To reuse certain SVG objects I have symbols defined in an SVG element which is written out at the top of my DOM. To display an SVG I can do:
<svg><use xlink:href="#symbol-identifier" /></svg>
To animate some SVG's I use Snap.svg, but I can't seem to create a Snap object from an SVG referenced by an xlink. To work around this I want to inject the SVG contents (already present in the DOM) at runtime into the SVG currently using the xlink.
To do this I need to get the contents (innerHTML?) of a symbol. This works in every browser i tested, except for mobile safari.
Below a simple test setup (without the symbol-wrap, but works the same):
HTML:
<div id="outer" style="width: 100px; height: 100px;">
<svg id="inner" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="10" cy="10" r="5" fill="#ffffff" />
</svg>
</div>
Javascript:
var outerElement = document.getElementById('outer');
var innerElement = document.getElementById('inner');
outerElement.innerHTML // returns <svg id=".. etc..
innerElement.toString() // returns [object SVGSymbolElement]
innerElement.getAttribute('viewBox') // returns 0 0 100 100
innerElement.innerHTML // returns undefined (in Mobile Safari)
// Everywhere else the content <circle .. etc .. is returned.
Why is this? And is there another way of getting the SVG contents as a string, without regexping away all the unwanted HTML around the outerElement.
XMLSerializer can serialize elements.
var string = XMLSerializer().serializeToString(innerElement);
should work for you.

Categories

Resources