I'm currently working with SVG. I need to know the string length in pixels in order to do some alignment. How can I do to get the length of a string in pixel ?
Update: Thanks to nrabinowitz. Based on his help, I can now get the length of dynamic-added text. Here is an example:
<svg id="main"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
width="1020"
height="620"
viewBox="0 0 1020 620"
onload="startup(evt)">
<script>
<![CDATA[
var startup = function (evt) {
var width;
var svgNS = "http://www.w3.org/2000/svg";
var txtNode = document.createTextNode("Hello");
text = document.createElementNS(svgNS,"text");
text.setAttributeNS(null,"x",100);
text.setAttributeNS(null,"y",100);
text.setAttributeNS(null,"fill","black");
text.appendChild(txtNode);
width = text.getComputedTextLength();
alert(" Width before appendChild: "+ width);
document.getElementById("main").appendChild(text);
width = text.getComputedTextLength();
alert(" Width after appendChild: "+ width)
document.getElementById("main").removeChild(text);
}
//]]>
</script>
</svg>
I've been wondering this too, and I was pleasantly surprised to find that, according to the SVG spec, there is a specific function to return this info: getComputedTextLength()
// access the text element you want to measure
var el = document.getElementsByTagName('text')[3];
el.getComputedTextLength(); // returns a pixel integer
Working fiddle (only tested in Chrome): http://jsfiddle.net/jyams/
Having read various similar threads with interest and benefitted from some of the ideas, I've created a page which compares three of the Javascript methods side-by-side. I've noted results in
IE9
Firefox 29.0.1 and
Chrome 34.0.1847.131 m
You can load it in your browser and see what works for you:
http://bl.ocks.org/MSCAU/58bba77cdcae42fc2f44.
Related
I have a text tag that I'm adding to SVG via JS script and would like to make that text editable.
Found a couple of solutions on StackOverflow:
creating a css class and applying it to my SVG text element. This works perfectly, at least in Safari, but isn't recommended it by MDN https://developer.mozilla.org/en-US/docs/Web/CSS/user-modify
Here is the class that is actually someone's answer, unfortunately I can't remember whose:
.editable {
font-size: 0.3em;
user-modify: read-write;
-moz-user-modify: read-write;
-webkit-user-modify: read-write;
}
wrapping the svg in a contenteditable div (answer given by Erik). This also works but results in bad cursor behavior, at least in Safari.
<div contenteditable="true">
<svg id="svgArea" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
</svg>
</div>
The JS part
function foo(x, y, w, h) {
var rect, group;
var svgPlace = document.getElementById('svgArea');
var xmlns = "http://www.w3.org/2000/svg";
group = document.createElementNS(xmlns, 'g');
svgPlace.appendChild(group);
var txtElem = document.createElementNS(xmlns, 'text');
txtElem.setAttributeNS(null, 'x', x);
txtElem.setAttributeNS(null, 'y', y * 1.25);
txtElem.setAttributeNS(null, 'fill', 'LightBlue');
txtElem.setAttributeNS(null, 'contentEditable', true); //does nothing!
txtElem.setAttributeNS(null, 'class', 'editable'); //this is the best working option at the moment
var txtVal = document.createTextNode('test');
txtElem.appendChild(txtVal);
group.appendChild(txtElem);
}
I'd love to be able to set the contentEditable as an attribute, if that's possible. But most importantly, what's the best way to make SVG text editable?
#Raymond comment is the correct answer but since you still have problem I wrote down an example.
document.getElementById("svgWrapper").contentEditable = "true";
<div id="svgWrapper">
<svg id="svgArea" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
</svg>
</div>
I ended up solving this by placing an input tag inside a foreign object, which is added to an SVG group. That works on Safari so I wanted to post it here, in case anyone else is struggling with the same thing.
<svg>
<g>
<foreignobject>
<input></input>
</foreignobject>
</g>
</svg>
If you need to add using JS:
function addSvg(x, y, w, h) {
var rect, group;
var svgPlace = document.getElementById('svgArea');
var xmlns = "http://www.w3.org/2000/svg";
group = document.createElementNS(xmlns, 'g');
svgPlace.appendChild(group);
// solution
let foreigner = document.createElementNS(xmlns, "foreignObject");
foreigner.setAttributeNS(null, "x", x);
foreigner.setAttributeNS(null, "y", y);
foreigner.setAttributeNS(null, "width", w);
foreigner.setAttributeNS(null, "height", h);
group.appendChild(foreigner);
let txt = document.createElement('input');
foreigner.appendChild(txt);
}
I have a bar chart and I want to be able to assign individual fill colors to bars depending on a condition. I don't think it matters, but I am using angular-nvd3 linePlusBarChart to draw bars.
UPDATE
There seemingly is an issue with my local environment, possibly with NVD3 or angular libraries, I am not sure what's going on yet. I put together jsfiddle and everything worked perfectly. See the link below, the bars can have different colors.
On my local computer though, when the execution reaches the point var y = document.querySelector(...., I am getting angular error in console ->>>>> y is null.
http://jsfiddle.net/p0g9Lqu8/
Here is the HTML.
<nvd3>
<g class="nv-bars">
<rect x="0" y="419" height="1" fill="LimeGreen" class="nv-bar positive nv-bar-0-0" width="267"></rect>
<rect x="0" y="252" height="168" fill="LimeGreen" class="nv-bar positive nv-bar-0-1" width="267"></rect>
<rect x="0" y="294" height="126" fill="LimeGreen" class="nv-bar positive nv-bar-0-2 hover" width="267"></rect>
<rect x="0" y="252" height="168" fill="LimeGreen" class="nv-bar positive nv-bar-0-3" width="267"></rect>
</g>
</nvd3>
As you can see, each rect tag has a dynamic CSS class nv-bar-0-1, nv-bar-0-2.. that I would like to be able to point to from JS. NOTE: from HTML, if I use CSS, all works, but from JS loop, does not. Any help is appreciated.
This perfectly works.
<style>
nvd3 .nv-bars rect {
fill:white;
}
nvd3 .nv-bars rect.nv-bar-0-1{
fill:yellow;
}
</style>
But this, does not:
for(var i = 0; i < 4; i++) {
var y = document.querySelector('nvd3 .nv-bars rect.nv-bar-0-' + i);
y.style.fill = "#ffff00";
}
OR this one, same result
var y = document.querySelector('nvd3 .nv-bars rect.nv-bar-0-1); // y is always null
y.style.fill = "#ffff00"; // triggers null reference error here
I call that JS fragment inside Angular controller in this block:
angular.element(document).ready(function () {
var y = document.querySelector('nvd3 .nv-bars rect.nv-bar-0-1');
console.log('>>>>>>>>>>>===' + y); // this outputs [object HTMLUnknownElement]
y.style.fill = "#ffff00";
});
I thank everybody for time and input. I finally found where the problem was and what the solution would be, so I would like to share it with the community in case anybody comes across a similar problem.
My final output into the browser is a result of combining multiple HTML/JS/CSS fragments. The issue was in timing: the JS code was attempting to refer to a tag, but it appeared the dynamic construction of that tag with its HTML/CSS was not complete at the time of JS reference.
angular.element(document).ready(.... could not do the job, but a pure JS approach from the link below - did.
https://github.com/jfriend00/docReady
I enclosed the JS block that was to check some conditions and assign colors to bars, into the docReady block. See the simplified code sample below.
docReady(function () {
for (var i = 0; i < 4; i++) {
var k = document.querySelector('nvd3 .nv-bars rect.nv-bar-0-' + i);
// some condition goes here.....
k.style.fill = "green";
}
var y = document.querySelector('nvd3 .nv-bars rect.nv-bar-0-1');
// this too changes color
y.style.fill = "#ff0000";
});
Worked in all major browsers: Firefox, Chrome, Safari and IE
I'm trying to draw an isoceles triangle with it's top vertex in the middle of the screen.
I want to use JavaScript as screen sizes of different users can be different and hence, center of the screen must first be found out.
Here is my code:
function drawRect(){
var w = $(document).width();
var h = $(window).height();
var halfh = h/2;
var halfw = w/2;
var svg = document.createElement("svg");
var poly = document.createElement("polygon");
svg.setAttribute("style", "position: fixed;");
svg.setAttribute("height", ""+h);
svg.setAttribute("width", ""+w);
poly.setAttribute("points", ""+halfw+","+halfh+" 0,"+h+" "+w+","+h);
poly.setAttribute("style", "fill:lime;stroke:purple;stroke-width:1");
svg.appendChild(poly);
var svgplace = document.getElementById("carpet");
svgplace.appendChild(svg);
}
No triangle appears on the screen. But, if I open the 'Inspect Element' console on chrome, and I modify the created html element slightly, it appears! (Any kind of small mod. Like adding a space in the middle somewhere)
Please help!
Thanks in advance
You need to be using
document.createElementNS("http://www.w3.org/2000/svg", "polygon");
SVG is in its own namespace aside from HTML. Therefore, you need to create elements that are in the SVG namespace using createElementNS.
Consider the following example that works in Chrome and Firefox
var newItem = document.createElementNS("http://www.w3.org/2000/svg", "circle");
newItem.setAttribute("cx", ((16 * 1) + 8));
newItem.setAttribute("cy", "50%");
newItem.setAttribute("r", 4);
newItem.setAttribute("fill", "#333333");
document.getElementById("target").appendChild(newItem);
<svg id="target">
</svg>
Im creating an SVG pattern in HTML and the user will be able to change colour and size etc. But its not working.
I get an error regarding the body onload function. and then when appending the SVG Diagram to the svg placeholder that i have.
Here is the Script:
<script>
var SVG = document.getElementById("svgArea");
document.content.appendChild(SVG);
function drawCircle()
{
var svgLink = "http://www.w3.org/2000/svg";
var Centre = document.createElementNS(svgLink,"circle");
Centre.setAttributeNodeNS(null,"id","Centre");
Centre.setAttributeNodeNS(null,"cx",230);
Centre.setAttributeNodeNS(null,"cy",0);
Centre.setAttributeNodeNS(null,"r",75);
Centre.setAttributeNodeNS(null,"fill","centreColour");
document.getElementById("svgArea").appendChild(Centre);
var group = document.getElementById("svgArea");
group.setAttribute("transform","translate(230,0)");
}
</script>
Then for the body tag i have the following:
<body onload="drawCircle()">
And for the contents of the page i have the following code:
<div class="content">
<!-- SVG DIAGRAM -->
<svg id="SVG" style="background:pink" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="690" height"400">
<g id="svgArea">
</g>
</svg>
</div>
Errors:
[Error] TypeError: 'undefined' is not an object (evaluating 'document.content.appendChild')
global code (SVG.html, line 33)
[Error] TypeMismatchError: DOM Exception 17: The type of an object was incompatible with the expected type of the parameter associated to the object.
drawCircle (SVG.html, line 57)
onload (SVG.html, line 107)
Where and what am i doing wrong?
Thanks
This doesnt Work:
//Drawing Petals
for (var i = 0; i < numberOfPetals; i++)
{
var svgLink = "http://www.w3.org/2000/svg";
var flowerPettle = document.createElementNS(svgLink,"ellipse");
flowerPettle.setAttributeNS(null,"id","flowerPettle");
flowerPettle.setAttributeNS(null,"ry", 230);
flowerPettle.setAttributeNS(null,"rx",0)
flowerPettle.setAttributeNS(null,"fill",petalColour);
var rotate = "rotate(" + (i*(360 / numberOfPetals)) + " " + 300 + "," + 30 + ")";
flowerPettle.setAttribute("transform",rotate);
document.getElementById("FlowerArea").appendChild(flowerPettle);
}
There are a few things wrong with your code. One is the createAttributeNode() calls. Another is the document.content reference. Plus there are things like the strange colour value "centreColour".
I've put together a working fiddle. Hopefully it helps you get things working in your code.
Demo here
Svg looks like:
<svg width=xxx height=xxx>
<g width=xxx height=xxx>
</g>
</svg>
My original resize script:
var desiredWidth1=$('svg').attr("width");
var scaleVal1=$(window).width()/desiredWidth1;
var desiredWidth2=$('svg').attr("height");
var scaleVal2=$(window).height()/desiredWidth2;
var originalTrans = $('svg').attr('transform');
if(scaleVal1>scaleVal2){
$('svg').css("-webkit-transform","scale("+scaleVal2+")");
$('svg').attr("transform", 'translate('+80*scaleVal2+',0)');
}
else{
$('svg').css("-webkit-transform","scale("+scaleVal1+")");
$('svg').attr("transform", 'translate('+80*scaleVal1+',0)');
}
It only resizes the svg once the page loaded, and it is not dynamically resizing.
Therefore my new jquery on window resize here:
$(window).on("resize","g",function(){
var desiredWidth1=$("svg").attr("width");
var scaleVal1=$(window).width()/desiredWidth1;
var desiredWidth2=$("svg").attr("height");
var scaleVal2=$(window).height()/desiredWidth2;
if(scaleVal1>scaleVal2){
$("g").css("-webkit-transform","scale("+scaleVal2+")");
$("g").attr("transform", 'translate('+80*scaleVal2+',0)');
}
else{
$("g").css("-webkit-transform","scale("+scaleVal1+")");
$("g").attr("transform", 'translate('+80*scaleVal1+',0)');
}
});
This is my resize jquery. I want to resize the element 'g' based on client window size.
However this jquery is not working properly. There is no warning or error in console, and it seems to be some problems in DOM and cannot change the element g.
Any information on my code or better scenarios would be helpful. Thanks.
If you set the viewBox, width and height attributes of the <svg> to the right values, the browser will scale everything for you.
var svg = $("#mysvg").get(0);
var w = svg.width.baseVal.value;
var h = svg.height.baseVal.value;
svg.setAttribute('viewBox', '0 0 '+w+' '+h);
svg.setAttribute('width', '100%');
svg.setAttribute('height', '100%');
Demo here
If you need the width and height to be something specific (rather than "100%"), just modify those last two lines. No need to go in and modify the <g> element.
PS. Note that you can't trust jQuery to modify the attributes of the SVG correctly. It is designed for HTML, not SVG and doesn't always do the right thing. It is usually better to use vanilla Javascript as I have done here.
window.onload = function(){//First open resize
var desiredWidth1=$('svg').attr("width");
var scaleVal1=$(window).width()/desiredWidth1;
var desiredWidth2=$('svg').attr("height");
var scaleVal2=$(window).height()/desiredWidth2;
var originalTrans = $('svg').attr('transform');
$('svg').css("transform-origin","left");
if(scaleVal1>scaleVal2){
$('svg').css("-webkit-transform","scale("+scaleVal2+")");
$('svg').css("-ms-transform","scale("+scaleVal1+")");
$('svg').attr("transform", 'translate('+80*scaleVal2+',0)');
}
else{
$('svg').css("-webkit-transform","scale("+scaleVal1+")");
$('svg').css("-ms-transform","scale("+scaleVal1+")");
$('svg').attr("transform", 'translate('+80*scaleVal1+',0)');
}
}
window.onresize = function() {//Dynamically resize the svg to fit the window
var desiredWidth1=$("svg").attr("width");
var scaleVal1=$(window).width()/desiredWidth1;
var desiredWidth2=$("svg").attr("height");
var scaleVal2=$(window).height()/desiredWidth2;
if(scaleVal1>scaleVal2){
$("svg").css("-webkit-transform","scale("+scaleVal2+")")
$("svg").css("-ms-transform","scale("+scaleVal2+")")
$("svg").attr("transform", 'translate('+80*scaleVal2+',0)');
}
else{
$("svg").css("-webkit-transform","scale("+scaleVal1+")");
$("svg").css("-ms-transform","scale("+scaleVal1+")");
$("svg").attr("transform", 'translate('+80*scaleVal1+',0)');
}
}
Finally work this out based on my original codes. As BigBadaboom mentioned and thank you very much, jQuery is usually not working for elements inside the SVG, such as g, path, node etc. However, it just works fine for the whole SVG element.