Customize the amCharts Stock Chart legend - javascript

I use an amCharts' Stock Chart with the comparison function. I use the StockLegend object for the legend and I want to customize the valueTextComparing parameter. Actually, I have this :
var stockLegend = new AmCharts.StockLegend();
stockLegend.markerType = 'bubble';
stockLegend.markerSize = 8;
stockLegend.horizontalGap = 1;
stockLegend.spacing = 100;
stockLegend.periodValueText = '[[value.close]]';
stockLegend.valueTextComparing = '[[value]] | [[percents.value]]%';
What I want is to have two different colors for [[percents.value]] switch the value is positive or negative (and add bold effect on all the valueTextComparing).
I see in the documentation a valueFunction parameter, but not the equivalent for Comparing.
Can you help me?

OK I find a way to do what I want. It's a bit cheating but it's work.
First, I use a specific character to separate the value and the percent (here the "|") :
stockLegend.periodValueText = '[[value.close]]|[[percents.value.close]]%';
stockLegend.valueTextComparing = '[[value]]|[[percents.value]]%';
After that, I created another Legend without amCharts in HTML :
<div id="graph_second_legend">
<div id="gsl_0_circle"></div>
<div id="gsl_0"></div>
<div id="gsl_1"></div>
<div id="gsl_2_circle"></div>
<div id="gsl_2"></div>
<div id="gsl_3"></div>
</div>
Then, I created a function to change this legend :
function parseLegend($){
$('.amChartsLegend text').each(function(index){
switch(index){
case 0:
var text = $(this).text();
var content = '<span class="graph_fund_label">' + text + '</span>';
$('#gsl_'+index).html(content);
break;
case 2:
var text = $(this).text();
var content = '<span class="graph_index_label">' + text + '</span>';
$('#gsl_'+index).html(content);
break;
default:
var text = $(this).text().split('|');
var negative = text[1].split('-');
var negaClass = '';
if(negative.length > 1){
negaClass = ' negative';
}
var content = '<span class="graph_vl_amount">' + text[0] + '</span> ';
content = content + '<span class="graph_vl_percent' + negaClass + '">' + text[1] + '</span>';
$('#gsl_'+index).html(content);
break;
}
});
}
And finally, I call this function when graph selection changed :
chart.addListener("zoomed", function(event){
parseLegend($);
});
And when the mouse is moving hover the graph :
$('.amChartsPanel').mouseover(function(){
setTimeout(function(){parseLegend($);}, 10);
});
$('.amChartsPanel').mouseout(function(){
setTimeout(function(){parseLegend($);}, 10);
});
$('.amChartsPanel').mousemove(function(){
setTimeout(function(){parseLegend($);}, 10);
});
$('.amChartsPanel').mouseleave(function(){
setTimeout(function(){parseLegend($);}, 10);
});
(I used a timeout because amCharts take a moment to change the Legend and the javascript events are too fast for him).

Related

How to make custom legends on multiple pie charts amcharts 4?

Imagine that we have a pie chart like the code bellow:
am4core.ready(function() {
// Create chart instance
chartReg[id] = am4core.create(id, am4charts.PieChart);
// Add data
chartReg[id].data = data;
chartReg[id].innerRadius = 60;
// Add and configure Series
var pieSeries = chartReg[id].series.push(new am4charts.PieSeries());
pieSeries.dataFields.value = "value";
pieSeries.dataFields.category = "key";
pieSeries.ticks.template.disabled = true;
pieSeries.alignLabels = false;
// Create custom legend
chartReg[id].events.on("ready", function(event) {
// populate our custom legend when chart renders
chartReg[id].customLegend = $('#legend');
pieSeries.dataItems.each(function(row, i) {
var color = pieSeries.colors.getIndex(i);
var percent = Math.round(row.values.value.percent * 100) / 100;
var value = row.value;
var title = row.category
legend.innerHTML += '<div class="legend-item" id="legend-item-' + i + '" onclick="toggleSlice(' + i + ');" onmouseover="hoverSlice(' + i + ');" onmouseout="blurSlice(' + i + ');"><div class="legend-marker" style="background: ' + color + '"></div><div class="legend-title">' + title + '</div><div class="legend-value">' + value + ' | ' + percent + '%</div></div>';
});
});
});
The custom legends work fine like bellow:
But if we have multiple pie charts that get rendered in the DOM at the same time, the legends don't show up!
❤❤ Thank you for reading my question. ❤❤
I found the answer. Insted of:
legend.innerHTML += '<div>...</div>';
We should use:
$('#legend_'+id).append('<div>...</div>');
that dynamically adds the legends to the related div ;)

Javascript not showing up total value in middle of Oracle APEX Donut chart

I am trying use below code (found from a forum) as JavaScript initialization code in Oracle APEX Donut chart to display total value in middle. But the result showing up only the Text "Total" in middle of the chart and does not show any numerical value. Can anyone help me out in spotting the error from the below code ? I am new to Javascript and have very less knowledge about the same.
function( options ){
var total;
this.pieSliceLabel = function(dataContext){
var series_name;
percent = Math.round(dataContext.value/dataContext.totalValue*100);
total = Math.round(dataContext.totalValue);
if ( dataContext.seriesData ) {
series_name = dataContext.seriesData.name;
} else {
series_name = 'Other';
}
document.getElementById("myDiv").innerHTML = Math.round(dataContext.totalValue);
return series_name + " " + percent + "% ( " + dataContext.value + " )";
}
// Set chart initialization options
options.dataLabel = pieSliceLabel;
this.centerCallback = function(dataContext){
var pieElem = document.createElement('div');
pieElem.innerHTML =
'<div id="myDiv" style="position:absolute;text-align:center;font-size:16px;">' +
'Total' +' '+ total +
'</div>';
var outerDiv = pieElem.children[0];
var innerBounds = dataContext.innerBounds;
// Outer bounds
outerDiv.style.top = innerBounds.y + "px";
outerDiv.style.left = innerBounds.x + "px";
outerDiv.style.height = innerBounds.height + "px";
outerDiv.style.width = innerBounds.width + "px";
outerDiv.style.lineHeight = innerBounds.height + "px";
return pieElem;
}
options.pieCenter = {
renderer : centerCallback
}
return options;
}
if I correct understood, try to fix it, add to centerCallback,
var value = dataContext.totalValue;
pieElem.innerHTML =
'<div id="myDiv" style="position:absolute;text-align:center;font-size:16px;">' +
'Total ' + value +
'</div>';
Could you try this
function( options ){
// will hold the total value, must be global variable
var total;
Add below statement in this.pieSliceLabel = function(dataContext){}
percent = Math.round(dataContext.value/dataContext.totalValue*100);
total = dataContext.totalValue; //assign to global variable
Update the below innerHTML code in this.centerCallback = function(dataContext){}
//updated element
pieElem.innerHTML =
'<div id="myDiv" style="position:absolute;text-align:center;font-size:16px;">' +
'Total' + total +
'</div>';

Creating and deleting divs using javascript

I have a few JavaScript functions designed to add and remove HTML divs to a larger div. The function init is the body's onload. New lines are added when an outside button calls NewLine(). Divs are removed when buttons inside said divs call DeleteLine(). There are a few problems with the code though: when I add a new line, the color values of all the other lines are cleared, and when deleting lines, the ids of the buttons, titles, and line boxes go out of sync. I've gone through it with the Chrome debugger a few times, but each time I fix something it seems to cause a new problem. I would greatly appreciate some input on what I'm doing wrong.
function init()
{
numOfLines = 0; //Keeps track of the number of lines the Artulator is displaying
}
function NewLine()
{
var LineBoxHolder = document.getElementById("LineBoxHolder");
numOfLines += 1;
LineBoxCode += "<div class = 'Line Box' id = 'LineBox" + numOfLines + "'>" //The code is only split onto multiple lines to look better
+ " <h6 id = 'Title " + numOfLines + "' class = 'Line Box Title'>Line " + numOfLines + "</h6>";
+ " <p>Color: <input type = 'color' value = '#000000'></p>"
+ " <input type = 'button' value = 'Delete Line' id = 'DeleteLine" + numOfLines + "' onclick = 'DeleteLine(" + numOfLines + ")'/>"
+ "</div>";
LineBoxHolder.innerHTML += LineBoxCode;
}
function DeleteLine(num)
{
deletedLineName = "LineBox" + num;
deletedLine = document.getElementById(deletedLineName);
deletedLine.parentNode.removeChild(deletedLine);
num++;
for ( ; num < numOfLines + 1 ; )
{
num++;
var newNum = num - 1;
var changedLineName = "LineBox" + num;
var changedHeaderName = "Title" + num;
var changedButtonName = "DeleteLine" + num;
var changedButtonOC = "DeleteLine(" + newNum + ")";
var changedLine = document.getElementById(changedLineName);
var changedHeader = document.getElementById(changedHeaderName);
var changedButton = document.getElementById(changedButtonName);
var changedLine.id = "LineBox" + newNum;
var changedHeader.innerHTML = "Line" + newNum;
var changedHeader.id = "Title" + newNum;
var changedButton.setAttribute("onclick",changedButtonOC);
var changedButton.id = "DeleteLine" + newNum;
}
num--;
numOfLines = num;
}
You are having a hard time debugging your code because of your approach. You are "marking" various elements with the IDs you construct, and using the IDs to find and address elements. That means that when things change, such as line being deleted, you have to go back and fix up the markings. Almost by definition, the complicated code you wrote to do something like that is going to have bugs. Even if you had great debugging skills, you'd spend some time working through those bugs.
Do not over-use IDs as a poor-man's way to identify DOM elements. Doing it that way requires constructing the ID when you create the element and constructing more IDs for the sub-elements. Then to find the element again, you have to construct another ID string and do getElementById. Instead, use JavaScript to manage the DOM. Instead of passing around IDs and parts of IDs like numbers, pass around the DOM elements themselves. In your case, you don't need IDs at all.
Let's start off with DeleteLine. Instead of passing it a number, pass it the element itself, which you can do my fixing the code inside your big DOM string to be as follows:
<input type='button' value='Delete Line' onclick="DeleteLine(this.parentNode)"/>
So we have no ID for the line element, no ID for the element, and no ID within the onclick handler. DeleteLine itself can now simply be
function DeleteLine(line) {
{
line.parentNode.removeChild(line);
renumberLines();
}
We'll show renumberLines later. There is no need to adjust IDs, rewrite existing elements, or anything else.
Since we no longer need the ID on each line or its sub-elements, the code to create each element becomes much simpler:
function NewLine()
{
var LineBoxHolder = document.getElementById("LineBoxHolder");
numOfLines += 1;
var LineBoxCode = "<div class='LineBox'>" +
+ " <h6 class='LineBoxTitle'>Line " + "numOfLines + "</h6>"
+ " <p>Color: <input type='color' value='#000000'></p>"
+ " <input type='button' value='Delete Line' onclick= 'DeleteLine(this.parentNode)'/>"
+ "</div>";
LineBoxHolder.innerHTML += LineBoxCode;
}
The only remaining work is to fix up the titles to show the correct numbers. You can do this by just looping through the lines, as in
function renumberLines() {
var LineBoxHolder = document.getElementById("LineBoxHolder");
var lines = LineBoxHolder.childElements;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
var h6 = line.querySelector('h6');
h6.textContent= "Line " + (i+1);
}
}
I voted to close because the question is too broad, but will answer anyway on a few points to... well, point in the right direction.
var changedButton.setAttribute("onclick",changedButtonOC); This is not a variable declaration. Omit the var.
for ( ; num < numOfLines + 1 ; ) { num++; ... The correct form here would be simply for (; num < numOfLines + 1; num++) { ....
Instead of incrementing (num++) then decrementing (num--) around the loop, why not just use the right math?
See:
for (; num < numOfLines; num++) {
...
}

how to change the text of node in Go.js

I am using go.js to making the graphs. Everything is ok, but now I want to edit the text like the color. For this i have made a textarea. And I have done this, but the issue is when i change the text for one node it changes the text for the other nodes I have selected previously. Don't know where I am wrong. Here is my code:
var info = document.getElementById("myInfo");
myDiagram.addDiagramListener("ChangedSelection", function(e1) {
var sel = e1.diagram.selection;
var str = "";
if (sel.count === 0) {
str = "Selecting nodes in the main Diagram will display information here.";
info.innerHTML = str;
return;
} else if (sel.count > 1) {
str = sel.count + " objects selected.";
info.innerHTML = str;
return;
}
// One object selected, display some information
var elem = sel.first();
var shape = elem.findObject("SHAPE");
var txtblock = elem.findObject("TEXT");
str += "<h3>Selected Node:</h3>";
str += "<p>Figure: " + shape.figure + "</p>";
str += "<p>Text: <textarea style='height:100px;' id='nodetext'> " + txtblock.text + "</textarea></p>";
var strokeColor = shape.stroke;
str += '<p style="float: left; margin-right: 10px;">Color: <input type="text" id="custom" /></p>';
info.innerHTML = str;
$(document).on('keyup','#nodetext',function(a)
{
a.preventDefault();
txtblock.text=$(this).val() ;
})
// Initialize color picker
$("#custom").spectrum({
color: strokeColor,
// Change colors by constructing a gradient
change: function(color) {
var c = color.toRgb();
var r, g, b;
var grad1 = new go.Brush(go.Brush.Linear);
r = Math.min(c.r + 10, 255);
g = Math.min(c.g + 10, 255);
b = Math.min(c.b + 10, 255);
grad1.addColorStop(0, "rgb(" + r + "," + g + "," + b + ")");
grad1.addColorStop(0.5, color.toRgbString());
r = Math.max(c.r - 30, 0);
g = Math.max(c.g - 30, 0);
b = Math.max(c.b - 30, 0);
grad1.addColorStop(1, "rgb(" + r + "," + g + "," + b + ")");
shape.fill = grad1;
shape.stroke = "rgb(" + r + "," + g + "," + b + ")";
txtblock.stroke = (r < 100 && g < 100 && b < 100) ? "white" : "black";
}
});
});
To be clear, your question is about modifying the colors of a Shape and a TextBlock, not trying to modify the TextBlock.text property.
The problem is that you are adding a "change" event handler for the Spectrum object each time the Diagram.selection collection changes. That event handler is a closure that holds a reference to the particular selected Node. As the selection changes you add a new event handler, but the old ones are still there and being called, modifying the previously selected nodes.
There are several possible solutions. I suggest that you define Spectrum's change event handler only once, not in the "ChangedSelection" DiagramEvent listener. Set it to be a function that operates on all of the selected Nodes in the Diagram, not on a particular Node. Or perhaps change the colors if there is only one Node that is selected, it that is the policy that you want.
By the way, unless your Links are not selectable, your code ought to handle the case when the user selects a Link.

Using HTML5 localstorage to update some text without document.write

I have a div with a class of 'card', which is also draggable() with jQuery. I'm using localstorage to store an array containing information about the card.
var cardID = 1;
var cardValues = new Array();
var name = "Shifting Sliver";
var cost = "3U";
var text = "Sliver cannot be blocked except by slivers.";
var power = "2";
var toughness = "2";
cardValues.push(name);
cardValues.push(cost);
cardValues.push(text);
cardValues.push(power);
cardValues.push(toughness);
localStorage.setItem(cardID, cardValues.join(";"));
var cardKey = localStorage.key(0);
var getCardValues = localStorage.getItem(cardKey);
values = getCardValues.split(";");
var name = values[0];
var cost = values[1];
var text = values[2];
var power = values[3];
var toughness = values[4];
function writeCard()
{
document.write('Card Name: ' + name + '<br />');
document.write('Card Cost: ' + cost + '<br />');
document.write('Card Text: ' + text + '<br />');
document.write('Card Power: ' + power + '<br />');
document.write('Card Toughness: ' + toughness);
}
In the html:
<div class="card">
<div class="info"><script type='text/javascript'> writeCard(); </script></div>
</div>
After dragging the card, when releasing the mouse, document.write causes the whole page to refresh and only the data in document.write is shown. How can I insert the data onto the card without using document.write?
I think I need to use DOM manipulation, but I'm a bit stuck on exactly how.
You can not use document.write when document is completed loading use innerHTML instead
ypu can also use jQuery .append

Categories