I want to allow the user to select the difficulty of the game. Easy is words length 3-5, Medium is length 6-9, Hard is 10-15. The word is being pulled from an AJAX API. Also, I've consulted this example but am still struggling to apply it to my own. I included extra code because I'm not sure if I could add radio buttons that store the level and word.length that then updates the word retrieved from the api to be the correct length.
var api = 'https://hangman-api.lively.software';
//add alphabet to div
alphabet.forEach((i) => {
$('.hangman-letters').append(`<div id="letter-${i}">${i}</div>`);
});
//set up initial puzzle
newPuzzle();
//set up new puzzle when user clicks "start over"
$('#game-over-replay').click(function(){
newPuzzle();
});
//trigger game on clicking letter button
$('.hangman-letters div').click(function(){
submitLetter(this.innerHTML, this);
});
//trigger game on keypress
$(window).keypress(function(e){
var thisKey = String.fromCharCode(e.which);
if(alpha.indexOf(thisKey) > -1) {
submitLetter(thisKey, document.getElementById(`letter-${thisKey}`));
}
})
//functions
//handle clicked letter or keyboard input
function submitLetter(letter, thisLetterButton) {
var isCorrect = letterChosen(letter);
if(isCorrect) $(thisLetterButton).addClass('disabled correct');
else $(thisLetterButton).addClass('disabled');
if(remainingBlanks < 1) {
gameOver(true);
}
if(totalIncorrect >= hangmanParts.length + 1) {
gameOver(false);
}
}
//wipe variables and containers and set up new game
//now called after api call is complete
function setUp(){
$('.game-over').hide();
puzzleLetterContainers = [];
previouslyChosen = '';
totalIncorrect = 0;
remainingBlanks = puzzle.length;
$('.hangman-puzzle').html('');
$('#added-parts').html('');
$('.hangman-letters div').each(function(){
this.classList = '';
})
puzzle.split('').forEach((i) => {
var thisClass = "hangman-puzzle-letters",
placeholder = " ";
if(i == ' ' || i == '-') {
thisClass += ' space';
remainingBlanks--;
placeholder = i;
}
$('.hangman-puzzle').append(`<div class="${thisClass}">${placeholder}</div>`);
});
//var difficulty[] = new difficulty;
//var difficulty[1] = Easy;
//var difficulty[2] = Medium;
//var difficulty[3] = Hard;
puzzleLetterContainers = document.getElementsByClassName('hangman-puzzle-letters');
}
I'm new in Google Chart API.
I want to use it to visualize my data about the review on Google Play Store which include multiple issues, sentiments and other conditions.
I want to build a horizontal bar chart which x axis containing different app , and each app's containing 7 issues, y axis is the sum of sentiment of each issue in different app.
I have already done the horizontal chart containing all data in a single div element. However, for the user's convenience, I want to show 5 data at most in a single div element, and dynamically create div element if there is more than 5 data in the current data set. At last, the div elements which paint the chart will horizontally append to the another div element called issueBar_div. The user can use the scrollbar to view different chart.
What I've done:
Partition data:
var title = ['APP'];
var issueRow = {{ projectissues|safe }}; // 7 different issues got from Django
var graph_data = [title.concat(issueRow)];
var cnt = 0;
var divide_row = [];
var tableRows = ... // tableRows is the app name mapping to sentiment sum which corresponding to different issue array dictionary.
// like the form APP -> [20, 12, -1, 3, 5, 21, 57]
for (var app in tableRows) {
cnt ++;
var row = [app];
for (var i = 0; i < tableRows[app].length; i++) {
row.push(tableRows[app][i]);
}
if(cnt < 6){
divide_row.push(row);
}
else{
graph_data.push(divide_row);
divide_row = [];
cnt = 0;
}
}
Create the div element and draw the chart
In order to build a use the scrollbar, I add some restriction to the issueBar_div.
<div id="issueBar_div" style="height:400px; overflow-x:scroll; overflow-y:hidden";></div>
Dynamically create the div element and draw.
function drawIssueBar() {
var issueBar = document.getElementById("issueBar_div");
// titleRow include the APP and other issue
var titleRow = graph_data[0];
delete_element();
for(var i = 1;i<graph_data.length;i++){
// create div element and set their attribute
var my_div = document.createElement("div");
my_div.id = "issuebar_" + i;
my_div.style.display = "table-cell";
// append this element to #issueBar_div
issueBar.appendChild(my_div);
// get the sliced data and push to total_data
var row_data = graph_data[i];
var total_data = [titleRow];
for(var k=0;k<row_data.length;k++){
total_data.push(row_data[k]);
}
// the new data container
var data = new google.visualization.arrayToDataTable(total_data);
var div_width = $("#home").width();
var materialOptions = {
height: 400,
width: div_width,
hAxis: {
title: 'Sum of sentiment'
},
vAxis: {
title: 'APP'
},
bars: 'vertical'
};
var materialChart = new google.charts.Bar(document.getElementById("issuebar_" + i));
materialChart.draw(data, materialOptions);
}
}
// delete the div element
function delete_element(){
i = 1;
while(true){
element = document.getElementById("issuebar_" + i);
if(element !== null){
element.outerHTML = "";
delete element;
i++;
}
else{
break;
}
}
}
Current Problem:
Basically, the created div elements will create as expect, but the chart will only show one chart as below.
The successful chart
The fail part when move the scrollbar to right
How can I solve this problem?
Any answer will be greatly appreciated. Thanks.
ALL. I have already solve my problem!!!
I notice that there is a very important sentence showing on Google Chart document:
For each chart on the page, add a call to google.charts.setOnLoadCallback() with the callback that draws the chart as an input
So I decide to callback drawing method when drawing each bar chart.
The specific solution
1.I use the new Function as a callback function. The row_data is the data needing to present, and the my_div is the div element where to store the chart.
var drawIssueBar = new Function("row_data", "my_div",
"var data = google.visualization.arrayToDataTable(row_data);" +
"var div_width = $('#home').width();" +
"var materialOptions = {" +
"height: 400," +
"width: div_width," +
"hAxis: {" +
"title: 'Sum of sentiment'" +
"}," +
"vAxis: {" +
"title: 'APP'" +
"}," +
"bars: 'vertical'" +
"};"+
"var materialChart = new google.charts.Bar(my_div);" +
"materialChart.draw(data, materialOptions);"
);
2.Rewrite the data processing.
var titleRow = ... // like the form of ["APP", "issue1", "issue2", ..."issue7"]
var cnt = 0;
var divide_row = [];
for (var app in tableRows) {
cnt ++;
var row = [app].concat(tableRows[app]);
if(cnt < 6){
divide_row.push(row);
}
else{
graph_data.push([titleRow].concat(divide_row));
// console.log(graph_data[graph_data.length - 1]);
divide_row = [];
cnt = 0;
}
}
3.Write a new function called drawIssueChart to draw all the bar chart.
function drawIssueChart(){
// create the div element and draw the chart
delete_element();
var issueBar = document.getElementById('issueBar_div');
for(var i = 0;i<graph_data.length;i++){
var my_div = document.createElement("div");
my_div.id = "issuebar_" + i;
my_div.style.display = "table-cell";
issueBar.appendChild(my_div);
var row_data = graph_data[i];
// use the drawIssueBar as a callback
google.charts.setOnLoadCallback(drawIssueBar(row_data, my_div));
}
}
Result
The successful chart
Another successful chart when moving the scrollbar to right
I learn a lot from this solution!
Hopefully it can help the people who encounter this problem.
I have been working for the last months with dygraphs. It is a incredible library and I have got great results but I´m having some problems to find the way of interpolating data from different signals to be shown in the same chart.
The data I received from different sensors have not the same timestamp for the different samples, so for the most of the points of the x axe timestamps I have only the value of one signal. The chart is plotted perfectly, but I would like to see the interpolated value of the rest of the signals in that x point I am pointing over. Below I have the chart I get.
Reading on the dygraph documentation I have seen that when you have independent series, it is possible to see at least the value "undefined" for the signals without data in that point of the x axe.
The csv I use to plot the data is shown below. It has the same structure indicated in the dygraph documentation but I don´t get this undefined label neither.
TIME,LH_Fuel_Qty,L_Left_Sensor_NP
1488801288048,,1.4411650490795007
1488801288064,0.478965502446834,
1488801288133,,0.6372882768113235
1488801288139,1.131315227899919,
1488801288190,1.847605177130475,
1488801288207,,0.49655791428536067
1488801288258,0.45488168748987334,
1488801288288,,1.3756073145270766
1488801288322,0.5636921255908185,
1488801288358,,1.1193344122758362
Thanks in advance.
This is an approach that does not add any data to your csv data and still provides interpolated values for all the columns as you move your mouse around. It adds a listener to the mousemove event within dygraph and interpolates the closest points for all of the data. At the moment I have simply shown it in an extra DIV that is after the graph but you can display it however you like:
function findNextValueIndex(data, column, start) {
var rows = data.length;
for (var i = start; i < rows; i++) {
if (data[i][column] != null) return (i);
}
return (-1);
}
function findPrevValueIndex(data, column, start) {
for (var i = start; i >= 0; i--) {
if (data[i][column] != null) return (i);
}
return (-1);
}
function interpolate(t0, t1, tmid, v0, v1) {
return (v0 + (tmid - t0) / (t1 - t0) * (v1 - v0));
}
function showValues(headers, colors, vals) {
var el = document.getElementById("info");
var str = "";
for (j = 1; j < headers.length; j++) {
str += '<p style="color:' + colors[j] + '";>' + headers[j] + ": " + vals[j] + "</p>";
}
el.innerHTML = str;
document.getElementById("hiddenDiv").style.display = "none";
}
function movecustom(event, dygraph, point) {
var time = dygraph.lastx_;
var row = dygraph.lastRow_;
var vals = [];
var headers = [];
var colors = [];
var cols = dygraph.rawData_[0].length;
// draw a line on the chart showing the selected location
var canvas = dygraph.canvas_;
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(0,200,200,0.1)";
ctx.moveTo( dygraph.selPoints_[0].canvasx, 0);
ctx.lineTo( dygraph.selPoints_[0].canvasx, 1000);
ctx.stroke();
for (var j = 1; j < cols; j++) {
colors[j] = dygraph.colors_[j - 1];
if (dygraph.rawData_[row][j] == null) {
var prev = findPrevValueIndex(dygraph.rawData_, j, row - 1);
var next = findNextValueIndex(dygraph.rawData_, j, row + 1);
if (prev < 0)
vals[j] = dygraph.rawData_[next][j];
else if (next < 0)
vals[j] = dygraph.rawData_[prev][j];
else {
vals[j] = interpolate(dygraph.rawData_[prev][0], dygraph.rawData_[next][0], time, dygraph.rawData_[prev][j], dygraph.rawData_[next][j]);
}
} else {
vals[j] = dygraph.rawData_[row][j];
}
}
headers = Object.keys(dygraph.setIndexByName_);
showValues(headers, colors, vals);
}
window.onload = function() {
new Dygraph(
document.getElementById('graph'), document.getElementById('csvdata').innerHTML, {
connectSeparatedPoints: true,
drawPoints: true,
labelsDiv: "hiddenDiv",
interactionModel: {
'mousemove': movecustom
}
}
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/dygraph/2.0.0/dygraph.js"></script>
<div id="graph" style="height:120px;"></div>
<div id="info"></div>
<div id="hiddenDiv" style="display:none"></div>
<pre id="csvdata" style="display:none">
TIME,LH_Fuel_Qty,L_Left_Sensor_NP
1488801288048,,1.4411650490795007
1488801288064,0.478965502446834,
1488801288133,,0.6372882768113235
1488801288139,1.131315227899919,
1488801288190,1.847605177130475,
1488801288207,,0.49655791428536067
1488801288258,0.45488168748987334,
1488801288288,,1.3756073145270766
1488801288322,0.5636921255908185,
1488801288358,,1.1193344122758362
</pre>
It seems that the best way to do this is to massage the data before submitting it to the dygraph call. This means the following steps:
1) parse the csv file into an array of arrays.
2) go through each line of the array to find where the holes are
3) interpolate to fill those holes
4) modify the constructed arrays to be displayed by dygraph
5) call dygraph
Not the most attractive code, but seems to work...
function findNextValueIndex(data, column, start) {
var rows = data.length;
for(var i=start;i<rows;i++) {
if(data[i][column].length>0) return(i);
}
return(-1);
}
function interpolate(t0, t1, tmid, v0, v1) {
return((v0 + (tmid-t0)/(t1-t0) * (v1-v0)).toString());
}
function parseCSV(string) {
var data = [];
// first get the number of lines:
var lines = string.split('\n');
// now split the first line to retrieve the headings
var headings = lines[0].split(",");
var cols = headings.length;
// now get the data
var rows=0;
for(var i=1;i<lines.length;i++) {
if(lines[i].length>0) {
data[rows] = lines[i].split(",");
rows++;
}
}
// now, fill in the blanks - start by finding the first value for each column of data
var vals = [];
var times = [];
for(var j=1;j<cols;j++) {
var index = findNextValueIndex(data,j,0);
vals[j] = parseFloat(data[index][j]);
}
// now put those start values at the beginning of the array
// there is no way to calculate the previous value of the sensor missing from the first sample
// so we use the first reading and duplicate it
for(var j=1;j<cols;j++) {
data[0][j] = vals[j].toString();
times[j] = parseInt(data[0][0]);
}
// now step through the rows and interpolate the missing values
for(var i=1;i<rows;i++) {
for(var j=1;j<cols;j++) {
if(data[i][j].length>0) {
vals[j] = parseFloat(data[i][j]);
times[j] = parseInt(data[i][0]);
}
else {
var index = findNextValueIndex(data,j,i);
if(index<0) // no more data in this column
data[i][j] = vals[j].toString();
else
data[i][j] = interpolate(times[j],parseInt(data[index][0]),parseInt(data[i][0]),vals[j],data[index][j]);
}
}
}
// now convert from strings to integers and floats so dygraph can handle it
// I've also changed the time value so that it is relative to the first element
// it will be shown in milliseconds
var time0 = parseInt(data[0][0]);
for(var i=0;i<rows;i++) {
data[i][0] = parseInt(data[i][0]) - time0;
for(var j=1;j<cols;j++) {
data[i][j] = parseFloat(data[i][j]);
}
}
var obj = {
labels: headings,
data: data
}
return(obj);
}
window.onload = function () {
var data_obj = parseCSV(document.getElementById('csvdata').innerHTML);
new Dygraph(
document.getElementById('graph'), data_obj.data,
{
labels: data_obj.labels,
connectSeparatedPoints: true,
drawPoints: true
}
);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dygraph/2.0.0/dygraph.js"></script>
<div id="graph" style="height:200px;"></div>
<pre id="csvdata" style="display:none">
TIME,LH_Fuel_Qty,L_Left_Sensor_NP
1488801288048,,1.4411650490795007
1488801288064,0.478965502446834,
1488801288133,,0.6372882768113235
1488801288139,1.131315227899919,
1488801288190,1.847605177130475,
1488801288207,,0.49655791428536067
1488801288258,0.45488168748987334,
1488801288288,,1.3756073145270766
1488801288322,0.5636921255908185,
1488801288358,,1.1193344122758362
</pre>
Does
connectSeparatedPoints: true
Not do what you need?
Sorry but I'm completly new to js and jquery.
I got dynamic table which values are in localstorage. I can add new row, delete row, and edit cells. This is working.
I want to add a sorting this table by clicked colum. I found here code and try it. It just working when I write table and not use my javascript to add rows from localstorage. Table in two cases looks same in html code. I have no idead why sorting isnt working with dynamic table.
This is w/o my dynamic table from localstore, sorting as supposed to:
http://jsfiddle.net/eW8Kg/1/
This is with table from localstorage(not working in jsfiddle?) on my comupter this is working good, but table is not sorting! (I left this static values):
http://jsfiddle.net/XAu5G/
I think my problem can be in creation of table content:
var Html = "<tbody>";
for (var i = 1; i < localStorage.length; i++) {
var input = "<td><input style='border:hidden' class=\"fields\" name = " + localStorage.key(i) + " type='text' onchange='change(\"" + localStorage.key(i) + "\")' /></td>"
Html += "<tr class=\"field\">";
for (var j = 0; j < 4; ++j) {
Html += input;
}
var button = "<td><input type='button' value = 'Usuń' onclick='Remove(\"" + localStorage.key(i) + "\")'></td>";
Html += button + "</tr>";
}
Html += "<tr id=\"actions\"></tr></tbody>"
document.getElementById("list").innerHTML += Html;
This jquery code do sorting:
$(document).ready(function() {
var table = $('#list');
jQuery.fn.sortElements = (function() {
var sort = [].sort;
return function(comparator, getSortable) {
getSortable = getSortable || function() {return this;};
var placements = this.map(function() {
var sortElement = getSortable.call(this),
parentNode = sortElement.parentNode,
// Since the element itself will change position, we have
// to have some way of storing it's original position in
// the DOM. The easiest way is to have a 'flag' node:
nextSibling = parentNode.insertBefore(
document.createTextNode(''),
sortElement.nextSibling
);
return function() {
if (parentNode === this) {
throw new Error(
"You can't sort elements if any one is a descendant of another."
);
}
// Insert before flag:
parentNode.insertBefore(this, nextSibling);
// Remove flag:
parentNode.removeChild(nextSibling);
};
});
return sort.call(this, comparator).each(function(i) {
placements[i].call(getSortable.call(this));
});
};
})();
$('#x').click(function(){
$('#list').hide();
});
$('#nazwa-header').wrapInner('<span title="sort this column"/>').each(function() {
var th = $(this),
thIndex = th.index(),
inverse = false;
th.on('click', function() {
table.find('td').filter(function() {
return $(this).index() === thIndex;
}).sortElements(function(a, b) {
console.log($(a).find('input').val(),$(b).find('input').val());
return $(a).find('input').val() > $(b).find('input').val() ?
inverse ? -1 : 1
: inverse ? 1 : -1;
}, function() {
// parentNode is the element we want to move
return this.parentNode;
});
inverse = !inverse;
});
});
});
I need to rewrite this code in pure JavaScript, i.e. without jQuery. It gets the content of a div and adds it after the first image in another div.
$(document).ready(function() {
var teksti = $('#inside1').html();
$('<div id="inside1">' + teksti + '</div><div style="clear:both;"></div>').insertAfter("#artikull > p > img:first");
});
If you only need to support modern browsers, it's not too complicated:
// $(document).ready(function() {
document.addEventListener("DOMContentLoaded", function() {
// var teksti = $('#inside1').html();
var teksti = document.getElementById('inside1').innerHTML;
// $('<div id="inside1">' + teksti + '</div><div style="clear:both;"></div>')
// .insertAfter("#artikull > p > img:first");
document
.querySelector('#artikull > p > img')
.insertAdjacentHTML(
'afterend',
'<div id="inside1">' + teksti + '</div><div style="clear:both">'
);
// });
});
The roughly equivalent lines from the original jQuery are in the comments.
I'm a little confused, though; the code you've presented will create an element with the same ID as the original one and ends up with a couple of divs in a p—resulting in a somewhat deformed DOM. Wouldn't you prefer to simply wrap the existing element and move it instead of creating a new one with exactly the same content, and shouldn't you move it somewhere that accepts block-level children?
function adddiv(){
var teksti = document.getElementById('inside1').innerHTML;
var div = document.getElementById('artikull');
var imazh = div.getElementsByTagName("img")[0];
imazh.outerHTML = imazh.outerHTML + teksti;
}
The code below should be compatible in modern browsers and IE from version 9.
document.addEventListener("DOMContentLoaded", function (e) {
var insideHTML = '<div id="inside1">' + window.document.getElementById('inside1').innerHTML + '</div><div style="clear:both;"></div>',
artikullElement = window.document.getElementById('artikull'),
pElements = (function (nodes) {
var results = [],
node;
for (var i = 0, iLen = nodes.length; i < iLen; i++) {
node = nodes[i];
// Get all children P tags
if (node.nodeValue === 1 && node.tagName === "P")
results.push(node);
}
return results;
})(artikullElement.childNodes),
imgElement = (function (nodes) {
var node;
for (var i = 0, iLen = nodes.length; i < iLen; i++) {
// Get the first child tag image found in any of the P tags
if (node.nodeValue === 1 && node.tagName === "IMG")
return node;
}
})(pElements);
if (imgElement) {
if (imgElement.insertAdjacentHTML)
imgElement.insertAdjacentHTML('beforeEnd', insideHTML);
else {
var range = window.document.createRange(),
docFragment = range.createContextualFragment(insideHTML);
imgElement.parentNode.insertBefore(docFragment, imgElement.nextSibling);
}
}
});