font awesome SVG icons doesn't work on safari (iOS) - svg

I have following font awesome reference
<link href="css/fontawesome-free-5.12.1-web/css/all.css" rel="stylesheet">
<script defer src="css/fontawesome-free-5.12.1-web/js/all.js"></script>
Alternatively I also have premium kit reference
<script src="https://kit.fontawesome.com/7xxxx.js" crossorigin="anonymous"></script>
Both references renders icons on Google Chrome, but fails to work on Safari (iOS)
Console error says
NoModificationAllowedError: The object can not be modified
Here is the code all.js fontawesome library that is having issues
if (node.parentNode && node.outerHTML) {
node.outerHTML = newOuterHTML + (config.keepOriginalSource && node.tagName.toLowerCase() !== 'svg' ? "<!-- ".concat(node.outerHTML, " -->") : '');
} else if (node.parentNode) {
var newNode = document.createElement('span');
node.parentNode.replaceChild(newNode, node);
newNode.outerHTML = newOuterHTML;
}
How can I make this work for safari?
node.outerHTML = newOuterHTML + (config.keepOriginalSource && node.tagName.toLowerCase() !== 'svg' ? "<!-- ".concat(node.outerHTML, " -->") : '');

For some reason font-awesome <i> tag with font-awesome class inside SVG works on Chrome but not on safari.
So I had to replace my code
Old code:
var svg = d3.select("body")
.append("svg")
.attr("width", 800)
.attr("height", 900)
.append("i")
.attr("class", ({ property }) => {
const styleClass = getIconClass({ property})
return styleClass
})
Replaced to:
var svg = d3.select("body")
.append("svg")
.attr("width", 800)
.attr("height", 900);
var y = 70;
var x=10;
["\uf083", "\uf0c2", "\uf7ad", "\uf738", "\uf185"].forEach(function(code, i) {
svg.append("text")
.attr("x",x)
.attr("y",y)
.attr("style", "font-family: 'Font Awesome 5 Pro'; font-weight: 900; ")
.attr('font-size', function(d) { return '70px';} )
.text(function(d) { return code; });
y+=70;
x+=70;
});
JS Fiddle

Related

Phaser3 multimple scenes issus. "Uncaught ReferenceError: loadScene is not defined at config.js:17"

I want to make a game in Phaser3 with multiple scenes. When I try to run the code with "cordova run browser" it renders a grey screen and in the code inspect console I get the error: "Uncaught ReferenceError: loadScene is not defined at config.js:19".
//I linked all the files in the index.html
<script type="text/javascript" src="./js/config.js"></script>
<script type="text/javascript" src="./js/helpers.js"></script>
<script type="text/javascript" src="./js/loadScene.js"></script>
<script type="text/javascript" src="./js/mainMenu.js"></script>
<script type="text/javascript" src="./js/gamePlay.js"></script>
<script type="text/javascript" src="./js/gameOver.js"></script>```
// set the configuration file config.js
const gameState = {
score: 0
};
const config = {
type: Phaser.AUTO,
width: 800,
height: 1368,
physics: {
default: 'arcade',
arcade: { debug: true }
},
scene: [loadScene, mainMenu, gamePlay, gameOver] //**here a get the error**
};
const game = new Phaser.Game(config);
// loadScene.js is one of the scenes
class loadScene extends Phaser.Scene {
constructor() { super({ key: 'loadScene' }); }
preload() {
this.load.image('background', '../img/BG/bgVstretch.png');
}
create() {
window.addEventListener('resize', resize);
resize();
this.add.image(0, 0, 'background').setOrigin(0);
this.add.text(100, 100, "Loading Scene", { font: "20px Impact" });
this.input.on('pointerdown', () => {
this.scene.stop('loadScene');
this.scene.start('mainMenu');
});
}
}
// mainMenu.js gamePlay.js gameOver.js....have the same structure as loadScene.js
// helpers.js contains the functions that resizes the game according to the screen.
function resize() {
var canvas = gameState.game.canvas,
width = window.innerWidth,
height = window.innerHeight;
var wratio = width / height,
ratio = canvas.width / canvas.height;`
`if (wratio < ratio) {
canvas.style.width = width + "px";
canvas.style.height = (width / ratio) + "px";
} else {
canvas.style.width = (height * ratio) + "px";
canvas.style.height = height + "px";
}
}```
The game does not render. For now I just wanted it to switch from one scene to another on pointerdown.
if you would like to switch scenes for me the following code worked
`this.scene.switch('whatever the key is')`
I wrote this code after I created a rectangle, this code makes the rectangle interactive and when you click on it it takes you to the next level.
`advanceButton.setInteractive();
advanceButton.on('pointerup', ()=>{
this.scene.switch('level2')
})
});`
as for why your code isn't rendering remove the semicolon after you finish the key

Replacing a d3.js path transition with a new one?

I've seen this question and many other examples, but it isn't helping. I'm trying the last example on this page and after 5 seconds, I want the curved path that is being drawn, to completely disappear and 5 more seconds later, I want a new path to be created.
I've tried the below code, but although the entire svg element itself is removed, when I use appendGraph() to created the svg and the path again, the same old path re-appears. How can I ensure that the old path is completely removed and that the tick function also does not get called when the graph is removed?
The fiddle is here: http://jsfiddle.net/nav9/5uygqj9v/
And the code is:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<style>
svg {
font: 10px sans-serif;
}
.noselect {
/* these are to disable text selection */
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
opacity: 0.5;
shape-rendering: crispEdges;
}
rect.zoom {
stroke: steelblue;
fill-opacity: 0.3;
}
#placeholder {margin: 10px 5px 15px 70px;}
</style>
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
</head>
<body>
<div id="placeholder" ></div>
<script>
//---------globals
var timer = null, interval = 500, value = 0;
var value1 = 0;
var n = 143, duration = interval, now = new Date(Date.now() - duration), count = 0, data = d3.range(n).map(function() { return 0; });
var margin = {top: 20, right: 40, bottom: 50, left: 60}, width = 580 - margin.right, height = 420 - margin.top - margin.bottom;
var x = d3.time.scale().domain([now - (n - 2) * duration, now - duration]).range([0, width]);
var y = d3.scale.linear().domain([-1, 1]).range([height, 0]);
var line = d3.svg.line().interpolate("basis")
.x(function(d, i) { return x(now - (n - 1 - i) * duration); })
.y(function(d, i) { return y(d); });
var svg, path, yaxis, axis;
//--------program starts
appendGraph();
tick();
value1 = 0;
setTimeout(function() {removeGraph();}, 5000);
setTimeout(function() {addGraphAgain();}, 10000);
//-------------------------------functions -------------------------------
function appendGraph()
{
svg = d3.select("body").select("#placeholder").append("p").append("svg:svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "mainSVG")
.style("margin-left", -margin.left + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
axis = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(x.axis = d3.svg.axis().scale(x).orient("bottom"));
yaxis = svg.append("g")
.attr("class", "y axis")
.call(y.axis = d3.svg.axis().scale(y).orient("left"));
path = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("path")
.data([data])
.attr("id", "line1")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", "1.5px")
.style("visibility","visible");
}//appendGraph
//TODO: These tick functions could be simplified to handle more lines on the graph
function tick()
{
// push the accumulated count onto the back, and reset the count
value1 = Math.random() * 100;
if (value1 >= 0) {data.push(value1);} else {data.push(0);}//ensure that no NaN or undefined values corrupt the range
// update the domains
now = new Date();
x.domain([now - (n - 2) * duration, now - duration]);
count = 0;
// redraw the lines
svg.select("#line1").attr("d", line).attr("transform", null);
// slide the line left
path.transition().duration(duration).ease("linear").attr("transform", "translate(" + x(now - (n - 1) * duration) + ")").each("end", tick);
y.domain([0, 100]);
y = d3.scale.linear().domain([0, 100]).range([height, 0]);
yaxis.call(y.axis = d3.svg.axis().scale(y).orient("left"));
// pop the old data point off the front
data.shift();
console.log("tick being called");
}
function removeGraph()
{
path.transition().duration(0).each(function() { this.__transition__.active = 0; });//at least this is stopping tick from being called
svg.selectAll("*").remove();
//-------tried these too
// d3.select("#mainSVG").remove("svg");
// d3.select("#line1").remove("path");
// path.remove();
//d3.selectAll("path").attr("d", "Z");
console.log("REMOVED");
}//removeGraph
function addGraphAgain()
{
appendGraph();
tick();
value1 = 0;
console.log("ADDED AGAIN");
}//addGraphAgain
</script>
</body>
</html>
Not an exact answer to this question, but since the reason I asked this was because I wanted to have phases where I wanted to send null inputs to the graph and there seemed no other way to do it other than to remove the line and replace it with a new line.
The trick to handle null or NaN data or missing data in d3.js or to simply not display data for a while is to use defined.
A working example of it here and in the line transition, it's like this:
I supply a random number at if (counter%5==0) ran = null;data.push(ran); and .defined(function(d) { return d != null; }) takes care of the null, by not drawing a line there.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.line {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var n = 40,
random = d3.random.normal(0, .2),
data = d3.range(n).map(random);
var margin = {top: 20, right: 20, bottom: 20, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.domain([0, n - 1])
.range([0, width]);
var y = d3.scale.linear()
.domain([-1, 1])
.range([height, 0]);
var line = d3.svg.line()
.defined(function(d) { return d != null; })
.x(function(d, i) { return x(i); })
.y(function(d, i) { return y(d); });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + y(0) + ")")
.call(d3.svg.axis().scale(x).orient("bottom"));
svg.append("g")
.attr("class", "y axis")
.call(d3.svg.axis().scale(y).orient("left"));
var path = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
var counter = 0;
tick();
function tick()
{
// push a new data point onto the back
var ran = random();
counter++;
if (counter%5==0) ran = null;
data.push(ran);
// redraw the line, and slide it to the left
path
.attr("d", line)
.attr("transform", null)
.transition()
.duration(500)
.ease("linear")
.attr("transform", "translate(" + x(-1) + ",0)")
.each("end", tick);
// pop the old data point off the front
data.shift();
}
</script>

d3 objects do not render within svg bounds on IE10

As shown in the image, a circle drawn with center at 0,0 displays correctly on chrome, but on IE10 it overflows the SVG bounds. What do I need to do to get this to render correctly on IE?
Here is the code:
<body>
<div id="chart1">
</div>
<script>
var width = 50,height = 50;
var SVGmap = d3.select("#chart1")
.append("svg")
.attr("width", width)
.attr("height", height);
var g = SVGmap.append("g");
g.append("circle")
.style("stroke", "gray")
.style("fill", "red")
.attr("r", 40)
.attr("cx", 0)
.attr("cy", 0)
</script>
</body>
Thanks Robert. Adding .attr("overflow", "hidden") to the SVG Element solves this.

d3 Gives a 406 Error when trying to run d3js example on IIS

I have setup a basic IIS server and am trying to demonstrate a d3js example. First I create an html page with the example code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
</script>
and then i create the data.csv file:
State,Under 5 Years,5 to 13 Years,14 to 17 Years,18 to 24 Years,25 to 44 Years,45 to 64 Years,65 Years and Over
AL,310504,552339,259034,450818,1231572,1215966,641667
AK,52083,85640,42153,74257,198724,183159,50277
AZ,515910,828669,362642,601943,1804762,1523681,862573
AR,202070,343207,157204,264160,754420,727124,407205
CA,2704659,4499890,2159981,3853788,10604510,8819342,4114496
CO,358280,587154,261701,466194,1464939,1290094,511094
CT,211637,403658,196918,325110,916955,968967,478007
DE,59319,99496,47414,84464,230183,230528,121688
DC,36352,50439,25225,75569,193557,140043,70648
FL,1140516,1938695,925060,1607297,4782119,4746856,3187797
GA,740521,1250460,557860,919876,2846985,2389018,981024
HI,87207,134025,64011,124834,356237,331817,190067
ID,121746,201192,89702,147606,406247,375173,182150
IL,894368,1558919,725973,1311479,3596343,3239173,1575308
IN,443089,780199,361393,605863,1724528,1647881,813839
IA,201321,345409,165883,306398,750505,788485,444554
KS,202529,342134,155822,293114,728166,713663,366706
KY,284601,493536,229927,381394,1179637,1134283,565867
LA,310716,542341,254916,471275,1162463,1128771,540314
ME,71459,133656,69752,112682,331809,397911,199187
MD,371787,651923,316873,543470,1556225,1513754,679565
MA,383568,701752,341713,665879,1782449,1751508,871098
MI,625526,1179503,585169,974480,2628322,2706100,1304322
MN,358471,606802,289371,507289,1416063,1391878,650519
MS,220813,371502,174405,305964,764203,730133,371598
MO,399450,690476,331543,560463,1569626,1554812,805235
MT,61114,106088,53156,95232,236297,278241,137312
NE,132092,215265,99638,186657,457177,451756,240847
NV,199175,325650,142976,212379,769913,653357,296717
NH,75297,144235,73826,119114,345109,388250,169978
NJ,557421,1011656,478505,769321,2379649,2335168,1150941
NM,148323,241326,112801,203097,517154,501604,260051
NY,1208495,2141490,1058031,1999120,5355235,5120254,2607672
NC,652823,1097890,492964,883397,2575603,2380685,1139052
ND,41896,67358,33794,82629,154913,166615,94276
OH,743750,1340492,646135,1081734,3019147,3083815,1570837
OK,266547,438926,200562,369916,957085,918688,490637
OR,243483,424167,199925,338162,1044056,1036269,503998
PA,737462,1345341,679201,1203944,3157759,3414001,1910571
RI,60934,111408,56198,114502,277779,282321,147646
SC,303024,517803,245400,438147,1193112,1186019,596295
SD,58566,94438,45305,82869,196738,210178,116100
TN,416334,725948,336312,550612,1719433,1646623,819626
TX,2027307,3277946,1420518,2454721,7017731,5656528,2472223
UT,268916,413034,167685,329585,772024,538978,246202
VT,32635,62538,33757,61679,155419,188593,86649
VA,522672,887525,413004,768475,2203286,2033550,940577
WA,433119,750274,357782,610378,1850983,1762811,783877
WV,105435,189649,91074,157989,470749,514505,285067
WI,362277,640286,311849,553914,1487457,1522038,750146
WY,38253,60890,29314,53980,137338,147279,65614
When I access the page, nothing displays...A quick look through fiddler shows the html content downloads fine (you can also see it in show source). You can also look at the data.csv file directly by accessing it from the url
http://localhost/data.csv
The problem shown in fiddler is a 406 error when d3js attempts to load the CSV file. Any ideas?
Thanks
What does your HTTP Request's Accept header contain? Apache is probably configured in such a way so as to return a 406 because the value in the Accept header does not include whatever MIME type your CSV is returning.
See
http://blogs.msdn.com/b/ieinternals/archive/2011/03/27/http-406-not-acceptable-php-ie9-standards-mode-accepts-only-text_2f00_css-for-stylesheets.aspx for a similar problem sometimes seen in browsers.
Change the build action for the csv file to "Resource" in the file's properties using your Visual Studio.

Add text/label onto links in D3 force directed graph

I've been working on modified force directed graph and having some problems with adding text/label onto links where the links are not properly aligned to nodes. How to fix it?
And how I can add an event listener to an SVG text element? Adding .on("dblclick",function(d) {....} just doesn't work.
Here's the code snippet:
<style type="text/css">
.link { stroke: #ccc; }
.routertext { pointer-events: none; font: 10px sans-serif; fill: #000000; }
.routertext2 { pointer-events: none; font: 9px sans-serif; fill: #000000; }
.linktext { pointer-events: none; font: 9px sans-serif; fill: #000000; }
</style>
<div id="canvas">
</div>
<script type="text/javascript" src="d3/d3.js"></script>
<script type="text/javascript" src="d3/d3.layout.js"></script>
<script type="text/javascript" src="d3/d3.geo"></script>
<script type="text/javascript" src="d3/d3.geom.js"></script>
<script type="text/javascript">
var w = 960,
h = 600,
size = [w, h]; // width height
var vis = d3.select("#canvas").append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("transform", "translate(0,0) scale(1)")
.call(d3.behavior.zoom().on("zoom", redraw))
.attr("idx", -1)
.attr("idsel", -1)
;
var routers = {
nodes: [
{id:0, name:"ROUTER-1", group:1, ip: "123.123.123.111",
x:394.027, y:450.978,outif:"ge-0/1/0.0",inif:""},
{id:1, name:"ROUTER-2", group:1, ip: "123.123.123.222",
x:385.584, y:351.513,outif:"xe-4/2/0.0",inif:"ge-5/0/3.0"},
{id:2, name:"ROUTER-3", group:1, ip: "123.123.123.333",
x:473.457, y:252.27,outif:"ae1.0",inif:"xe-1/0/1.0"},
{id:3, name:"ROUTER-4", group:2, ip: "123.123.123.444",
x:723.106, y:266.569,outif:"as0.0",inif:"ae1.0"},
{id:4, name:"ROUTER-5", group:3, ip: "123.123.123.555",
x:728.14, y:125.287,outif:"so-4/0/2.0",inif:"as1.0"},
{id:5, name:"ROUTER-6", group:3, ip: "123.123.123.666",
x:738.975, y:-151.772,outif:"",inif:"PO0/2/2/1" }
],
links: [
{source:0, target:1, value:3, name:'link-1',speed:"1000mbps",
outif:"ge-0/1/0.0",nextif:"ge-5/0/3.0"},
{source:1, target:2, value:3, name:'link-2',speed:"10Gbps",
outif:"xe-4/2/0.0",nextif:"xe-1/0/1.0"},
{source:2, target:3, value:3, name:'link-3',speed:"20Gbps",
outif:"ae1.0",nextif:"xe-1/2/1.0"},
{source:3, target:4, value:3, name:'link-4',speed:"1Gbps",
outif:"as0.0",nextif:"as1.0"},
{source:4, target:5, value:3, name:'link-5',speed:"OC3",
outif:"so-4/0/2.0",nextif:"PO0/2/2/1"}
]
};
var force = d3.layout.force()
.nodes(routers.nodes)
.links(routers.links)
.gravity(0)
.distance(100)
.charge(0)
.size([w, h])
.start();
var link = vis.selectAll("g.link")
.data(routers.links)
.enter().append("svg:g");
link.append("svg:line")
.attr("class", "link")
.attr("title", function(d) { return "From: "+d.outif+", To: "+d.nextif })
.attr("style", "stroke:#00d1d6;stroke-width:4px")
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
link.append("svg:text")
.attr("class", "linktext")
.attr("dx", function(d) { return d.source.x; })
.attr("dy", function(d) { return d.source.y; })
.text("some text to add...");
var node = vis.selectAll("g.node")
.data(routers.nodes)
.enter()
.append("svg:g")
.attr("id", function(d) { return d.id;})
.attr("title", function(d) {return d.ip})
.attr("class", "node")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.on("dblclick",function(d) {
alert('router double-clicked'); d3.event.stopPropagation();
})
.on("mousedown", function(d) {
if (d3.event.which==3) {
d3.event.stopPropagation();
alert('Router right-clicked');
}
})
.call(force.drag);
node.append("svg:image")
.attr("class", "node")
.attr("xlink:href", "router.png")
.attr("x", -24)
.attr("y", -18)
.attr("width", 48)
.attr("height", 36);
node.append("svg:text")
.attr("class", "routertext")
.attr("dx", -30)
.attr("dy", 20)
.text(function(d) { return d.name });
node.append("svg:text")
.attr("class", "routertext2")
.attr("dx", 0)
.attr("dy", -20)
.attr("title", "some title to show....")
.text(function(d) { return d.outif })
.on("click", function(d,i) {alert("outif text clicked");})
.call(force.drag);
node.append("svg:text")
.attr("class", "routertext2")
.attr("dx", -40)
.attr("dy", 30)
.text(function(d) { return d.inif });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; });
});
function redraw() {
vis.attr("transform",
"translate(" + d3.event.translate + ")"
+ "scale(" + d3.event.scale + ")");
};
</script>
Use a smaller example outside of D3 to see how the SVG stuff works. Then just rebuild this structure using D3 and your custom data.
<html>
<body>
<svg width="600px" height="400px">
<defs>
<!-- DEFINE AN ARROW THAT WE CAN PLACE AT THE END OF EDGES. -->
<!-- USE REFX TO MOVE THE ARROW'S TIP TO THE END OF THE PATH. -->
<marker
orient="auto"
markerHeight="12"
markerWidth="12"
refY="0"
refX="9"
viewBox="0 -5 10 10"
id="ARROW_ID"
style="fill: red; fill-opacity: 0.5;">
<path d="M0, -5L10, 0L0, 5"></path>
</marker>
</defs>
<!-- DEFINE A PATH. SET ITS END MARKER TO THE ARROW'S ID. -->
<!-- SET FILL NONE TO DRAW A LINE INSTEAD OF A SHAPE. -->
<path
d="M100,100 A300,250 0 0,1 500,300"
style="fill:none; stroke:grey; stroke-width:2px;"
id="PATH_ID"
marker-end="url(#ARROW_ID)" />
<!-- DEFINE A TEXT ELEMENT AND SET FONT PROPERTIES. -->
<!-- USE DY TO MOVE TEXT ABOVE THE PATH. -->
<text
style="text-anchor:middle; font: 16px sans-serif;"
dy="-12">
<!-- DEFINE A TEXT PATH FOLLOWING THE PATH DEFINED ABOVE. -->
<!-- USE STARTOFFSET TO CENTER TEXT. -->
<textPath
xlink:href="#PATH_ID"
startOffset="50%">Centered edge label</textPath>
</text>
</svg>
</body>
</html>
Have you experimented with creating text elements separately in a standalone (simpler) example? It should give you a better feeling for how the different attributes control positioning.
For vertical alignment, use the "dy" attribute:
by default, the baseline of the text is at the origin (bottom-aligned)
a dy of .35em centers the text vertically
a dy of .72em places the topline of the text at the origin (top-aligned)
Using em units is nice because it will scale automatically based on the font size. If you don't specify units (such as -20 in your code), it defaults to pixels.
For horizontal alignment, use the "text-anchor" attribute:
the default is "start" (left-aligned for left-to-right languages)
"middle"
"end"
There's also the "dx" attribute, which is tempting to use for padding. However, I wouldn't recommend it because there is a bug in Firefox and Opera that cause it to not work as expected in conjunction with text-anchor middle or end.
Created JS fiddle example for showing labels over links in D3 Forced layout chart
See working demo in JS Fiddle: http://jsfiddle.net/bc4um7pc/
Give Id's to your path like below
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", function(d) { return "link " + d.type; })
.attr("id",function(d,i) { return "linkId_" + i; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
Use SVG textPath element for associating labels with above links by specifying its 'xlink:href' attribute to point to its respective link/path.
var linktext = svg.append("svg:g").selectAll("g.linklabelholder").data(force.links());
linktext.enter().append("g").attr("class", "linklabelholder")
.append("text")
.attr("class", "linklabel")
.style("font-size", "13px")
.attr("x", "50")
.attr("y", "-20")
.attr("text-anchor", "start")
.style("fill","#000")
.append("textPath")
.attr("xlink:href",function(d,i) { return "#linkId_" + i;})
.text(function(d) {
return "my text"; //Can be dynamic via d object
});
I am using an arch as a link between nodes with a label text placed in the middle. Here is a code snippet:
var vis = d3.select("body")
.append("svg")
.attr("width", 600)
.attr("height", 400)
.append("g");
var force = d3.layout.force()
.gravity(.05)
.distance(120)
.charge(-100)
.size([600, 400]);
var nodes = force.nodes(), links = force.links();
// make an arch between nodes and a text label in the middle
var link = vis.selectAll("path.link").data(links, function(d) {
return d.source.node_id + "-" + d.target.node_id; });
link.enter().append("path").attr("class", "link");
var linktext = vis.selectAll("g.linklabelholder").data(links);
linktext.enter().append("g").attr("class", "linklabelholder")
.append("text")
.attr("class", "linklabel")
.attr("dx", 1)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return "my label" });
// add your code for nodes ....
force.on("tick", tick); force.start();
function tick () {
// curve
link.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + ","
+ dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
// link label
linktext.attr("transform", function(d) {
return "translate(" + (d.source.x + d.target.x) / 2 + ","
+ (d.source.y + d.target.y) / 2 + ")"; });
// nodes
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; });
}
Just add this line:
.attr("text-anchor", "middle")
to the code after the line:
node.append("svg:text")
it should look like this:
node.append("svg:text")
.attr("text-anchor", "middle")
......

Resources