Line break in C3 generated SVG chart via JavaScript - javascript

I need a help on generating line break in html.
Javascript
var x = "jun";
var y = "2015";
var calculate= x + "<br>" + y;
Html returns like below
<div>jan <br> 2015</div>
expected result: i need a line break in html but should not render <br> tag.
Update: what i want is "jan" in first line and next line "2015"
I am using these values in c3 chart x values.
JSFIDDLE
Thanks in Advance.

Your question statement was a bit unprecise : You are using C3.js which will produce svg element.
So the markup returned was actually <tspan dx="0" dy=".71em" x="0">0<br>2014</tspan>.
C3 will use the textContent property of the tspan to append the text content returned by your function.
As already said in other questions, you can't add a line break into <tspan> elements.
So the solution is effectively to create a new tspan just under the other one, in the same <text> element.
Unfortunately, there is no way to get these precise elements except by looping through all others tspans, so this may sounds like a real hack but here is a script that will do what you want...
// get our svg doc
var svg = document.querySelector('svg');
// get our tspans element
var tspans = svg.querySelectorAll('tspan');
// transform it to an array so the clones don't add to the list
var ts = Array.prototype.slice.call(tspans);
for(var i = 0; i<ts.length; i++){
// get the content
var cont = ts[i].textContent.split('\n');
// that wasn't the good one...
if(cont.length<2) continue;
// create a clone
var clone = ts[i].cloneNode(1);
// set the text to the new line
clone.textContent = cont[1];
// space it a litlle bit more
clone.setAttribute('dy', '0.9em')
// set the good text to the upperline
ts[i].textContent = cont[0];
// append our clone
ts[i].parentNode.insertBefore(clone, ts[i].nextSibling)
};
var chart = c3.generate({
data: {
json: [{
date: '2014-01-01',
upload: 200,
download: 200,
total: 400
}, {
date: '2014-01-02',
upload: 100,
download: 300,
total: 400
}, {
date: '2014-02-01',
upload: 300,
download: 200,
total: 500
}, {
date: '2014-02-02',
upload: 400,
download: 100,
total: 500
}],
keys: {
x: 'date',
value: ['upload', 'download']
}
},
axis: {
x: {
type: 'timeseries',
tick: {
format: function (x) {
if (x.getDate() === 1) {
return x.getMonth() + '\n' + x.getFullYear();
}
}
}
}
}
});
// get our svg doc
var svg = document.querySelector('svg');
// get our tspans element
var tspans = svg.querySelectorAll('tspan');
// transform it to an array so the clones don't add to the list
var ts = Array.prototype.slice.call(tspans);
for(var i = 0; i<ts.length; i++){
// get the content
var cont = ts[i].textContent.split('\n');
// that wasn't the good one...
if(cont.length<2) continue;
// create a clone
var clone = ts[i].cloneNode(1);
// set the text to the new line
clone.textContent = cont[1];
// space it a litlle bit more
clone.setAttribute('dy', '0.9em')
// set the good text to the upperline
ts[i].textContent = cont[0];
// append our clone
ts[i].parentNode.insertBefore(clone, ts[i].nextSibling)
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://rawgit.com/masayuki0812/c3/master/c3.js"></script>
<link href="https://rawgit.com/masayuki0812/c3/master/c3.css" rel="stylesheet">
<div id="chart"></div>

You need to create new <tspan> for each new line. Reason is that <tspan> is usually found inside <text> element. Which has certain coordinates.
You cannot go "against" those coordinates.
The only thing you can do is create another <tspan> with different set of coordinates and position it as you like.
Because SVG Text Elements are rendered using the same rendering methods as the rest of the SVG Graphical Elements, the same coordinate
system, transformations, ... etc also apply.
The SVG Text Element renders the first character at the initial
current text position.
This position is defined by the 'x' and 'y' attributes of the SVG Text
Element.
Within a <text> element, text and font properties and the current text
position can be adjusted with absolute or relative coordinate values
by including a <tspan> element.

Perhaps that's what you need:
var calculate= '<pre>' + x + '\n' + y + '</pre>';
You have to put the whole thing in pre-tags that the \n is interpreted as a line break.
About: http://www.sitepoint.com/everything-need-know-html-pre-element/
Demo on CodePen: http://codepen.io/mizech/pen/gPOrEz

i have tried following code in abc.html and it's working.please try.
<!DOCTYPE html>
<html>
<body>
<div id="demo"></div>
<script>
var x = "jun";
var y = "2015";
document.getElementById("demo").innerHTML =x+"<br>"+y;
</script>
</body>
</html>

Related

How to highlight a line when mouse over it in observable plot using JavaScript?

My chart looks like so::
and here is my code:
linePlot = Plot.plot({
marginLeft: 60, // space to the left of the chart
y: {
type: "log", // set the type
},
marks: [
Plot.line(data, {x: "timestamp", y: "views", z:"artist", title: d=>`${d.artist}`,})
]
})
I want to highlight or change color of each line when the mouse is over it.
The easiest thing to do would be to attach a pointerenter event to the lines. Since you're using Observable, to use D3 to handle that process. Here's what it looks like on Observable:
https://observablehq.com/d/2e1daf099a7aaaea
To be clear, you are using two libraries: D3 and Plot, both of which are automatically available on Observable. You can use them both in vanilla Javascript pretty easily, though:
// Manufacture some data
let pt_lists = d3.range(10).map(() => {
let cur = 0;
return d3.range(1000).map(function(x) {
let step = 2 * d3.randomInt(0, 2)() - 1;
cur = cur + step;
return [x, cur];
});
});
// Plot the data
let plot = Plot.plot({
marks: pt_lists.map((pts) => Plot.line(pts, {
strokeWidth: 2
}))
});
// Here's where the action is.
// We use d3 to select all the paths in the plot
d3.select(plot)
.selectAll("path")
// React when the pointer hovers over the path.
.on("pointerenter", function() {
d3.select(plot).selectAll("path").attr("opacity", 0.2);
d3.select(this).attr("opacity", 1);
});
// Reset the appearance when the pointer leaves the SVG
d3.select(plot).on("pointerleave", function() {
d3.select(plot).selectAll("path").attr("opacity", 1);
});
// Attach the plot to the container DIV
d3.select('#chart').append(() => plot)
<script src="https://cdn.jsdelivr.net/npm/d3#7"></script>
<script src="https://cdn.jsdelivr.net/npm/#observablehq/plot#0.6"></script>
<div id="chart" style="width: 600px; height: 400px"></div>
It might also be possible to do the interaction in css:
d3.select(chart)
.append("svg:style")
.text(`
path:hover {stroke-width: 2px;}
`)

Data label not rendered on sapui5 viz dual y-axis graph when line and column intersect

I have a requirement to show data labels of two graphs on the same axes.
Only when they intersect, one of the two labels won't show. This can be demonstrated below:
As you can see on the 2nd, 5th and 6th columns from the left with values 0%, 7% and 8% respectively
only the orange line values are shown but the blue column values are missing.
This is the final html of the graph after rendering:
So data-datapoint-id 142, 145 and 146 are missing from the html.
I tried using the plotArea.dataLabel.renderer function as a manipulation of what was proposed here but nothing changed, still not rendering.
Anyone encountered a similar problem? Is that a sapui5 issue or can it be fixed by manually inserting the labels into the HTML if so how?
Thanks,
Ori
Using SVG text and jQuery I managed to manually insert the labels into the top middle of the blue rectangle columns.
This is the result, not perfect but works:
and this is the code:
chart.setVizProperties({
plotArea: {
dataLabel: {
formatString: {'פחת כללי': FIORI_PERCENTAGE_FORMAT_2},
renderer: function (oLabel) {
// Create empty text node to be returned by the function such that the label won't be rendered automatically
var node = document.createElement("text");
if (oLabel.ctx.measureNames === "כמות פחת כללי") {
var kamutLabelIdx = oLabel.ctx._context_row_number;
// Use jQuery and SVG to manipulate the HTML
var kamutLabelQuery = '[data-id=\"' + kamutLabelIdx + '\"]';
var kamutColumn = $(kamutLabelQuery)[0];
// Create text element as child of the column
kamutColumn.innerHTML += "<text>" + oLabel.text + "</text>";
var labelNode = $(kamutLabelQuery += ' text');
// Set the label position to be at the middle of the column
const labelLength = 60;
const xPos = (labelLength + oLabel.dataPointWidth) / 2;
const yPos = oLabel.styles['font-size'];
labelNode.attr({
"textLength" : labelLength,
"x" : xPos,
"y" : yPos,
"font-size" : yPos
});
return node;
}
}
}
}
});
The oLabel parameter of the renderer function provides useful info about the data label to be created:
I still wonder if that's a bug with sapui5 vizframe and if there is a simpler way to do this.
Please let me know of your thoughts

Snap.svg and dynamic text

I'm trying to place text dynamically into an svg created by Snap, this is what I tried:
this.setContent(
`<svg id="${this.svgId}"></svg>`
);
var snap = Snap($(`#${this.svgId}`)[0]);
text = "asdfsdfsdsfd";
var rect = snap.paper.rect(0, 0, 50, text.length*3 + 4, 10);
snap.text(1.5,10, text);
console.log("rect", rect);
console.log("snap", snap);
rect.attr({
fill: "#FFFFFF",
fillOpacity: 0.6,
});
I get this:
I want the rectangle to be just a little bigger than the text, but there must be a better way to do it than to calculate the length and height of the text, and that's assuming the font size won't change.
This is the only result I found regarding text in the snap docs: http://snapsvg.io/docs/#Paper.text
You could try using getBBox() on the text element, and use that to figure the size of the rect. getBBox() wll give you the x,y,width,height,x2,y2 figures to help.
var text = s.text(0,0,'blah blah')
var bb = text.getBBox();
var rect = s.rect(bb.x, bb.y, bb.width, bb.height )
Adjusting with offsets for whatever padding etc that you want. You may also need to allow for stroke widths, as I don't think that's included.

Add javascript to Wordpress loop with class selection

I would like to add category icons to a Wordpress page, each icon animated with snap.svg.
I added the div and inside an svg in the loop that prints the page (index.php). All divs are appearing with the right size of the svg, but blank.
The svg has a class that is targeted by the js file.
The js file is loaded and works fine by itself, but the animation appears only in the first div of that class, printed on each other as many times it is counted by the loop (how many posts there are on the actual page from that category).
I added "each()" and the beginning of the js, but is not allocating the animations on their proper places. I also tried to add double "each()" for the svg location and adding the snap object to svg too, but that was not working either.
I tried to add unique id to each svg with the post-id, but i could not pass the id from inside the loop to the js file. I went through many possible solutions I found here and else, but none were adaptable, because my php and js is too poor.
If you know how should I solve this, please answer me. Thank you!
// This is the js code (a little trimmed, because the path is long with many randoms, but everything else is there):
jQuery(document).ready(function(){
jQuery(".d-icon").each(function() {
var dicon = Snap(".d-icon");
var dfirepath = dicon.path("M250 377 C"+ ......+ z").attr({ id: "dfirepath", class: "dfire", fill: "none", });
function animpath(){ dfirepath.animate({ 'd':"M250 377 C"+(Math.floor(Math.random() * 20 + 271))+ .....+ z" }, 200, mina.linear);};
function setIntervalX(callback, delay, repetitions, complete) { var x = 0; var intervalID = window.setInterval(function () { callback(); if (++x === repetitions) { window.clearInterval(intervalID); complete();} }, delay); }
var dman = dicon.path("m136 ..... 0z").attr({ id: "dman", class:"dman", fill: "#222", transform: "r70", });
var dslip = dicon.path("m307 ..... 0z").attr({ id: "dslip", class:"dslip", fill: "#196ff1", transform:"s0 0"});
var dani1 = function() { dslip.animate({ transform: "s1 1"}, 500, dani2); }
var dani2 = function() { dman.animate({ transform: 'r0 ' + dman.getBBox().cx + ' ' + dman.getBBox(0).cy, opacity:"1" }, 500, dani3 ); }
var dani3 = function() { dslip.animate({ transform: "s0 0"}, 300); dman.animate({ transform: "s0 0"}, 300, dani4); }
var dani4 = function() { dfirepath.animate({fill: "#d62a2a"}, 30, dani5); }
var dani5 = function() { setIntervalX(animpath, 200, 10, dani6); }
var dani6 = function() { dfirepath.animate({fill: "#fff"}, 30); dman.animate({ transform: "s1 1"}, 100); }
dani1(); }); });
I guess your error is here:
var dicon = Snap(".d-icon");
You are passing a query selector to the Snap constructor, this means Snap always tries to get the first DOM element with that class, hence why you're getting the animations at the wrong place.
You can either correct that in two ways:
Declare width and height inside the constructor, for example var dicon = Snap(800, 600);
Since you are using jQuery you can access to the current element inside .each() with the $(this) keyword. Since you are using jQuery instead of the dollar you could use jQuery(this).
Please keep in mind this is a jQuery object and probably Snap will require a DOM object. In jQuery you can access the dom object by appending a [0] after the this keyword. If var dicon = Snap( jQuery(this) ); does not work you can try with var dicon = Snap( jQuery(this)[0] );
Additionally, you have several .attr({id : '...', in your code. I assume you are trying to associate to the paths an ID which are not unique. These should be relatively safe since they sit inside a SVG element and I don't see you are using those ID for future selection.
But if you have to select those at a later time I would suggest to append to these a numerical value so you wont have colliding ID names.

is it possible to ignore 0 values when creating a graph in dygraph

<html>
<head>
<script type="text/javascript"
src="dygraph-combined.js"></script>
</head>
<body>
<div id="graphdiv"></div>
<script type="text/javascript">
g = new Dygraph(
// containing div
document.getElementById("graphdiv"),
// CSV or path to a CSV file.
"Date,Duration,Error\n" +
"2008-05-07,75,23\n" +
"2008-05-08,70,45\n" +
"2008-05-09,70,0\n" +
"2008-05-10,70,23\n" +
"2008-05-11,70,11\n" +
"2008-05-12,70,\n" +
"2008-05-13,80,33\n",
{
// options go here. See http://dygraphs.com/options.html
legend: 'always',
animatedZooms: true,
title: 'dygraphs chart template',
includeZero: false
}
);
</script>
</body>
</html>
Is it possible to for the graph to ignore the zero or null values so it connects directly from 45 to 23 or 11 to 33. in excel we the option does this option exists? incluedZero does not seem to have any affect.
The really simple answer is to remove the comma where the value is unneeded or not wanted.
So for example
"2008-05-09,70,0\n"+
//Should be "2008-05-09,70\n"+
and
"2008-05-12,70,\n"+
//Should be "2008-05-12,70\n"+
However, if you really cannot modify the data that is being passed to DyGraphs, then you have two options
Create the graph, pull out the data, and then remove the column value where it equals 0 or null using array.pop(), then reload the modified data back into Dygraphs using updateOptions(). You can use the method I describe in my answer here as a starting point
OR
Create a custom plotter (as below) that just draws a line to the next non-zero/null value. The above two are better solutions because Dygraphs will still think that there are zero values at certain points in time (because it is afterall in the data you gave it).
//Define the custom plotter
var custom_plotter = function(e) {
var context = e.drawingContext;
var points = e.points;
var num_points = points.length;
context.beginPath(); //Start
//Move "paint brush" to the start location
context.moveTo(points[0].canvasx, points[0].canvasy);
var p; //var to store current point
for(var i = 1; i < num_points; i++) {
p = points[i];
if(p.yval != 0 && p.yval != null) {
//Map out a line if the current value is desirable
context.lineTo(p.canvasx, p.canvasy);
}
}
context.stroke(); //Actually draw the lines mapped out in the loop above
context.closePath(); //Close
}
g = new Dygraph(
// containing div
document.getElementById("graphdiv"),
// CSV or path to a CSV file.
"Date,Duration,Error\n" +
"2008-05-07,75,23\n" +
"2008-05-08,70,45\n" +
"2008-05-09,70,0\n" +
"2008-05-10,70,23\n" +
"2008-05-11,70,11\n" +
"2008-05-12,70,\n" +
"2008-05-13,80,33\n",
{
// options go here. See http://dygraphs.com/options.html
legend: 'always',
animatedZooms: true,
title: 'dygraphs chart template',
plotter: custom_plotter, //Pass Dygraphs the custom plotter defined above
}
);

Categories