SVG Input Field - javascript

I'm trying to make one of my SVG text fields accept input from the user when they press on it. Is there any way of doing that?
const wrapper = document.getElementById('wrapper');
const text = document.getElementById('Username');
const inputWrapper = document.getElementById('input-wrapper');
const input = document.getElementById('input');
const button = document.getElementById('button');
text.addEventListener('click', () => {
text.classList.toggle('hide');
inputWrapper.classList.toggle('hide');
input.focus();
});
button.addEventListener('click', () => {
text.classList.toggle('hide');
inputWrapper.classList.toggle('hide');
});
input.addEventListener('change', (e) => {
text.innerText = e.target.value;
});
.cls-1 {
clip-path: url(#clip-Login_page);
}
.cls-2 {
opacity: 0.67;
fill: url(#pattern);
}
.cls-3 {
fill: #d9d9d9;
}
.cls-3, .cls-5 {
stroke: #0d0d0d;
}
.cls-4 {
fill: url(#pattern-2);
}
.cls-5 {
fill: #f2f2f2;
}
.cls-6, .cls-7 {
fill: #707070;
font-family: Georgia;
}
.cls-6 {
font-size: 25px;
}
.cls-7 {
font-size: 20px;
}
.cls-8 {
stroke: none;
}
.cls-9 {
fill: none;
}
.cls-10 {
fill: #fff;
}
.cls-11 {
filter: url(#Rectangle_5);
}
.cls-12 {
filter: url(#Amcan_logo);
}
.cls-13 {
filter: url(#Rectangle_2);
}
.hide {
display: none;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 375 667">
<defs>
<pattern id="pattern" preserveAspectRatio="none" width="100%" height="100%" viewBox="0 0 1920 1080">
<image width="1920" height="1080" xlink:href="img/Amcan.gif"/>
</pattern>
<filter id="Rectangle_2" x="11" y="178" width="354" height="63" filterUnits="userSpaceOnUse">
<feOffset dy="5" input="SourceAlpha"/>
<feGaussianBlur stdDeviation="3" result="blur"/>
<feFlood flood-opacity="0.161"/>
<feComposite operator="in" in2="blur"/>
<feComposite in="SourceGraphic"/>
</filter>
<pattern id="pattern-2" preserveAspectRatio="none" width="100%" height="100%" viewBox="0 0 593 186">
<image width="593" height="186" xlink:href="img/Amcan_logo.png"/>
</pattern>
<filter id="Amcan_logo" x="13" y="28" width="349" height="123" filterUnits="userSpaceOnUse">
<feOffset dy="10" input="SourceAlpha"/>
<feGaussianBlur stdDeviation="3" result="blur-2"/>
<feFlood flood-opacity="0.161"/>
<feComposite operator="in" in2="blur-2"/>
<feComposite in="SourceGraphic"/>
</filter>
<filter id="Rectangle_5" x="77" y="369" width="222" height="63" filterUnits="userSpaceOnUse">
<feOffset dy="5" input="SourceAlpha"/>
<feGaussianBlur stdDeviation="3" result="blur-3"/>
<feFlood flood-opacity="0.161"/>
<feComposite operator="in" in2="blur-3"/>
<feComposite in="SourceGraphic"/>
</filter>
<clipPath id="clip-Login_page">
<rect width="375" height="667"/>
</clipPath>
</defs>
<g id="Login_page" data-name="Login page" class="cls-1">
<rect class="cls-10" width="375" height="667"/>
<rect id="Amcanerino" class="cls-2" width="510" height="667" transform="translate(-71)"/>
<g class="cls-13" transform="matrix(1, 0, 0, 1, 0, 0)">
<g id="Rectangle_2-2" data-name="Rectangle 2" class="cls-3" transform="translate(20 182)">
<rect class="cls-8" width="336" height="45"/>
<rect class="cls-9" x="0.5" y="0.5" width="335" height="44"/>
</g>
</g>
<g class="cls-12" transform="matrix(1, 0, 0, 1, 0, 0)">
<rect id="Amcan_logo-2" data-name="Amcan_logo" class="cls-4" width="331" height="104" transform="translate(22 28)"/>
</g>
<g id="Rectangle_3" data-name="Rectangle 3" class="cls-5" transform="translate(20 250)">
<rect class="cls-8" width="336" height="45"/>
<rect class="cls-9" x="0.5" y="0.5" width="335" height="44"/>
</g>
<g id="Rectangle_4" data-name="Rectangle 4" class="cls-5" transform="translate(20 311)">
<rect class="cls-8" width="336" height="45"/>
<rect class="cls-9" x="0.5" y="0.5" width="335" height="44"/>
</g>
<g class="cls-11" transform="matrix(1, 0, 0, 1, 0, 0)">
<g id="Rectangle_5-2" data-name="Rectangle 5" class="cls-3" transform="translate(86 373)">
<rect class="cls-8" width="204" height="45"/>
<rect class="cls-9" x="0.5" y="0.5" width="203" height="44"/>
</g>
</g>
<text id="SUBMIT" class="cls-6" transform="translate(139 405)"><tspan x="0" y="0">SUBMIT</tspan></text>
<text id="Login" class="cls-6" transform="translate(156 214)"><tspan x="0" y="0">Login</tspan></text>
<div id="wrapper">
<text id="Username" class="cls-7" transform="translate(142 280)"><tspan x="0" y="0">Username</tspan></text>
<div id="input-wrapper" class="hide">
<input id="input" type="text" value="Username"/>
<button id="button">Submit</button>
</div>
</div>
<text id="Password" class="cls-7" transform="translate(145 341)"><tspan x="0" y="0">Password</tspan></text>
</g>
</svg>
This is the amended code as suggested by Solo.

<tspan> and <text> are not html elements, but svg elements with their own attributes. The property contenteditable doesn't exist on tspan (you can console.log document.querySelector("#myTspan").isContentEditable, it will return undefined.
However, it can inherit properties from its parents. So in case of an inline svg in a HTML file, wrapping your svg in an editable element will work in some browsers, but all you texts are then editable.
<div contenteditable="true">
<svg>
<text id="Username" class="cls-7" transform="translate(142 20)"><tspan x="0" y="0">Username</tspan></text>
</svg>
</div>
In a standalone SVG, you have no way to have contenteditable working since it's part of HTML specification, not that of SVG.
You have to go for a script as other answers suggest, but read carefully (since you didn't mentionned at first that it was a standalone svg) : Including JavaScript in SVG

Yes and no, you can't edit the text directly, but you can put an input overtop of it. See the code below:
.input-real {
background: rgba(255, 255, 255, 0);
color: transparent;
padding: 0;
border: 0 none transparent;
line-height: 0;
}
.input-real:focus {
background: rgba(255, 255, 255, 1);
color: #333;
}
.input-real,
.input-mimic {
font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
font-size: 14px;
}
<svg viewBox="0 0 375 667">
<text id="Username" class="input-mimic" x="50" y="50" alignment-baseline="hanging">Username</text>
<foreignObject width="50" height="23" x="50" y="46">
<input id="input" class="input-real" type="text" value="Username"/>
</foreignObject>
</svg>

use contenteditable on tspan
see more here - https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content
How does it work?
All you have to do is set the contenteditable attribute on nearly any HTML element to make it editable.
function toggleEdit() {
var text = document.getElementById('Username');
var tspan = text.firstElementChild;
tspan.setAttribute('contenteditable', true);
}
<text id="Username" class="cls-7" transform="translate(142 280)"><tspan x="0" y="0">Username</tspan></text>
<div><button onclick="toggleEdit();">Edit input</button></div>

Thank you very much for your help guys! I have found out that I can just use JavaScript 'prompt' form to achieve this. All I had to do was to implement the following:
<script>
function changeUsername()
{
document.getElementById('Username').textContent = prompt("Please enter your Username");
}
</script>
<rect onclick="changeUsername()" class="cls-8" width="336" height="45"/>
I completely forgot that the prompt form existed! Again, thank you all :)

No need of jquery or JS,
Just add contenteditable="true" to that text element and you're done...
<text contenteditable="true" id="Username" class="cls-7" transform="translate(142 280)"><tspan x="0" y="0">Username</tspan></text>

Related

text is going out of <svg><rect> box once in 1000x times but on safari browser always reproduce this bug

Im trying to fix this bug, and I have tried alot things but nothing help, if I give value of x='23%' like <text id="badgeText" x = "23% ... I can fix bug on safari but than bug start repodruce everytime on other browsers(mozila,chrome etc...), anyhow the bug is reproducing once in many times on other browsers ...
bug image
<div class="CoveoShowIfFieldValue" data-field="#isRecommended" data-field-value="True">
<svg height="25px" style="overflow:visible!important;">
<g id="recommendedBadge" style="object-fit:contain!important;">
<rect x="0" y="0" rx="15px" ry="15px"/>
<text id="badgeText" y="66%" font-family="Metric" text-anchor="middle" class="CoveoText" data-value="RECOMMENDED" style="text-transform: uppercase;"></text>
<g class="tooltip" transform="translate(21,62)">
<rect x="-6%" width="450" y="-35" height="25" rx="5px" ry="5px"/>
<text y="-31" dy="1em" font-family="Metric" text-anchor="middle" font-weight="100" class="CoveoText" data-value="RecommendedTooltip"></text>
</g>
</g>
</svg> </div>
It’s not a bug:
You need to add some width related properties like width or a viewBox.
Otherwise a browser can’t tell how to render a relative unit within a svg element.
Some browsers might be more forgiving by guessing or setting default width values as fallbacks. (Currently most firefox and chromium based browsers use 300px as a fallback).
<style>
svg {
border: 1px solid red;
height: 25px;
}
text {
fill: #fff;
}
</style>
<h2>Use viewBox</h2>
<svg viewBox="0 0 450 25" style="overflow:visible!important;">
<g id="recommendedBadge">
<rect x="0" y="0" rx="15" ry="15" width="100%" height="25" />
<text id="badgeText" x="50%" y="50%" dy="2" dominant-baseline="middle" font-family="Metric" text-anchor="middle" class="CoveoText" data-value="RECOMMENDED" style="text-transform: uppercase;">
Badge Text
</text>
<g class="tooltip" transform="translate(0 25)">
<rect x="0" y="0" width="100%" height="100%" rx="5" ry="5" fill="red" />
<text x="50%" y="50%" dy="5" font-family="Metric" text-anchor="middle" font-weight="100" class="CoveoText" data-value="RecommendedTooltip">tooltip</text>
</g>
</g>
</svg>
<br><br><br>
<h2>Use width</h2>
<svg height="25" width="100%" style="overflow:visible!important;">
<g id="recommendedBadge">
<rect x="0" y="0" rx="15" ry="15" width="100%" height="25" />
<text id="badgeText" x="50%" y="50%" dy="2" dominant-baseline="middle" font-family="Metric" text-anchor="middle" class="CoveoText" data-value="RECOMMENDED" style="text-transform: uppercase;">
Badge Text
</text>
<g class="tooltip" transform="translate(0 25)">
<rect x="0" y="0" width="100%" height="100%" rx="5" ry="5" fill="red" />
<text x="50%" y="50%" dy="5" font-family="Metric" text-anchor="middle" font-weight="100" class="CoveoText" data-value="RecommendedTooltip">tooltip</text>
</g>
</g>
</svg>

How to insert multiline text in a svg element

As a primer in svg and javascript I've been trying for a while to make this code work with no avail.
My problem is that I'm not able to take the text from a text element (class="texts") and put it into another one (MySpeechBoxText1) maintaining it's multiline formatting.
Here is my code:
<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"
version="1.1"
width="594mm" height="420mm" id="svg58064"
viewBox="0 0 2245.0393 1587.4016">
<style>
.bar {
fill: #a9a9a9;
opacity: 0.6;
}
</style>
<g class="miogruppo">
<rect class="bar" x="50" y="60" width="80" height="120"/>
<text class="texts" x="0" y="50" font-family="Verdana" font-size="35" fill="blue" display='none'>
<tspan x="20" dy="1.2em">test 1</tspan>
<tspan x="20" dy="1.2em">test 1</tspan>
</text>
</g>
<g class="miogruppo">
<rect class="bar" x="180" y="80" width="80" height="170"/>
<text class="texts" x="0" y="50" font-family="Verdana" font-size="35" fill="blue" display='none'>
<tspan x="20" dy="1.2em">test 2</tspan>
<tspan x="20" dy="1.2em">test 2</tspan>
</text>
</g>
<g id="group1" display='none'>
<title>Tester 2</title>
<path id="test1"
d="M15,0 H150 V150 H15 L15,90 L0,90 L15,75 Z15 "
style="stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;
stroke-dasharray:none;stroke-miterlimit:4;stroke-opacity:1;fill-opacity:0.5;fill:#ffffff"
inkscape:connector-curvature="0"/>
<text id="MySpeechBoxText1" x="60" y="60" > </text>
</g>
<script><![CDATA[
var bars = document.getElementsByClassName('bar');
var texts = document.getElementsByClassName('texts');
var mySpeechBox = document.getElementById("group1");
var MySpeechBoxText1 = document.getElementById("MySpeechBoxText1");
for (var i = 0; i < bars.length; i++) {
bars[i].addEventListener('mouseover', mouseOverEffect);
bars[i].addEventListener('mouseout', mouseOutEffect);
bars[i].addEventListener('mousemove', mousemoveEffect(i));
}
for (var i = 0; i < texts.length; i++) {
texts[i].addEventListener('mouseover', mouseOverEffect);
texts[i].addEventListener('mouseout', mouseOutEffect);
texts[i].addEventListener('mousemove', mousemoveEffect(i));
}
function mouseOverEffect() {
mySpeechBox.style.display='block';
}
function mouseOutEffect() {
mySpeechBox.style.display='none';
}
function mousemoveEffect(a) {
return function() {
var myX = +bars[a].getAttribute("x");
var myY = +bars[a].getAttribute("y");
var myWidth = +bars[a].getAttribute("width");
var myHeight = +bars[a].getAttribute("height");
var MySumX =myX + myWidth/2;
var MySumY =myY + myHeight/2 - 90;
mySpeechBox.setAttribute("transform", 'translate(' + MySumX + ',' + MySumY + ')');
//MySpeechBoxText1.style.whiteSpace = "pre";
MySpeechBoxText1.textContent = texts[a].textContent; //here the text should be multiline
}
}
]]></script>
</svg>
Instead of textContent, use innerHTML.
const text1 = document.querySelector('.text1');
const text2 = document.querySelector('.text2');
text2.innerHTML = text1.innerHTML;
<svg viewBox="0 0 240 80" xmlns="http://www.w3.org/2000/svg">
<style>
.small { font: italic 13px sans-serif; }
.heavy { font: bold 30px sans-serif; }
/* Note that the color of the text is set with the *
* fill property, the color property is for HTML only */
.Rrrrr { font: italic 40px serif; fill: red; }
</style>
<text class="text1" x="0" y="5" font-family="Verdana" font-size="10" fill="blue" >
<tspan x="20" dy="1.2em">test 1</tspan>
<tspan x="20" dy="1.2em">test 1</tspan>
</text>
<text x="25" y="25" class="text2"></text>
</svg>

How can i toggle html element visibility with js

I have a map (made of svg elements) which looks like the following at startup.
When a user clicks a 'zone' i would like for two things to happen. Only one zone can be clicked at at time.
The zone receives an 'active' class tag so the style changes to represent a selected state.
The markers for that zone become visible.
Then a user can click a 'marker' in that zone which gets the following 'active' tag applied so it appears selected to the user as well. Only one marker can be selected at a time as well.
<!DOCTYPE doctype html>
<html lang="en">
<style type="text/css">
* {
background: rgb(40,40,40);
}
.zone {
fill: rgba(255,255,255,0.25);
stroke: rgba(255,255,255,0.25);
stroke-width: 1;
cursor: hand;
}
.marker {
fill: rgba(255,0,0,1.0);
stroke: rgb(255,255,255);
stroke-width: 0;
cursor: crosshair;
}
.active_zone {
stroke: rgba(255,255,255,1.0);
fill: rgba(255,0,0,0.25);
}
.active_marker {
stroke: rgb(255,255,255);
stroke-width: 1;
}
</style>
<body>
<div class="wrapper">
<svg width="500px" viewBox="0 0 374 180" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<!-- Zones -->
<g transform="matrix(1,0,0,1,-47,-21)">
<path class="zone" d="M90,37L47,106L91,176L159,179L176,125L220,91L180,44L90,37Z"/>
</g>
<g transform="matrix(1,0,0,1,-47,-21)">
<path class="zone" d="M250,21L318.436,37L333,116L285,201L159,179L176,125L220,91L180,44L250,21Z"/>
</g>
<g transform="matrix(1,0,0,1,-47,-21)">
<path class="zone" d="M416,56L318.436,37L333,116L285,201L421,183L409,120L416,56Z"/>
</g>
<!-- Markers -->
<g transform="matrix(1,0,0,1,-47,-21)">
<rect class="marker active_marker" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,106,-29.5)">
<rect class="marker" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,30,-2.5)">
<rect class="marker" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,132,60.5)">
<rect class="marker" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,195,84)">
<rect class="marker" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,204,-11.5)">
<rect class="marker" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,230,33)">
<rect class="marker" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,-21,15.5)">
<rect class="marker" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,79,69.5)">
<rect class="marker" x="94" y="56" width="18" height="18"/>
</g>
</svg>
</body>
</html>
Creating the answer to this was fun, I've never worked with SVG's.
jQuery cannot apply .addClass() to path elements, so in my original answer nothing was working - I was clicking but there were no style changes. The solution to this was using .attr(), which is why you see that in the answer. I added a class, .marker-visible so that I can 1) distinguish which markers are shown and 2) actually show the markers. Every zone and marker has a data-zone attribute that tells the javascript which zone is being clicked and which markers are part of that zone.
I created a click handler for the zones in document.ready(), and all that click handler does is reset the class on all zones (so they appear unclicked) and adds the zone-active class to the clicked zone. Then it shows all the markers in the zone by finding all markers that have the same data-zone tag.
I used $(document).on('click', '.marker-visible') rather than $('.marker-visible').click() because the markers get the marker-visible class assigned on the fly, so I can't assign the click handler on the fly as well (I could but that wouldn't be the best). Instead, I assigned it to the document so it always runs and I don't need to assign and drop click handlers during runtime. The click handler does practically the same thing as the zone click handler, in that it just resets the classes of all the other visible markers and gives the clicked marker the marker-active class.
Comment if you need any further clarification.
$(document).ready(function(){
$('.zone').click(function(){
$('.zone').attr('class', 'zone');
$('.marker').attr('class', 'marker');
$(this).attr('class', 'zone zone-active');
$('.marker[data-zone="' + $(this).data('zone') + '"]').attr('class', 'marker marker-visible');
});
$(document).on('click', '.marker-visible', function(){
$('.marker-visible').attr('class', 'marker marker-visible');
$(this).attr('class', 'marker marker-visible marker-active');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE doctype html>
<html lang="en">
<style type="text/css">
* {
background: rgb(40,40,40);
}
.zone {
fill: rgba(255,255,255,0.25);
stroke: rgba(255,255,255,0.25);
stroke-width: 1;
cursor: pointer;
}
.marker {
fill: rgba(255,0,0,1.0);
stroke: rgb(255,255,255);
stroke-width: 0;
cursor: crosshair;
display: none;
}
.zone-active {
stroke: rgba(255,255,255,1.0);
fill: rgba(255,0,0,0.25);
}
.marker-visible{
display: block;
}
.marker-active {
stroke: rgb(255,255,255);
stroke-width: 1;
}
</style>
<body>
<div class="wrapper">
<svg width="500px" viewBox="0 0 374 180" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<!-- Zones -->
<g transform="matrix(1,0,0,1,-47,-21)">
<path class="zone" data-zone="1" d="M90,37L47,106L91,176L159,179L176,125L220,91L180,44L90,37Z"/>
</g>
<g transform="matrix(1,0,0,1,-47,-21)">
<path class="zone" data-zone="2" d="M250,21L318.436,37L333,116L285,201L159,179L176,125L220,91L180,44L250,21Z"/>
</g>
<g transform="matrix(1,0,0,1,-47,-21)">
<path class="zone" data-zone="3" d="M416,56L318.436,37L333,116L285,201L421,183L409,120L416,56Z"/>
</g>
<!-- Markers -->
<g transform="matrix(1,0,0,1,-47,-21)">
<rect class="marker" data-zone="1" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,106,-29.5)">
<rect class="marker" data-zone="2" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,30,-2.5)">
<rect class="marker" data-zone="1" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,132,60.5)">
<rect class="marker" data-zone="2" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,195,84)">
<rect class="marker" data-zone="3" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,204,-11.5)">
<rect class="marker" data-zone="3" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,230,33)">
<rect class="marker" data-zone="3" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,-21,15.5)">
<rect class="marker" data-zone="1" x="94" y="56" width="18" height="18"/>
</g>
<g transform="matrix(1,0,0,1,79,69.5)">
<rect class="marker" data-zone="2" x="94" y="56" width="18" height="18"/>
</g>
</svg>
</body>
</html>
JQuery have a Toggle function to solve your problem:
.toggle()
I hope it's helps ;)

Whats the correct way to get the bounding box of DOM elements containing svg

I have the following html and get strange results with getBoundingClientRect, if there are svg elements inside:
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<svg>
<g transform="translate(10,10) scale(1)">
<g class="nodes">
<g style="inline" transform="translate(20,20)">
<rect style="stroke: rgb(170, 170, 170); stroke-width: 1; fill: rgb(248, 248, 248);" width="100" height="90"></rect>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(182, 204, 216); stroke-width: 0;" x="0" y="0" height="20" width="100"></rect>
<text class="nodetext" x="3" y="15">Text 1</text>
</g>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(221, 185, 172); stroke-width: 0;" x="0" y="22" height="20" width="100"></rect>
<text class="nodetext" x="3" y="37">Test 2</text>
</g>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(221, 185, 180); stroke-width: 0;" x="0" y="44" height="20" width="100"></rect>
<text class="nodetext" x="3" y="59">Test 3</text>
</g>
<g class="nodebox">
<rect class="noderect" style="fill: rgb(236, 163, 154); stroke: rgb(212, 139, 130); stroke-width: 2;" x="0" y="66" height="20" width="100"></rect>
<text class="nodetext" x="3" y="81">Test 4</text>
<g class="nodeicon">
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" x="80" y="68">
<rect height="14" width="14" y="1" x="1" stroke="#888888" stroke-width="1" fill="#ffffff" fill-opacity="0.5"></rect>
<line x1="4" y1="8" x2="12" y2="8" stroke="#888888" stroke-width="2"></line>
</svg>
</g>
</g>
</g>
</g>
</g>
</svg>
</body>
</html>
I get a much greater rectangle than I would expect in Firefox.
When I inspect the objects, the displayed bounding box for the inner svg element is fine, but the surrounding g element (class nodeicon) is outside.
If I remove this g element, the next surrounding g element is outside.
The following picture shows this:
It looks like the offset of the svg is applied twice.
Is getBoundingClientRect the correct way to get the position and size of elements (e.g. the g element with class nodes) for this? Is there something wrong with the HTML or svg element or did I run into a Firefox bug?
I am using the current version of Firefox (58.0 64bit).
A problem that you have here is that the svg tag nested inside the g tag (.nodeicon) is starting a new viewport context. Strictly speaking it should not be nested inside a g tag anyway, but regardless, it isn't really necessary as you're using it as a method of grouping the two elements inside it - which is the purpose of the g tag.
Try removing the svg tag nested inside .nodeicon, and move the coordinates in that svg's x and y attributes to a transform attribute on the g tag.
i.e:
<g class="nodeicon" transform="translate(80, 68)">
<rect height="14" width="14" y="1" x="1" stroke="#888888" stroke-width="1" fill="#ffffff" fill-opacity="0.5"></rect>
<line x1="4" y1="8" x2="12" y2="8" stroke="#888888" stroke-width="2"></line>
</g>

Why isn't my svg's embedded javascript working in an html document?

I have the following code for an svg:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="470px" height="260px" version="1.1" onload="addEvents()" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<image xlink:href="baby.jpg" x="0" y="0" height="260px" width="470px">
</image>
<g transform="translate(100,50)">
<circle class="circle" id="tip1" cx="20" cy="0" r="10" stroke="" stroke-width="0" fill="rgb(143, 124, 184)" />
<g transform="translate(10,15)">
<polygon class="baby-tip tip1 arrow" points="0,10 10,0, 20,10" style="fill: rgb(143, 124, 184)" />
</g>
<rect class="baby-tip tip1" x="0" y="25" height="70" width="220" rx="5" ry="5" style="fill: rgb(143, 124, 184)"/>
<text class="baby-tip tip1" x="10" y="45">Here is a whole bunch of text.</text>
<text class="baby-tip tip1" x="10" y="60">Clearly this should wrap, but it</text>
<text class="baby-tip tip1" x="10" y="75">doesn't. What will we do?</text>
</g>
<g transform="translate(150,160)">
<circle class="circle" id="tip2" cx="20" cy="0" r="10" stroke="" stroke-width="0" fill="rgb(143, 124, 184)" />
<g transform="translate(10,15)">
<polygon class="baby-tip tip2 arrow" points="0,10 10,0, 20,10" style="fill: rgb(143, 124, 184)" />
</g>
<rect class="baby-tip tip2" x="0" y="25" height="70" width="220" rx="5" ry="5" style="fill: rgb(143, 124, 184)"/>
<text class="baby-tip tip2" x="10" y="45">Here is a whole bunch of text.</text>
<text class="baby-tip tip2" x="10" y="60">Clearly this should wrap, but it</text>
<text class="baby-tip tip2" x="10" y="75">doesn't. What will we do?</text>
</g>
<style>
.baby-tip {
font-size: 14px;
font-family: "MuseoSans100", Arial, Helvetica, sans-serif;
fill: white;
opacity: 0;
}
</style>
<script>
var log = console.info;
function addEvents() {
var dots = document.getElementsByClassName("circle");
var i = dots.length;
while(i--) {
toggleTips(dots[i]);
}
}
function closeOtherTips(otherTips) {
var l = otherTips.length;
while (l--) {
otherTips[l].style.opacity = 0;
}
}
function toggleTips(dot, l) {
window.dot = dot;
dot.addEventListener("click", function() {
var className = dot.id;
var tips = document.getElementsByClassName(className);
var otherTips = document.querySelectorAll('text:not(.' + className + '), rect:not(.' + className + '), polygon:not(.' + className + ')');
var t = tips.length;
closeOtherTips(otherTips);
while (t--) {
tips[t].style.opacity != 0 ? tips[t].style.opacity = 0 : tips[t].style.opacity = 1;
}
});
}
</script>
</svg>
When I save this content as test.svg, and view it in a browser, it runs beautifully - I should see two purple dots, and when I click each one I get a little tool tip, and only one displays at a time.
However, if I create the following html document:
<!DOCTYPE html>
<html>
<body>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<img class="baby-dot-svg" src="test.svg" />
<img class="baby-dot-bg" src="baby.jpg" />
</body>
</html>
Two things change:
1) the tooltip / all js functionality is lost - nothing happens when I click on the dots.
2) the link to baby.jpg no longer works - despite baby.jpg and the svg residing in the same directory, the background of the svg is blank.
See http://images.agoramedia.com/wte3.0/gcms/web_view_svg_test2.html to view this in action.
Why is this breaking?
There are mulitple ways to embed SVG files, but the <img> approach doesn't allow it to be interactive.
Try:
<object data="test.svg" type="image/svg+xml">
<img src="baby.jpg" />
</object>
For more information, see this answer.

Categories

Resources