Change color to SVG id - javascript

I have made an SVG image of a Cat in Illustrator. I have named my layers so I have my cats eyes named "eyes".
When I import the SVG to a developer window I can see that the layer name is there
<g id="eyes">
<path class="cls-1" d="M.25,766.38c2.52,9,16.26,23.83,35.15,39.06s3
...
Now via JavaScript I would like to change the color to eyes. How do I do that?
HTML:
<object id="bild" data="a.svg" type="image/svg+xml"></object>
JavaScript:
var catImage = document.getElementById( 'bild');
catImage.layerName.style="fill:red";

A SVG imported inside a <object> or <img> tag cannot be directly styled by the css or the javascript of the host page.
However, for <object>, you can gain such access thru the contentDocument property of the objectelement.
In your case, that would be ...
let cat = document.getElementById('bild').contentDocument;
But, the contentDocument property will not be available until after the page has been completely loaded and rendered. So, you will need to put your code inside an on-load event handler.
Which, in your case, that would be ...
window.addEventListener("load", function() {
let cat = document.getElementById('bild').contentDocument;
let eyes = cat.getElementById('eyes');
eyes.style.fill="red";
});

Related

How to embed SVG with Path into Html taken from a src?

I have an svg uploaded to my website and I want to embed it into my html so that it can have the structure of svg and path:
<svg>
<path id="svgPath"/>
</svg>
I managed to do it with image
<svg width="1920" height"720">
<image id="imgId" xlink:href="srclink" width="1920" height="720" />
</svg>
And when I query that image, I don't have the same methods that I have as for a path element. For example, I don't have the .getTotalLength()
const path = document.querySelector("#pathId");
path.getTotalLength(); // will work
const img = document.querySelector("#imgId");
img.getTotallength(); // does not work
I can insert it with Javascript too, if there is a way to do that while loading it from the srclink.
I need to be able to access the path as the rest of my code is dependent on that.
If it is an external *.svg file, A native Web Component <load-file> can fetch it and inject it in the DOM; either in shadowDOM or replacing the <load-file> Element, thus becoming part of the DOM you can access and style with CSS.
customElements.define("load-file", class extends HTMLElement {
// declare default connectedCallback as sync so await can be used
async connectedCallback(
// call connectedCallback with parameter to *replace* SVG (of <load-file> persists)
src = this.getAttribute("src"),
// attach a shadowRoot if none exists (prevents displaying error when moving Nodes)
shadowRoot = this.shadowRoot || this.attachShadow({mode:"open"})
) {
// load SVG file from src="" async, parse to text, add to shadowRoot.innerHTML
shadowRoot.innerHTML = await (await fetch(src)).text()
// append optional <tag [shadowRoot]> Elements from inside <load-svg> after parsed <svg>
shadowRoot.append(...this.querySelectorAll("[shadowRoot]"))
// if "replaceWith" attribute
// then replace <load-svg> with loaded content <load-svg>
// childNodes instead of children to include #textNodes also
this.hasAttribute("replaceWith") && this.replaceWith(...shadowRoot.childNodes)
}
})
Full explanation in Dev.To post: 〈load-file〉Web Component, add external content to the DOM

Multiple SVGs on a page

I have multiple SVGs on my page (they can be dynamically added using a plus or minus image as well). ie:
<img src="/svgs/mysvg.svg" id="svg1">
<img src="/svgs/mysvg.svg" id="svg2">
<img src="/svgs/mysvg.svg" id="svg3">
Inside mysvg.svg, there is a path element with the id #circle. When I am only display 1 svg on the page, I can use the following javascript to change the color of #circle:
$('#circle').css('fill', '#000000');
When I have multiple SVGs on a single page, how can I select which svg I want to change? ie:
var mysvg1 = $('#svg1');
mysvg1.find('#circle').css('fill', '#000000');
Try this :
var mysvg1 = $('#svg1');
mysvg1.find('[id="circle"]').css('fill', '#000000');
Normally you cannot do anything like that, document loaded in <img> is not a part of DOM of the host document.
But you can try to do something like this:
var img = document.getElementById("svg1");
// get the SVG document inside the img tag
var svgDoc = img.contentDocument;
// get that circle item by ID;
var svgItem = svgDoc.getElementById("circle");
// Set the fill attribute
svgItem.setAttribute("fill", "#000000");
I am not sure if img.contentDocument would work on <img>, on <object data="/svgs/mysvg.svg" type="image/svg+xml"> it works.

I want show title in circle tag

I have a circle tag HTML5.
When i add a tag title and hover it, it show title.
But, i add tag title by jquery, it not show title.
This is my code :
<circle class="visible" style="fill: blue" cx="900" cy="350" r="6">
<!--<title>DeviceB|2015/09/01 24:27</title> ( When i add title by jquery and it show title)-->
</circle>
<script type="text/javascript">
$(document).ready(function () {
$('circle').click(function (e) {
$(this).append('<title>DeviceB|2015/09/01 24:27</title>');
});
});
</script>
The <title> tag is meant for usage in <head> to show the title of the webpage in the browser tab in modern browsers and in window frame in older browsers.
Otherwise, the attribute title="" is used in the main document for a tooltip of the description of the hovered element.
Try adding other elements instead, like: <h1>, <span> for a semantic webpage.
There is a misconception in your code. A structural document title (you more likely want that rather than a bouncing or changing title) is set with a <title> tag inside a <head> tag. The user won't expect the document title to change after it's fully loaded.
However, if you really want to change the title at runtime, you may access it with document.title. Also, you should save your device name somewhere easy to control. For example:
HTML
<input type="hidden" id="device_name" value="DeviceB-FooBar-Barbarian-Etc" />
Then :
JavaScript
$(document).ready(function() {
$('circle').click(function(e) {
document.title = $('#device-name').val();
});
});
Hope this helps and good luck!
The title tag is allowed inside svg elements and acts as a tooltip on mouseover (TITLE — the SVG accessible name element
My understanding is the svg title must be created in an svg namespace for it to work properly (and keep it separate from the HTML tag of the same name). For completeness sake, I've included working code for both JQuery and JavaScript.
Also helpful What is jQuery for Document.createElementNS()?
//Assign svg namespace - required
const svgns = "http://www.w3.org/2000/svg";
//Click the blue circle, then mouseover for the <TITLE>/tooltip
//JQuery version
$(document).ready(function() {
$('#c2').click(function() {
//Create the new title element
t = $(document.createElementNS(svgns, 'title'));
//Assign a value to the text between the title tags
$(t).html("TITLE (i.e. svg tooltip) created by JQuery");
//Append it as a child of the svg circle which was clicked
$(this).append(t);
});
});
//Plain JavaScript version
//Click the red circle, then mouseover for the <TITLE>/tooltip
function addTitle(el) {
//Create the new title element
t = document.createElementNS(svgns, 'title');
//Assign a value to the text between the title tags
t.innerHTML = 'TITLE (i.e. svg tooltip) created by JS';
//Append it as a child of the svg circle which was clicked
el.appendChild(t);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<svg>
<circle id="c1" style="fill: red" cx="25" cy="25" r="20" onclick="addTitle(this);"/>
<circle id="c2" style="fill: blue" cx="75" cy="25" r="20" />
</svg>
<br /> Click red circle, then mouseover for title created by JS<br /> Click blue circle, then mouseover for title created by JQuery<br />

Appending code to the contents of an svg in an <object>

I have an <object> that holds an svg file. I want to be able to add animations to it via javascript / jQuery, because the values that I would like to animate are not selectable via CSS transistions (specifically, the r value of <circles>. I would love to be proven wrong, though!). Here's what I'm trying to use:
//create the object to hold the svg
$(selProject).append('<object id="circleCont" data="imgs/circles-01.svg" type="image/svg+xml"></object>')
var a = document.getElementById("circleCont");
a.addEventListener("load",function(){
var svgDoc = a.contentDocument; //get the inner DOM of .svg
var circ = svgDoc.getElementsByClassName('circ');
$(circ[0]).append('<animate attributeName="r" from="0.01" to="300" dur="0.2" begin="0s" fill="freeze"/>');
},false);
However, this doesn't seem to work. What is the better method to handle this?

SVG fragments are mistakenly parsed as HTMLUnknownElement

I have trouble composing an inline-SVG using Javascript.
The problem can be reduced to the following code (live example here):
Somewhere inside the body:
<svg id="drawing" xmlns="http://www.w3.org/2000/svg" version="1.1">
</svg>
Inside onReady:
$("#drawing").append($("<rect style='fill: blue' width='100' height='100' />"));
I expected to see a blue rectangle now. However, Chrome and Firefox don't show anything.
Using Firebug, I found out that Firefox interprets the "rect" as a HTMLUnknownElement (and not as a SVGElement).
If I choose "Edit SVG" on the SVG element (using Firebug) and insert a whitespace somewhere, the SVG seems to be reparsed and the rectangle appears.
How can I tell the parser to parse this fragment correctly?
I'm afraid it's not that easy:
jsfiddle is not the place to test svg, it sends wrong content-type
references to external js-files can't be created the html-way(always keep in mind, svg doesn't have to do anything with html)
jquery uses some dummy-div's for creating the elements when using append(), but svg doesn't know div-elements
also note: binding's to the load-event of a svg-document with jQuery doesn't seem to work
Here an example-code, works for me in FF when delivered as image/svg+xml
<svg id="drawing"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
onload="fx()">
<script type="text/ecmascript" xlink:href="http://code.jquery.com/jquery-latest.js" />
<script type="text/ecmascript">
function fx()
{
$(document.createElementNS('http://www.w3.org/2000/svg', 'rect'))
.css('fill','blue')
.attr({'width':100,'height':100})
.appendTo('#drawing');
}
</script>
</svg>
But like Marcin I would suggest to use a plugin.
To add from the parent document you may use an object containing the properties of the element, basic example:
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
/*<![CDATA[*/
function fx(obj,params)
{
var svgDoc=obj.contentDocument;
if(typeof params.name!='string')return;
var props=$.extend({'attrs':{},'style':{},'selector':null},params);
props.target=(!props.selector)?svgDoc.documentElement:$(svgDoc).find(props.selector)
$(svgDoc.createElementNS('http://www.w3.org/2000/svg', props.name))
.css(props.style)
.attr(props.attrs)
.appendTo(props.target);
}
/*]]>*/
</script>
</head>
<body>
<object onload="fx(this,{'name':'rect','attrs':{'width':100,'height':100},'style':{'fill':'blue'},'selector':'#drawing'})"
data="my.svg"
type="image/svg+xml"
width="200"
height="200">
<param name="src" value="my.svg">
</object>
</body>
</html>
The structure of the object:
name:tagName(string)
attrs:attributes(object)
style:style(object)
selector:selector(string, if omitted the root-element will be selected)
This example shows how to embed SVG in XHTML, including the programmatic creation of new SVG elements: http://phrogz.net/svg/svg_in_xhtml5.xhtml
This example shows how to use XHR to fetch SVG as XML, find a fragment of it, and two ways convert it into the local document before appending the node to the existing SVG document: http://phrogz.net/svg/fetch_fragment.svg
In general:
Don't use jQuery with SVG directly.
Dynamically-created SVG elements must be created using createElementNS, supplying the SVG namespace URI 'http://www.w3.org/2000/svg'. (Note, however, that the SVG attributes should not be created with a namespace.)
You need to be sure to serve your XHTML as XML (content-type: application/xhtml+xml) and not as text/html.
Here's a general-purpose function I use on occasion for creating SVG elements conveniently. It works both within SVG documents as well as SVG-in-XHTML, allows for text content to be created directly, and supports namespaced attributes (such as xlink:href).
// Example usage:
// var parentNode = findElementInTheSVGDocument();
// var r = createOn( parentNode, 'rect', {
// x:12, width:10, height:10, 'fill-opacity':0.3
// });
function createOn(root,name,attrs,text){
var doc = root.ownerDocument;
var svg = root;
while (svg.tagName!='svg') svg=svg.parentNode;
var svgNS = svg.getAttribute('xmlns');
var el = doc.createElementNS(svgNS,name);
for (var attr in attrs){
if (attrs.hasOwnProperty(attr)){
var parts = attr.split(':');
if (parts[1]) el.setAttributeNS(svg.getAttribute('xmlns:'+parts[0]),parts[1],attrs[attr]);
else el.setAttributeNS(null,attr,attrs[attr]);
}
}
if (text) el.appendChild(document.createTextNode(text));
return root.appendChild(el);
}
I assume you'd like to get a fragment of SVG parsed, without having to turn it into JSON and whatnot. Here's some CoffeeScript that does it. I tested the code on SVG embedded in HTML, but I think it should work in any circumstance.
https://github.com/pwnall/ddr/blob/master/javascripts/graphics/pwnvg.coffee#L169
I wrap the SVG fragment in an tag to build a stand-alone SVG document, then I use a DOMParser to parse the SVG document, and I pull out the children of the root element (the wrapper) one by one, and stick them into the original SVG's DOM.
In theory, there is an easier (and faster approach), but it doesn't work right now. http://crbug.com/107982
See: Convert svg into base64
I always make sure a svg fragment is inside a svg element or just force refresh the svg.

Categories