I'm just learning JS and trying to understand loops, I've created a very simple loop, however I want to style the loop output using CSS, like put each item into a grid layout.
I've seen tags inside document.write () parentheses however wherever I put the tag I get a SyntaxError for an unexpected '<'
Would someone kindly (and simply haha) explain where I'm going wrong and where the tags can go (feels like i've tried everywhere)
Thanks
Here is my JS code (don't have a CSS yet as not been able to tag the JS)
let motorbikeList = [
{ name: "Ducati V4r", horsepower: 240, },
{ name: "Ducati V4", horsepower: 214, },
{ name: "Ducati V2", horsepower: 155, },
{ name: "Yamaha R1", horsepower: 197, },
{ name: "Yamaha R6", horsepower: 133, }
]
for (let i = 0; i < motorbikeList.length; i++) {
if (motorbikeList[i].horsepower >= 220) {
document.write(motorbikeList[i].name + "(HP:" + motorbikeList[i].horsepower + " ) - Wow! that's alot of horsepower !" + '<br>');
} else if (motorbikeList[i].horsepower){
document.write(motorbikeList[i].name + "(HP:" + motorbikeList[i].horsepower + ") <br> ")
}
}
I think you may be after something like this:
let motorbikeList = [
{ name: "Ducati V4r", horsepower: 240, },
{ name: "Ducati V4", horsepower: 214, },
{ name: "Ducati V2", horsepower: 155, },
{ name: "Yamaha R1", horsepower: 197, },
{ name: "Yamaha R6", horsepower: 133, }
]
for (let i = 0; i < motorbikeList.length; i++) {
if (motorbikeList[i].horsepower >= 220) {
document.write("<div style='background-color:red;'>" + motorbikeList[i].name + "(HP:" + motorbikeList[i].horsepower + " ) - Wow! that's alot of horsepower !" + '</div>');
} else if (motorbikeList[i].horsepower){
document.write("<div style='background-color:green;'>" + motorbikeList[i].name + "(HP:" + motorbikeList[i].horsepower + ")</div>")
}
}
But it is worth looking at alternatives to document.write as a whole like Ostone0 has mentioned in his comment.
Let's be clear, document.write is one of the worst things in javascript, you can instead say:
document.body.innerHTML = "whatever you want"
and if you need to add some text to the exists text you can use the += operator instead of the = operator like that:
document.body.innerHTML += "another text"
And here you can use any HTML tag inside this code:
document.body.innerHTML = "<span> Welcome " + username + " </span><br/>"
or any other code.
a small note
if you need to concatenate strings with variable outputs, use the template string operator like this:
document.body.innerHTML = `<span> Welcome ${username} </span></br>`
Related
I am building a text based choice RPG game using Javascript. I am able to add functions inside of an object and run them, however, I want the function to run if the option ( text node ) is chosen.
Currently, I declare a function, and then call it at the bottom of the page. The issue is, I want to function to run as soon as the choice is chosen.
Here is the code I have so far...
function addRadiatedStatus(){
character.radiated = true;
}
const textNodes = [
{
id: 1,
text: "Do you want to help this person?",
options: [
{
text: "Yes",
nextText: 2
},
{
text: "No",
nextText: 2
},
],
faction: "Greaser"
},
{
id: 2,
text: "It was a trap, do you run or kill them?",
options: [
{
text: "Run",
nextText: 3,
damage: 0,
},
{
text: "Kill",
nextText: 5,
damage: 8,
moneyDrop: 20,
radiationDamage: 2,
function: function update(){
if(character.greaser == true && textNodes[1].faction ==
"greaser"){
console.log("greaser check worked");
}
console.log("upadte function worked")
character.health -= this.damage;
character.cash += this.moneyDrop;
character.radiationLevel += this.radiationDamage;
character.maxHP -= this.radiationDamage;
if(this.radiationDamage >= 1 ){
addRadiatedStatus();
}
console.log("character HP" + " " + character.health);
console.log("character maxHP" + " " + character.maxHP);
console.log("moneyDrop " + " " + this.moneyDrop);
console.log("character cash" + " " + character.cash);
console.log("radiation level" + " " + character.radiationLevel);
console.log("character Raditated?" + " " + character.radiated);
}
}
],
faction: "Greaser",
}
]
textNodes[1].options[1].function();
I tried a few different things, including adding another key run: update() , run: this.update(), run: this.function, etc.
I am also a little confused because I thought that ES6 allows me to remove the function keyword, but that doesn't seem to be allowed ( I am probably just doing it wrong, or running into an issue based on how I set up my object ).
I am not sure how to go from calling textNodes[1].options[1].function to dynamically calling whichever function is nested within a specific choice option.
Thanks in advance for the help.
You can change this section to this:
update: (function(){
console.log("upadte function worked")
character.health -= this.damage;
character.cash += this.moneyDrop;
character.radiationLevel += this.radiationDamage;
character.maxHP -= this.radiationDamage;
if(this.radiationDamage >= 1 ){
addRadiatedStatus();
}
console.log("character HP" + " " + character.health);
console.log("character maxHP" + " " + character.maxHP);
console.log("moneyDrop " + " " + this.moneyDrop);
console.log("character cash" + " " + character.cash);
console.log("radiation level" + " " + character.radiationLevel);
console.log("character Raditated?" + " " + character.radiated);
});
The function keyword has been changed to update, then the function has been changed to an anonymous function ... You should refrain from using js keywords for your user-defined ones.
Then the anonymous function will not execute until you call it:
textNodes[1].options[1].update()
Here's an example of what I'm talking about:
This shows that you have to actually access the function and any other key for that matter for you to access the values.
var text = [
{
data1: "here",
data2: (function(){
return "You have to explicitly call me!"
})
}
]
console.log("Without calling any data: " + text[0])
console.log("When you call data2: " + text[0].data2())
console.log("Let us access data1: " + text[0].data1)
The legend in my highcharts is not functioning correctly. I have 2 series, each named differently. When I click on 'series1' on the legend, the data for 'series1' is hidden leaving 'series2' behind. However, when I click on 'series2' first, all the data is hidden instead of only 'series2'. What are some common mistakes that could lead to this? Here is a snippet of the involved code:
EDIT: Here is a jsFiddle reproduction of my bug. You can see when you click on "Pool" in the legend both series are removed. Clicking on client, however, works as expected. https://jsfiddle.net/LLExL/5616/
EDIT2: I found the solution. Apparently if the first index of a given series of data is empty, the error occurs. It also appears that it must have the same number of elements equal to the index with the least amount of data points. So in my case, I check if the first element in clientChartData or poolChartData is empty, if so I append 5 pairs of quotes to that array.
$('#all-cpus').highcharts({
chart: {
type: 'boxplot',
inverted: true
},
title: {
text: 'All CPUs'
},
xAxis: {
title: { text: 'Hybrids' }
},
yAxis: {
title: { text: 'Yield' }
},
legend: { enabled: true },
credits: { enabled: false },
tooltip: {},
plotOptions: {
boxplot: {
fillColor: '#6c3a38',
lineWidth: 3,
medianWidth: 3,
stemColor: '#DF5353',
stemDashStyle: 'dot',
stemWidth: 1,
whiskerColor: '#AAEEEE',
whiskerLength: '20%',
whiskerWidth: 3
}
},
series: [
{ name: 'Client' },
{ name: 'Pool' }
]
});
Here is where I set the chart data. Assume organizedClientData as all the data I need.
//Set the data to be displayed on the chart
clientChartData = organizedClientData;
$chart.highcharts().series[0].setData(clientChartData, false);
$chart.highcharts().series[0].update({name: this.clientData.name}, false);
$chart.highcharts().series[1].setData(poolChartData, false);
$chart.highcharts().series[1].update({name: this.poolData.name}, false);
//Set tooltip info to reflect dynamic data and to include acres
$chart.highcharts().tooltip.options.formatter = function() {
var index = this.point.x;
var numAcresPool = poolAcres[ index ];
var numAcresClient = clientAcres[ index ];
var tooltipString = this.point.category + '<br>' +
this.point.series.name + '<br>' +
'Max: ' + this.point.high + '<br>' +
'Upper Quartile: ' + this.point.q3 + '<br>' +
'Median: ' + this.point.median + '<br>' +
'Lower Quartile: ' + this.point.q1 + '<br>' +
'Min: ' + this.point.low + '<br>';
//Handle client vs. pool acreage display, and check if on the 'All Cpus' tab
if(this.point.series.name == 'Pool') {
tooltipString = tooltipString + 'Acres: ' + numAcresPool;
}
else if(this.point.series.name == 'All Farms' /*&& $chart.selector != '#all-cpus'*/) {
tooltipString = tooltipString + 'Acres: ' + numAcresClient;
}
return tooltipString;
};
var xaxis = $chart.highcharts().xAxis[0];
var title = { text: this.getProfileData().cropId == 1 ? 'Hybrids' : 'Varieties' };
xaxis.setCategories(categories, true);
xaxis.setTitle(title, true);
$chart.highcharts().setSize( $chart.parent().width() - 15, $chart.highcharts().xAxis[0].categories.length * 50 + 150 );
$chart.highcharts().redraw();
This looks like a bug, I reported that to our developers here: https://github.com/highcharts/highcharts/issues/4939
I have two arrays and an object. One array conatins product codes, and another contains the quantities thereof. For example, the first quantity in the quantities array is the quantity of the product code that is the first one in the product code array. I also have an object that contains customer data. It would look something like this:
customer={
name:' firstname lastname',
email: 'example#domain.com',
company: "company name",
phone_number: 'phone number',
}
the arrays would look like this:
product_codes=[code_1; code_2; code_3];
quantities=[23, 56, 45];
Say that all of this is being mailed to customersupport#example.com.
I am familiar with the basics of the mailto function, but I would like to know if there is a way to format the body of the email so that it looks something like this:
...................................
Name: customer.name
email: customer.email
company name: customer.company
phone number: customer.phone_number
product code 1: corresponding quantity
product code 2: corresponding quantity
product code 3: corresponding quantity
...............................................
I would also like to be able to add any other given codes and quantities, as I am not sure of how many there will be. Is this even possible? If so, how? Please explain so that I can not only use it, but also understand how it works. Thanks!
If I'm not being clear enough, please let me know so I can edit it for greater clarity.
var sendEmail = function() {
var customer, body, quantities, product_codes;
customer = {
name: 'First Last',
email: 'example#example.com',
company: 'Company',
phone_number: 'phone number',
}
body = 'Name: '+ customer.name;
body += '\nEmail: '+ customer.email;
body += '\nCompany: '+ customer.company;
body += '\nPhone Number: '+ customer.phone_number;
product_codes = ['code_1', 'code_2', 'code_3'];
quantities = [23, 56, 45];
for(var i = 0; i < product_codes.length; i += 1) {
body += '\nProduct Code '+ product_codes[i] +': '+ quantities[i];
}
subject = 'Your Subject';
window.location = 'mailto:customersupport#example.com?body='+ encodeURIComponent(body) +'&subject='+ encodeURIComponent(subject);
};
// execute this function when the user clicks the #send-email button
var button = document.getElementById('send-email');
button.addEventListener('click', sendEmail);
I would build the string in a function:
HTML:
Click to Email
JAVASCRIPT:
//stuff you specified...
var customer={
name:' firstname lastname',
email: 'example#domain.com',
company: "company name",
phone_number: 'phone number',
}
var product_codes=['alpha', 'beta', 'gamma'];
var quantities=[23, 56, 45];
/* Assign a click action onto the link */
var yourLink = document.getElementById("thelink");
yourLink.onclick = function() {
var elf = "%0A"; //Encoded Line Feed
mailtoBody = "Name: " + customer.name + elf
+ "Email: " + customer.email + elf
+ "Company Name: " + customer.company + elf
+ "Phone Number: " + customer.phone_number + elf;
for (var i=0; i < product_codes.length; i++) {
mailtoBody += product_codes[i] + ": " + quantities[i] + elf;
}
location.href = "mailto:you#example.com?body=" + mailtoBody;
}
Here's a working example:
http://jsbin.com/kigutuhiqa/edit?html,js,output
It sounds to me like you want to construct the body of a message. If this is the case, you can create a function that builds the body of your message by taking in the 3 objects you mentioned: customer, codes, and quantity.
For example, you can do something like
function buildBody(cust, codes, quant){
var body = "";
body += "Name: " + cust.name + "\n";
body += "Email: " + cust.email + "\n";
body += "Company Name: " + cust.companyname + "\n";
for(var i=0; i<codes.length; ++i)
body += "Product Code " + codes[i] + ": " quant[i] + "\n";
return body;
}
I have not tested this code, but hopefully you get the idea.
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
I'm the type of person who loves to do a lot of projects especially if it involves only JavaScript since that is my strong point.
I thought of a little fun idea. Writing little pieces of CSS with JavaScript. These CSS pieces could then be used in a Blob or implemented into the webpage some other way.
Most of the time, I do projects just for FUN and for build up in experience.
Let's get more of a feel for what we are working with. One of these JavaScript stylesheets could look like this:
var sheet = {
"h1": {
"font-size": "24px",
"color": "blue",
children: {
"a": {
"font-size": "15px"
}
}
},
"a": {
color: "red"
}
};
This would return:
h1{font-size:24px;color:blue}h1 a{font-size:15px}a{color:red}
Note the children propert in the h1 element.
This is my way of nesting, making the h1 a.
My question however is, how could I make a continuous nesting so I could end up with something like:
"h1 div span a"
Meaning that each nested child will need be able to use the children property.
The script I have so far is below (belongs with the variable sheet).
var to = "";
for (var el in sheet) {
var props = [];
for (var prop in sheet[el]) {
if(prop != "children") {
props.push(prop + ":" + sheet[el][prop]);
}
}
to += el + "{" + props.join(";") + "}";
//----
if (sheet[el].children) {
for (var el2 in sheet[el].children) {
var props = [];
for (var prop in sheet[el].children[el2]) {
props.push(prop + ":" + sheet[el].children[el2][prop]);
}
to += el + " " + el2 + "{" + props.join(";") + "}"
}
}
//----
}
The sections in between the comments is the code I use for the 1 time nesting.
I'm not sure how difficult this would be to add. But I understand that it probable wouldn't be easy.
My full example is here: http://jsfiddle.net/shawn31313/2tfnz/1
You can pretty easily make your code recursive:
function buildCSS(stub, node){
var to = "";
for (var el in node) {
var rule = stub + " " + el;
var props = [];
for (var prop in node[el]) {
if(prop != "children") {
props.push(prop + ":" + node[el][prop]);
}
}
to += rule + "{" + props.join(";") + "}";
if (node[el].children) {
to += buildCSS(rule, node[el].children);
}
}
return to;
}
var to = buildCSS("", sheet);
This can definitely be cleaned up, but it illustrates the idea.
http://jsfiddle.net/2tfnz/3/
You might also consider a tweak to your object structure to make the code a bit cleaner:
var sheet = {
"h1": {
rules: {
"font-size": "24px",
"color": "blue"
},
children: {
"a": {
rules: {
"font-size": "15px"
}
}
}
},
"a": {
rules: {
color: "red"
}
}
};
In this way, you wouldn't need to distinguish between properties named children and those which aren't.
http://jsfiddle.net/2tfnz/6/
Put the code in a method so it can recursively call itself when it finds children. This one allows for both children and descendant properties and outputs nicely formatted CSS.
var sheet = {
"h1": {
"font-size": "24px",
"color": "blue",
children: {
"a": {
"font-size": "15px",
descendants: {
"span": {
"font-weight": "bold"
}
}
}
}
},
"a": {
color: "red"
}
};
function toCSS(obj, pre) {
var str = '', pre = pre || '';
for (var selector in obj) {
str += pre + selector + ' {\n';
var rules = obj[selector];
for (var ruleKey in rules) {
if (['descendants', 'children'].indexOf(ruleKey) > -1) continue;
str += ' ' + ruleKey + ': ' + rules[ruleKey] + ';\n';
}
str += '}\n\n';
if ('descendants' in rules) str += toCSS(rules.descendants, pre + selector + ' ');
if ('children' in rules) str += toCSS(rules.children, pre + selector + ' > ');
}
return str;
}
console.log(toCSS(sheet));
I like all of you guy's code. However, I told the idea of making my code recursive.
So I've made:
var to = "";
for (var el in sheet) {
var props = [];
var nest = [el];
var nestLookUp = {
"children": ">",
"desendents": ""
};
var nests = /children|desendents/;
var addNest = function (shh) {
for (var found in nestLookUp) {
if (shh.hasOwnProperty(found)) {
for (var el2 in shh[found]) {
var props = [];
nest.push(nestLookUp[found]);
nest.push(el2);
for (var prop in shh[found][el2]) {
if (!prop.match(nests)) {
props.push(prop + ":" + shh[found][el2][prop]);
}
}
if (props.length > 0) {
to += nest.join(" ").replace(/\s{2}/, " ") + "{" + props.join(";") + "}";
}
addNest(shh[found][el2]);
nest.pop();
nest.pop();
};
}
}
};
for (var prop in sheet[el]) {
if (prop != "children") {
props.push(prop + ":" + sheet[el][prop]);
}
}
to += el + "{" + props.join(";") + "}";
addNest(sheet[el]);
}
#Trevor - Your Idea with the descendants, is a good one :)
My code is a little longer than Trevors but personally more manageable. (39 lines his is 20)
I usually like making my own code because I usually understand how the code works.
Ha, I was a little confused looking at you guys code.