jQuery add attribute to SVG string - javascript

Using jQuery I'm trying to add an ID attribute to the 'path' element of the following text template. The text is loaded with RequireJS and is referenced with a variable name (say mySvg).
The intention, is to have a loop based on the number of buttons required, and give each 'path' element (near bottom of markup) a unique ID, after which the text gets appended to a DOM element.
I'd be massively grateful if anyone can show me how (using jQuery) to add the ID attribute and value when the markup is in it's raw text format (ie before it's been added to the DOM).
Many thanks in advance...
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="141px" height="58px" viewBox="0 0 141 58" enable-background="new 0 0 141 58" xml:space="preserve">
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="70" y1="62.5" x2="70" y2="-72.5">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.5667" style="stop-color:#3FA9F5"/>
</linearGradient>
<path class="svg button" fill="url(#SVGID_1_)" stroke="#000000" stroke-miterlimit="20" d="M137.5,43.5c0,6.627-5.373,12-12,12h-111
c-6.627,0-12-5.373-12-12v-30c0-6.627,5.373-12,12-12h111c6.627,0,12,5.373,12,12V43.5z"/>
</svg>

You can use JQuery to operate on fragments before they are appended to the DOM like this.
var e = $(yoursvg);
e.find("path").attr("id", "abcd");
// and then for example add it later
e.appendTo($('body'));
Demo here

Related

Trying to access SVG elements generated with <use> with JavaScript

I'm trying to use JavaScript (velocity.js) to animate an SVG that I've defined using <defs> and then instantiated with <use> and I'm having trouble accessing the DOM element of the SVG component I'm trying to modify. My code works with normal inline SVG just fine, but when I switch to the <defs>/<use> method it breaks.
When I use the inline SVG I can console.log the element in question and it returns information, but trying to access that same element generated with <use> returns an empty object. Is there anything in particular I need to be doing when trying to access the DOM elements of SVG generated with <use>?
The HTML
<div class="screen">
<svg>
<use href="/media/defs.svg#poppyIdle"></use>
</svg>
</div>
and top of defs.svg (generated by inkscape, all I did was add <defs> and <symbol>) (don't want to post the whole thing)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
<defs>
<symbol id="poppyIdle" viewBox="0 0 140 250">
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer12"
inkscape:label="eyelids"
style="display:inline"
transform="translate(-6.125676,0.02323548)">
<rect
style="fill:#4d4d4d;fill-opacity:1;stroke-width:0.0694716"
id="rect942-3"
width="28.423023"
height="2.0695279"
x="34.666679"
y="34.632057" />
<rect
style="display:inline;fill:#4d4d4d;fill-opacity:1;stroke-width:0.0694716"
id="rect942-3-6"
width="28.423023"
height="2.0695279"
x="85.931053"
y="34.629658" />
</g>
</symbol>
</defs>
</svg>
SVG use elements are like shadow DOM elements in HTML. Only the attributes of the USE element itself is exposed via the SVG DOM - you cannot alter any properties on a single USE element instance that are being cloned from the original symbol. It's not like a macro.

Reference SVG effect without using url() function

We are using Svelte/Sapper front-end with client-side routing. When user is navigating between different pages, SVGs on the page sometimes glitch-out. Example:
(SVG source in the end of the question)
This glitching is caused by using HTML <base> tag and referencing linear gradient effect with CSS url() function. This results in invalid URL (base + element id) inside SVG and effect will not be applied.
Related issues to my problem:
SVG Showing up as a black box in Safari (Mobile / Desktop)
Using base tag on a page that contains SVG marker elements fails to render marker
iOS - Linear Gradients showing as Black
SVGs turn to black in Safari during transformations, akin to a bug with tag
SVG path fill rendering can break after window.history.pushState
SVG url() doesn't work under in Safari and Firefox
My question is then: Can I reference effect such as <linearGradient> without relaying on url() function to avoid this issue entirely?
Hacky solution
I solved this issue by listening to end of animation event and then adding and in setTimeout removing display: inline-block; on <svg> element. This is forcing browser to re-render it and fixes glitch after each animation. This is obviously hacky solution with lot of technical debt. I will post different answer with solution if I find different/better way of solving this.
SVG icon source:
<svg xmlns="http://www.w3.org/2000/svg" style="fill: currentColor;" class="w-8 h-8" viewBox="0 0 24 24">
<defs>
<path id="a" d="M12 10.3L2.4 1v22l9.6-9.2 1.8-1.8 5.4-5.2v10.5l-3.7-3.5-1.8 1.7 7.9 7.6V1L12 10.3zm-7.1 6.9V6.8l5.4 5.2-5.4 5.2z">
</path>
</defs>
<clipPath id="b">
<use href="#a" overflow="visible"></use>
</clipPath>
<g clip-path="url(#b)">
<linearGradient id="c" gradientUnits="userSpaceOnUse" x1="-732.18" y1="294.75" x2="-731.55" y2="294.75" gradientTransform="scale(47.46 -47.46) rotate(45 -9.66 1030.9)">
<stop offset="0" stop-color="#201351"></stop>
<stop offset=".25" stop-color="#201351"></stop>
<stop offset=".39" stop-color="#620f3c"></stop>
<stop offset=".53" stop-color="#990b2a"></stop>
<stop offset=".65" stop-color="#c1081e"></stop>
<stop offset=".74" stop-color="#d90716"></stop>
<stop offset=".8" stop-color="#e20613"></stop>
<stop offset="1" stop-color="#e20613"></stop>
</linearGradient>
<path fill="url(#c)" d="M-8.6 12L12-8.6 32.6 12 12 32.6z"></path>
</g>
</svg>
The cleanest solution would be to reference all icons from one SVG sprite file at a static address.
For example https://example.com/assets/sprite.svg would quote the icon markup in <symbol> elements:
<svg xmlns="http://www.w3.org/2000/svg">
<!-- use the viewBox from your icon <svg> element -->
<symbol id="unique-icon" viewBox="0 0 24 24">
<!-- quote everything inside your icon <svg> element -->
</symbol>
<!-- more icons -->
</svg>
and icon usage would look like this:
<svg><use xlink:href="https://example.com/assets/sprite.svg#unique-icon></svg>`.
Note you can still apply styles to that <use> element, which will be inherited by the icon markup inside the shadow DOM.

Is there a way to register an onclick event handler on elements within an SVG image?

I'd like to load an SVG image within a HTML5 page and make it's components clickable. If the user moves the mouse over the shapes presented by the SVG an onclick event should perform some action. The goal is to enable the user to either click on parts of the SVG image or to click on data rows provided in a table below that SVG.
All SVG components are g elements that have a text element which in turn has text content that could be used as an ID for later on distinguishing the individual components.
SVG-Example (abbreviated by having only a single g-component and the text "abc"):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="232pt" height="260pt" viewBox="0.00 0.00 231.69 260.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g class="node">
<ellipse fill="#008000" stroke="#ffffff" cx="61.545" cy="-90" rx="46.2923" ry="18"/>
<text text-anchor="middle" x="61.545" y="-86.3" font-family="Times,serif" font-size="14.00" fill="#ffffff">abc</text>
</g>
</svg>
Is there a way to use JQuery (or some other JavaScript library or pure JavaScript code) to register onclick event handlers on these g elements? And if so how can you do it?
I just added an ID to the g element, and with simple jQuery I could manage to get the onClick event.
<svg width="232pt" height="260pt" viewBox="0.00 0.00 231.69 260.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g class="node" id="myId">
<ellipse fill="#008000" stroke="#ffffff" cx="61.545" cy="-90" rx="46.2923" ry="18"/>
<text text-anchor="middle" x="61.545" y="-86.3" font-family="Times,serif" font-size="14.00" fill="#ffffff">abc</text>
</g>
</svg>
$("#myId").on("click", function() { ... });
You can use whatever you want.
JSFiddle: http://jsfiddle.net/oc6zm1by/

Exporting to SVG in fabric with svgs doesn't match canvas

We are using Fabric.js 1.7.20. We aren't able (yet) to update to 2.0+.
http://jsfiddle.net/tcem4f4L/
The issue is that we are adding SVG objects to the map, but when they get output to an svg, some padding within the SVG causes the rendering to not look correct. The svg x/y coordinates and any lines within them need to have the stroke accounted for.
I'm trying to figure out a clean way to handle these and ensure that the bounding box on the canvas is around the SVG for aligning purposes.
Example SVG:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="95" height="11" viewBox="0 0 95 11" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="currentColor" stroke-width="1" fill="none" fill-rule="evenodd">
<rect x="0" y="0" width="95" height="11"></rect>
<path stroke-linecap="square" stroke-width="3" d="M1,9 l93,0"/>
</g>
</svg>
So (I think) to fix it on output to SVG, I'd need to change the rect x and y to 0.5 and the path to d="M1.5,9.5 l93,0"
I am worried that there are other things in this I'm missing.

D3.js Text on path not rendered (no height / width)

I am trying to render circles which have a text inside, that runs along a given path.
The markup d3 produces looks fine, but Chrome is not showing the texts.
Upon inspection it says text elements have 0 width and 0 height.
This is sample markup including only two circles:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 950 600">
<g>
<g transform="translate(334.14792673070184,58.96385042366173)">
<defs>
<path id="path-1" d="m5,50 a45,45 0 0 0 90,0"></path>
</defs>
<circle class="circle" fill="#ccc" cx="50" cy="50" r="50"></circle>
<text fill="#333" font-size="15px">
<textpath xlink:href="#path-1" start-offset="0%">123456</textpath>
</text>
<use xlink:href="#path-1" fill="#666" opacity="0.1"></use>
</g>
</g>
<g transform="translate(374.66047394649974,371.7948729806046)">
<defs>
<path id="path-2" d="m5,50 a45,45 0 0 0 90,0"></path>
</defs>
<circle class="circle" fill="#ccc" cx="50" cy="50" r="50"></circle>
<text fill="#333" font-size="15px">
<textpath xlink:href="#path-2" start-offset="0%">123456</textpath>
</text>
<use xlink:href="#path-2" fill="#666" opacity="0.1"></use>
</g>
</svg>
When I inspect the markup in Chrome console and click "Edit as HTML" on the SVG element, make a random change, save & exit - the SVG suddenly renders correctly.
The exact same thing happens in Firefox and Opera.
Copy pasting the generated markup into a jsfiddle renders everything as expected.
I have tried pulling the < defs > tags out of each individual group into a single global < defs > but it did not solve the problem.
I have also looked at user-agent-stylesheet and other CSS rules that might interfere with rendering.
Is this a problem with how the SVG tag is included and/or the container element's width/height properties? I have been trying different things to fix this for a couple of hours now...
Here is the full SVG markup http://pastebin.com/J2Lz8p23
Here are the relevant parts in my code http://pastebin.com/Bym8kJVN

Categories