Chart.js - Cannot read property 'getContext' of null - javascript

I have the following Javascript in my main.js file:
//array object of API stuff
function createChartWinLoss(wins, losses) {
var pieData = [
{
value: losses,
color: "#F7464A",
highlight: "#FF5A5E",
label: "Losses"
},
{
value: wins,
color: "#46BFBD",
highlight: "#5AD3D1",
label: "Wins"
}
];
var pieOptions = {
segmentShowStroke : false,
animateScale : true
}
var winLossChart = document.getElementById('winLossChart').getContext('2d');
new Chart(winLossChart).Pie(pieData, pieOptions);
}
//creates the chart with test data
createChartWinLoss();
function summonerLookUp() {
var SUMMONER_ID = "";
var API_KEY = "keyhere";
var sumID = $("#theKey").val();
var div = document.getElementById('stuff');
var combine = "";
var array = [sumID];
var wins;
var losses;
div.innerHTML = div.innerHTML + "<br />array count: " + array.length + "<br />";
for (var i = 0; i < array.length; i++) {
combine = "";
SUMMONER_ID = array[i];
getStuff(SUMMONER_ID, combine, API_KEY, div, i);
}
}
function getStuff(SUMMONER_ID, combine, API_KEY, div, count) {
var Topuser = SUMMONER_ID;
$.ajax({
url: 'https://euw.api.pvp.net/api/lol/euw/v2.5/league/by-summoner/' + SUMMONER_ID + '/entry?api_key=' + API_KEY,
type: 'GET',
dataType: 'json',
async: false,
data: {},
success: function (json) {
var user = Topuser;
if (typeof json[user][0].queue != "undefined") {
if (json[user][0].queue == "RANKED_SOLO_5x5") {
combine = json[user][0].tier + " " + json[user][0].entries[0].division + " - " + json[user][0].entries[0].wins + " Wins " + json[user][0].entries[0].losses + " losses";
div.innerHTML = div.innerHTML + "<br />" + count + ": " + user + " " + combine;
var wins = json[user][0].entries[0].wins;
var losses = json[user][0].entries[0].losses;
//populates chart with wins losses from api
createChartWinLoss(wins,losses);
}
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
var user = Topuser;
console.log(errorThrown);
if (errorThrown === "Not Found") {
div.innerHTML = div.innerHTML + "<br />" + count + ": " + user + " " + "UNRANKED";
}
}
});
}
And the HTML is as follows:
<div class="container">
<h2>Wins/Losses</h2>
<canvas id="winLossChart" width="400" height="200"></canvas>
</div>
As the title suggests, I am getting Uncaught TypeError: Cannot read property 'getContext' of null and I'm not entirely sure what the issue is. If I had to guess I'd say it was trying to reference something that wasn't there but I'm not 100% sure on if I am correct and how to fix it. Any advice would be great.

The line that is throwing the error is
var winLossChart = document.getElementById('winLossChart').getContext('2d');
It is saying that document.getElementById('winLossChart') does not exist.
This would be because your script is being interpreted before the elements have finished being created in the DOM.
You could either kick off the script in a window.onload function:
window.onload = function() {
createChartWinLoss();
}
Or you could put the script tag itself as the last element in the body element of your html file.
<body>
<div class="container">
<h2>Wins/Losses</h2>
<canvas id="winLossChart" width="400" height="200"></canvas>
</div>
<script src="myscript.js"></script>
</body>
Either solution would mean that the main entry point of your code (createChartWinLoss) would only happen after the other elements on the page, including the canvas, were created.
As a general process towards solving these kinds of problems, when you saw the exception in your Javascript console, you should have been able to open the stack trace, which would have led you to the fact that the error originated on the line var winLossChart = ..., which would have made you more likely to have been able to discover the source of the problem.

I was having this same problem. The element was being returned as dispHTMLUnkownElement.
The solution was to add <!DOCTYPE html>to the top of my response and then IE picked up the element type properly.

Perhaps this can help someone else...
You have to use destroy() method.
To made that you have to change a few things in your code:
var winLossChart = "";// Here you make your chart var global
function createChartWinLoss(wins, losses) {
var pieData = [{
value: losses,
color: "#F7464A",
highlight: "#FF5A5E",
label: "Losses"
}, {
value: wins,
color: "#46BFBD",
highlight: "#5AD3D1",
label: "Wins"
}];
var pieOptions = {
segmentShowStroke: false,
animateScale: true
}
//HereĀ“s the change inside the function where you run destroy().
if(typeof winLossChart.destroy != "undefined") winLossChart.destroy();
winLossChart = document.getElementById('winLossChart').getContext('2d');
new Chart(winLossChart).Pie(pieData, pieOptions);
}
//creates the chart with test data...
https://github.com/chartjs/Chart.js/issues/3231

Related

External resources not reaching inside of function scope

I'm trying to make an inventory picker for my trading website, but I'm having some trouble calling external resources.
When originally calling the external function, all goes correctly, but whenever I try to call the external function INSIDE of a function in the html, it errors, saying
Uncaught TypeError: $(...).imagepicker is not a function
This is my relevant code.
<script src="~/Scripts/image-picker.js"></script>
<script>
var name = "HomeguardDev";
var inventory = [];
var selectedItems = [];
$.ajax({
async: false,
type: 'GET',
url: "/Home/getInventory/?username=" + name + "&page=1",
success: function (data) {
var parsed = JSON.parse(data);
for (var i = 0; i < parsed.length; i++) {
var text = parsed[i].Name;
inventory[i] = parsed[i];
if (parsed[i].SerialNumber !== "---") {
text = text + " [#" + parsed[i].SerialNumber + " / " + parsed[i].SerialNumberTotal + "]";
}
$("#sendingItems").append('<option data-img-label="<small>' + text + '</small>" data-img-src="' + parsed[i].ImageLink + '" value="' + i + '">' + text + '</option>');
}
}
});
$("#sendingItems").imagepicker({
show_label: true
});
function addItem() {
if (selectedItems.length < 4) {
var obj = (inventory[$("#sendingItems").val()]);
if (!containsObject(obj, selectedItems)) {
$('#sendingItems option[value="' + ($("#sendingItems").val()) + '"]').remove();
selectedItems.push(obj);
$("#sendingItems").imagepicker({
show_label: true
});
}
}
}
</script>
<p><a><input type="button" id="addItemButton" onclick="addItem()" value="Add item" /></a></p>
<p><a><input type="button" id="sendTradeButton" onclick="sendTrade()" value="Send Trade" /></a></p>
The error occurs inside of the function addItem() when calling imagepicker, but in the main block it calls and functions correctly. What am I doing incorrectly?

JS Objects and Functions Corrections

I've got a homework assignment to correct some Javascript code that is preventing functions from being executed. I've corrected everything I can find, even used JSFiddle and JSHint to make sure. So there's no syntax errors left, but there must be a logic error because nothing is working. I'd appreciate a quick look at my code, I'm sure it's a minor issue that I'm just overlooking. Thanks!
(There's bound to be some messy code here with how many things I've tried to change around, apologies for that).
First the very basic HTML:
<body>
<p id="message"></p>
<p id="movies"></p>
<p id="object"></p>
</body>
Then the JS:
// Define variables
var output = document.getElementById("message");
var BR = "<br>";
var makeBreakfast;
var mood;
var energyLevel;
var doWork;
var powerOn;
var command;
var duration;
var quitWork;
// Define robot object
var robot = {
material: "titanium",
mood: "happy",
energyLevel: 100,
powerOn: false,
command: "Sweeping the floor",
duration: 10,
favoriteMovies: ["2001: A Space Odyssey", "The Terminator", "I, Robot", "WALL-E", "Short Circuit", "Forbidden Planet"]
};
makeBreakfast = function (newMood) {
mood = newMood;
if (mood === "Happy") {
output.innerHTML += "Here are your waffles and milk, master." + BR;
} else {
output.innerHTML += "Here is your burnt toast and lukewarm water, master." + BR;
energyLevel = 50;
}
};
doWork = function () {
if (!powerOn) {
powerOn = true;
output.innerHtml += "My current task is: " + command + ". Duration: " + duration + " minutes." + BR;
}
};
quitWork = function () {
if (powerOn) {
energyLevel = 90;
powerOn = false;
command = "Taking a nap";
}
};
// Make robot do housework
doWork();
quitWork();
The full assignment is as follows:
1) Correct the code, and make doWork and quitWork execute properly.
2) Call the makeBreakfast argument and use the string "mad" as the argument.
3) Add a new method and call it to display a string (left this part out, not helpful right now)
4) List all of the robots favorite movies, each on a new line.
5) Use named indexing syntax to add a new property called language to the robot object. Initialize it with the language of your choice.
6) Using named indexing syntax, loop through the object and list each of the property and value pairs on a new line. Display the results in a new paragraph.
I really only wanted help with #1, but if seeing the full assignment helps, this is it. Thanks everyone!
Need to make it an object, and tell the poor schmuck to make you breakfast!
(Full Difference)
// Define variables
var output = document.getElementById("message");
var movies = document.getElementById("movies");
// Define variables
var output = document.getElementById("message");
var movies = document.getElementById("movies");
var object = document.getElementById("object");
var BR = "<br>";
// Define robot object
var robot = {
material: "titanium",
mood: "Happy",
energyLevel: 100,
powerOn: false,
command: "Sweeping the floor",
duration: 10,
favoriteMovies: ["2001: A Space Odyssey", "The Terminator", "I, Robot", "WALL-E", "Short Circuit", "Forbidden Planet"],
makeBreakfast: function (newMood) {
if(newMood) this.mood = newMood;
this.command = "Making breakfast";
this.doWork();
if (this.mood === "Happy") {
output.innerHTML += "Here are your waffles and milk, master." + BR;
} else {
output.innerHTML += "Here is your burnt toast and lukewarm water, master." + BR;
this.energyLevel = 50;
}
},
doWork: function () {
if(!this.powerOn) {
this.powerOn = true;
output.innerHTML += "My current task is: " + this.command + ". Duration: " + this.duration + " minutes." + BR;
}
},
quitWork: function () {
if (this.powerOn) {
this.energyLevel = 90;
this.powerOn = false;
this.command = "Taking a nap";
output.innerHTML += "My current task is: " + this.command + ". Duration: " + this.duration + " minutes." + BR;
}
}
};
// Make robot do housework
robot.doWork();
robot.quitWork();
robot.makeBreakfast();
robot.quitWork();
robot.makeBreakfast("Mad");
robot.quitWork();
<body>
<p id="message"></p>
<p id="movies"></p>
<p id="object"></p>
</body>

Can't scrobble tracks (batch) to Last.fm API. Invalid method signature

I've been trying this for a while now, but I can't get my head around what's wrong. Maybe I've tried so many ways that I'm not even sure this piece of code is right anymore.
Basically I'm trying to use the track.scrobble method from the Last.fm API, sending a batch of tracks.
That's the code I have, and it's always returning Invalid method signature. Does anyone can give me some help here, please?
UPDATE
Based on mccannf answer, I've changed the code, but am still getting the error:
var apiUrl = "http://ws.audioscrobbler.com/2.0/";
var apiMethod = "track.scrobble";
var apiKey = "MY_API_KEY";
var apiSecret = "MY_API_SECRET";
var key = "MY_SESSION_KEY";
var apiSig = "";
var lastfmScrobble = function (data) {
var dataToScrobble = setTiming(data);
var albums = [];
var artists = [];;
var timestamps = [];
var tracks = [];
var dataToHash = "";
for (var i = 0; i < dataToScrobble.tracks.length; i++) {
albums["album["+ i.toString() + "]"] = dataToScrobble.album;
artists["artist[" + i.toString() + "]"] = dataToScrobble.artist;
timestamps["timestamp[" + i.toString() + "]"] = dataToScrobble.tracks[i].split("|")[1];
tracks["track[" + i.toString() + "]"] = dataToScrobble.tracks[i].split("|")[0];
}
dataToHash += albums.sort().join("");
dataToHash += "api_key" + apiKey;
dataToHash += artists.sort().join("");
dataToHash += "method" + apiMethod;
dataToHash += "sk" + key;
dataToHash += timestamps.sort().join("");
dataToHash += tracks.sort().join("");
dataToHash += apiSecret;
apiSig = $.md5(unescape(encodeURIComponent(dataToHash)));
var songsToScrobble = {};
$.extend(songsToScrobble,
albums.sort(),
{ api_key: apiKey },
{ api_sig: apiSig },
artists.sort(),
{ method: apiMethod },
{ sk: key },
timestamps.sort(),
tracks.sort());
$.ajax({
url: apiUrl,
type: "POST",
data: songsToScrobble,
success: function (data) {
console.log(data);
}
});
}
Now the object sent has the correct format (JSON). What can still be wrong?
I did a quick sample JS Fiddle of your code.
The dataToHash is like this:
album[0]Achtung Babyalbum[1]Achtung Babyapi_keyxxxBLAHxxxartist[0]U2artist[1]U2methodtrack.scrobbleskkkkFOOkkktimestamp[0]1379368800timestamp[1]1379369000track[0]Onetrack[1]The FlymmmySecrettt
The songsToScrobble variable in the code above looked like this:
{ "album": [
"album[0]Achtung Baby",
"album[1]Achtung Baby"
],
"api_key":"xxxBLAHxxx",
"api_sig":"8dbc147e533411a41ba9169f59e65b3a",
"artist":["artist[0]U2","artist[1]U2"],
"method": "track.scrobble",
"sk":"kkkFOOkkk"
"timestamp": [
"timestamp[0]1379368800",
"timestamp[1]1379369000"
],
"track": [
"track[0]One",
"track[1]The Fly"
]
}
I believe songsToScrobble should look like this:
{ "album[0]": "Achtung Baby",
"album[1]": "Achtung Baby",
"api_key":"xxxBLAHxxx",
"api_sig":"8dbc147e533411a41ba9169f59e65b3a",
"artist[0]": "U2",
"artist[1]": "U2",
"method": "track.scrobble",
"sk":"kkkFOOkkk"
"timestamp[0]": "1379368800",
"timestamp[1]": "1379369000",
"track[0]": "One",
"track[1]": "The Fly"
}
Only other minor point is to make sure dataToHash is UTF-8 encoded before you convert to MD5 hash.
Edit
This is how I created the data for the ajax call. NOTE: this is untested - I don't have a last.fm account.
var songsToScrobble = {};
function addDataToScrobble(parentElement, inputData) {
if ($.isArray(inputData)) {
$.each(inputData, function(index ,element) {
songsToScrobble[parentElement + "[" + index + "]"] = element;
dataToHash += parentElement + "[" + index + "]" + element;
});
} else {
songsToScrobble[parentElement] = inputData;
dataToHash += parentElement + inputData;
}
}
for (var i = 0; i < data.tracks.length; i++) {
albums.push(data.album);
artists.push(data.artist);
// The tracks are coming in the format: title|timestamp
timestamps.push(data.tracks[i].split("|")[1]);
tracks.push(data.tracks[i].split("|")[0]);
}
addDataToScrobble("album", albums);
addDataToScrobble("api_key", apiKey);
addDataToScrobble("artist", artists);
addDataToScrobble("method", apiMethod);
addDataToScrobble("sk", key);
addDataToScrobble("timestamp", timestamps);
addDataToScrobble("track", tracks);
apiSig = $.md5(unescape(encodeURIComponent(dataToHash+apiSecret)));
songsToScrobble["api_sig"] = apiSig;
$.ajax({
url: apiUrl,
type: "POST",
data: songsToScrobble,
success: function (data) {
console.log(data);
}
});

How can I compare these strings in jQuery?

This program right now reads in xml code, gets a stock abbreviation, alphabetically sorts them, and then prints them out in an uo list. If you hover over the abbreviations the color will change to red. The goal I'm having is when you hover over an abbreviation, it will show all the data from the xml data just for that company. I tried using the if statement saying if the symbol (abbreviation in xml file) is equivalent to the name (abbreviation in array) then it prints out all the junk for it. The line that prints everything out works correctly in the format I want. I just need to work on the if statement.
What I have figured out is I cannot compare two variables with the ==. Keep in mind symbol is an attribute as well, and name is from an array that stores the symbols. I also tried just saying - if(checkPassword(name, symbol)) - and print it all out as I did in the jQuery code below, but that did not work.
I put a comment next to the if statement I am working on, it's towards the bottom of the jQuery.
HTML:
<body onload="onBodyLoad()">
<div id="stockList"></div>
<br />
<br />
<br />
<div id="stockInfo"></div>
jQuery:
$(document).ready(function () {
$.ajax({
type: "GET",
url: "stocks.xml",
dataType: "xml",
success: function (xml) {
var companyNames = [];
$(xml).find('Stock').each(function () {
var symbol = $(this).attr('symbol');
companyNames.push(symbol);
});
companyNames.sort();
$.each(companyNames, function (index, name) {
$('#stockList').append('<div><li>' + name + '</li></div>');
});
function CheckPassword(val, val2) {
var strInput = val.value;
var strInput2 = val2.value;
if (strInput != strInput2) {
val2.focus();
val2.select();
return false;
} else
return true;
}
$(xml).find('Stock').each(function () {
var company = $(this).find('Company').text();
var symbol = $(this).attr('symbol');
var market = $(this).find('Market').text();
var sector = $(this).find('Sector').text();
var price = $(this).find('Price').text();
var low = $(this).find('Low').text();
var high = $(this).find('High').text();
var amount = $(this).find('Amount').text();
var yieldx = $(this).find('Yield').text();
var frequency = $(this).find('Frequency').text();
$('*').mouseover(function () {
$('#stockList li').text($(this).attr('comparison'));
});
$('#stockList li').hover(
function () {
$(this).css({ color: 'red' }); //mouseover
if (name == symbol) { // THIS IS THE STATEMENT YOU'RE LOOKING FOR PROGRAMMING GODS
$('#stockInfo').append('<div><ol><li>' + "Company = " + company + '</li><br/><li>' + "Market = " + market + '</li><br/><li>' + "Sector = " + sector + '</li><br/><li>' + "Price = " + price + '</li><br/><li>' + "Year Range = " + low + " " + high + '</li></ol><br/>');
}
},
function () {
$(this).css({ color: 'navy' }); // mouseout
$('#stockInfo').empty();
}
);
});
}
});
});
XML sample:
<Products>
<Stock symbol="GOOG">
<Company>Google</Company>
<Market>NASDAQ</Market>
<Sector>Software</Sector>
<Price>$487.80</Price>
<YearRange>
<Low>$331.55</Low>
<High>$488.50</High>
</YearRange>
<Dividend available="false"/>
</Stock>
<Stock symbol="BA">
<Company>Boeing Company</Company>
<Market>NYSE</Market>
<Sector>Aerospace</Sector>
<Price>$79.05</Price>
<YearRange>
<Low>$63.70</Low>
<High>$89.58</High>
</YearRange>
<Dividend available="true">
<Amount>$1.20</Amount>
<Yield>$1.50</Yield>
<Frequency>QTR</Frequency>
</Dividend>
</Stock>
<Stock symbol="MO">
<Company>Altria Group</Company>
<Market>NYSE</Market>
<Sector>Comsumables</Sector>
<Price>$81.70</Price>
<YearRange>
<Low>$68.36</Low>
<High>$85.00</High>
</YearRange>
<Dividend available="true">
<Amount>$3.44</Amount>
<Yield>$4.2</Yield>
<Frequency>ANNUAL</Frequency>
</Dividend>
</Stock>
</Products>
var companyData = [];
$(xml).find('Stock').each(function () {
var symbol = $(this).attr('symbol');
companyNames.push(symbol);
companyData[symbol] = {
company: $(this).find('Company').text(),
symbol: $(this).attr('symbol'),
market: $(this).find('Market').text(),
sector: $(this).find('Sector').text(),
price: $(this).find('Price').text(),
low: $(this).find('Low').text(),
high: $(this).find('High').text(),
amount: $(this).find('Amount').text(),
yieldx: $(this).find('Yield').text(),
frequency: $(this).find('Frequency').text()
};
});
...
$("#stocklist li").hover(function() {
$(this).css({ color: 'red' }); //mouseover
var info = companyData[$(this).text()];
$('#stockInfo').append('<div><ol><li>' + "Company = " + info.company + '</li><br/><li>' + "Market = " + info.market + '</li><br/><li>' + "Sector = " + info.sector + '</li><br/><li>' + "Price = " + info.price + '</li><br/><li>' + "Year Range = " + info.low + " " + info.high + '</li></ol><br/>');
});

Is there something wrong with the way i'm starting my script? trying to get array to output a different response per answer

Ok so I think the problem with my code is related to the actual html page i'm activating the code with, but i can't be sure.
what i'm trying to do is have two questions asked via an array. I want the second question to show "gerp" instead of correct and for it to notify you is you are wrong on question two if you are wrong.
this is what i use on the page to start the "riddles" :
<button onclick="myFunction()">Click Me</button>
<p id="demo"></p>
and this is the code i use in the js file that is separate to the html file (sorry if i sound silly, i'm just trying to be specific):
var i = 0;
var myArray = [{
q: "What was her favorite Color?",
a: "purple" }, {
q: "gymhoaccetpptfe",
a: "rev six nine eleven"}];
function myFunction() {
var x;
var name = prompt(myArray[i].q);
if (name == myArray[i].a) {
x = document.getElementById("demo").innerHTML + " " + "Correct!" + " " + "Listen!"
+ " " +
"http://www.npr.org/2010/02/12/123534818/carl-sagan-and-ann-druyans-ultimate-mix-tape"
;
i= i + 1 ;
document.getElementById("demo").innerHTML = x;
} else if ((name == myArray[i + 1].a)) {
x = document.getElementById("demo").innerHTML + " " + "gerp!";
document.getElementById("demo").innerHTML = x;
} else {
x = document.getElementById("demo").innerHTML + name + " " + "is" + " " + "wrong!";
document.getElementById("demo").innerHTML = x;
}
}
I just want "Gerp" to show up when the second question is answered correctly.
it's because in the first if, you do i=i+1, but in the second if, for the gerp, you're checking for i + 1, which would be 2, the third element. unless you need it to be dynamic, just use the corresponding index:
... else if(name == myArray[1].a) { ...
You should really consider fixing your formatting and cleaning up your code. It'll look much nicer and errors will be easier to spot:
var questions = [{
question: 'What was her favorite Color?',
answer: 'purple',
message: 'Correct! Listen: http://www.npr.org/2010/02/12/123534818/carl-sagan-and-ann-druyans-ultimate-mix-tape'
}, {
question: 'gymhoaccetpptfe',
answer: 'rev six nine eleven',
message: 'gerp!'
}];
function add_message(text) {
var element = document.createElement('div');
var demo = document.getElementById('demo');
element.innerHTML = text;
demo.appendChild(element);
}
function ask_questions(questions) {
for (var i = 0; i < questions.length; ) {
var question = questions[i];
var response = prompt(question.question);
if (response == question.answer) {
add_message(question.message);
i++;
} else {
add_message('"' + response + '" is wrong!');
}
}
}
function start_riddles() {
ask_questions(questions);
}

Categories

Resources