I'm trying to display contents of an API but for some reason I'm getting an error in the console that says Uncaught (in promise) TypeError: Cannot read property 'appendChild' of null which sort doesn't make sense to me because I've set to innerHTML = <p>${this.items[i]}</p>.
What am I doing wrong and how can I fix this? If you need more information, please let me know.
Here's my html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="baseball">
<h1>test</h1>
</div>
<script type="application/javascript" src="index.js"></script>
<script type="text/javascript">
window.addEventListener("load", function() {
console.log("document is loaded");
var baseballStats = new BaseballStats();
baseballStats.init("https://statsapi.mlb.com/api/v1/people/660670/stats?stats=byDateRange&season=2018&group=hitting&startDate=&endDate=&leagueListId=mlb_milb", true);
});
</script>
</body>
</html>
Here's my javascript
class BaseballStats {
constructor() {
this.totalItems = 0;
this.list = document.querySelector("baseball");
}
init(url, bool) {
this.bool = bool;
var that = this;
console.log(url);
fetch(url)
.then(resp => resp.json())
.then(data => {
console.log(data.stats);
that.data = data;
if (this.bool) {
that.items = that.data.stats;
this.totalItems = that.items.length;
console.log("about to loop");
for (var i = 0; i < this.totalItems; i++) {
var listNode = document.createElement("LI");
listNode.innerHTML = `<p>${this.items[i]}</p>`;
console.log("did it reach here");
this.list.appendChild(listNode);
}
}
});
}
}
Try to console.log the list variable. You will see it's null. You're treating this variable as an object, but the content of the variable is null.
Your problem is the BaseballStats' list member is null. This is because you're misusing document.querySelector - it selects in the same way as CSS, so to select an element with a class you need to use the . selector - https://www.w3schools.com/cssref/css_selectors.asp.
You are selecting for 'baseball' however, which means it is trying to find an element with the tag name <baseball>. Change this to '.baseball' and it will work
Related
I'm learning JS and I'm trying to create a web game with javascript. The goal is simple: a flag is displayed at random, and the player must guess the name of the country associated with the flag.
The flag is randomly selected and displayed correctly, but I have a problem with the user interaction. I'd like to display "bad answer" in a <p> and if it's correct, display "good answer" (in a <p>), regenerate a flag and start again, indefinitely. The problem is that I can get the user's answer but i can't compare it to real answer and then display true or false.
I would like to know if someone could explain to me what is wrong and correct me please. Here is my code :
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
function getVal() {
const inputValue = document.querySelector('input').value;
console.log(inputValue);
}
function getData() {
var json = 'https://cdn.jsdelivr.net/npm/country-flag-emoji-json#2.0.0/dist/index.json'
fetch(json)
.then(data => data.json())
.then(data => {
const randomInt = getRandomInt(data.length);
console.log(data[randomInt]);
var image = document.getElementById("flag");
image.src = data[randomInt].image;
});
if (inputValue != data[randomInt].name.toLowerCase()) {
document.getElementsByClassName('result').class.add("result-false");
document.getElementsByClassName('result').innerHTML = 'Mauvaise Réponse';
} else if (inputValue == data[randomInt].name.toLowerCase()) {
document.getElementsByClassName('result').class.add("result-true");
document.getElementsByClassName('result').innerHTML = 'Bonne Réponse';
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Guess The Flag - </title>
<link rel="stylesheet" href="style.css">
<!-- <script type="text/js" src="app.js"></script> -->
</head>
<body>
<h1>GuessTheFlag</h1>
<div class="flagCanva">
<img id="flag" src="https://cdn.jsdelivr.net/npm/country-flag-emoji-json#2.0.0/dist/images/KH.svg" alt="">
</div>
<input type="text" name="flagName">
<button type="submit" onclick="getVal()">Je valide</button>
<p class="result"></p><br>
<button onclick="getData()">Next</button>
</body>
</html>
The reason is because the scope of inputValue is inside the function getVal only.
So in function getData it doesn't know inputValue.
The scope is the perimeter where the variable is known, it could be globally, local to a function, or at other level. It depends where and how you declare the variable.
It's an important thing to understand in most of the computer langage.
Here's a refactored working version with some comments to help clear things out:
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
let flag = "Cambodia"; // <= We need a global variable so that it can be set and accessed inside getVal() and getData()
function getVal() {
const inputValue = document.querySelector('input').value;
//>> Move the flag vs input comparison inside the input event handler:
if ( inputValue.toLowerCase() !== flag.toLowerCase()) { // <= Lowercasing both input and flag name to avoid case sensitive comparison failures
// Use `classList` instead of `class` to have access to the add() method
// Use `querySelector` to pick a single element instead of getElementsByClassName which returns a list of elements:
document.querySelector('.result').classList.add("result-false");
document.querySelector('.result').innerHTML = 'Mauvaise Réponse';
// No need for an else if here:
} else {
document.querySelector('.result').classList.add("result-true");
document.querySelector('.result').innerHTML = 'Bonne Réponse';
}
}
// TIP: Ideally the next function should be split into 2 functions:
// 1) fetchData(), runs once to grab the JSON
// 2) getRandomFlag(), runs on 'Next' click to get a random flag
// without re-fetching the JSON.
function getData() {
var json = 'https://cdn.jsdelivr.net/npm/country-flag-emoji-json#2.0.0/dist/index.json'
fetch(json)
.then(data=>data.json())
.then(data=> {
const randomInt = getRandomInt(data.length);
console.log(data[randomInt]);
var image = document.getElementById("flag");
image.src = data[randomInt].image;
flag = data[randomInt].name; // <= Set the value for the newly fetched flag name
});
}
Working demo:
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
let flag = "Cambodia"; // <= We need a global variable so that it can be set and accessed inside getVal() and getData()
function getVal() {
const inputValue = document.querySelector('input').value;
//>> Move the flag vs input comparison inside the input event handler:
if(inputValue.toLowerCase() != flag.toLowerCase()) {
// Use `classList` instead of `class` to have access to the add() method
// Use `querySelector` to pick a single element instead of getElementsByClassName which returns a list of elements:
document.querySelector('.result').classList.add("result-false");
document.querySelector('.result').innerHTML = 'Mauvaise Réponse';
} else {
document.querySelector('.result').classList.add("result-true");
document.querySelector('.result').innerHTML = 'Bonne Réponse';
}
}
function getData() {
var json = 'https://cdn.jsdelivr.net/npm/country-flag-emoji-json#2.0.0/dist/index.json'
fetch(json)
.then(data=>data.json())
.then(data=> {
const randomInt = getRandomInt(data.length);
console.log(data[randomInt]);
var image = document.getElementById("flag");
image.src = data[randomInt].image;
flag = data[randomInt].name;
});
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Guess The Flag - </title>
<link rel="stylesheet" href="style.css">
<!-- <script type="text/js" src="app.js"></script> -->
</head>
<body>
<h1>GuessTheFlag</h1>
<div class="flagCanva">
<img width="100" id="flag" src="https://cdn.jsdelivr.net/npm/country-flag-emoji-json#2.0.0/dist/images/KH.svg" alt="">
</div>
<input type="text" name="flagName">
<button type="submit" onclick="getVal()">Je valide</button>
<p class="result"></p><br>
<button onclick="getData()">Next</button>
</body>
</html>
There's a lot of refactoring that we can do (e.g. caching the selected elements, cache the json response to avoid re-fetching the data, removing global variables, etc.) to improve the code, but this is just a good start for a functional code.
I'm trying to make a web framework and one feature will be a key-value state management tool. I need the second <script> tag to only run after ./script.js loads in.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./framework.js"></script>
</head>
<body>
<p f-text="name"></p>
<script>
Framework.store('name', 'Joe');
</script>
</body>
</html>
framework.js:
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text')) {
const textValue = element.getAttribute('f-text');
const key = window.fStore.find((x) => x.key === textValue);
element.innerHTML = key.value;
}
});
window.Framework = {
store: (key, value?) => {
if (!value) {
const foundKey = window.fStore.find((x) => x.key === key);
return foundKey.value;
}
window.fStore = [...window.fStore, { key: key, value: value }];
}
}
Error:
SyntaxError: Unexpected token ')'
at /framework.js:12:22
ReferenceError: Framework is not defined
at /:12:5
You need to wait that your script is loaded, you can use this
window.addEventListener('load', function() {
Framework.store('name', 'Joe');
})
I have this code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./framework.js"></script>
</head>
<body>
<p f-text="name"></p>
<script>
Framework.store('name', 'Joe');
</script>
</body>
</html>
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text')) {
const textValue = element.getAttribute('f-text');
element.innerHTML = window.fStore[textValue];
}
});
window.Framework = {
store: (key, value = '') => {
if (value === '') {
return window.fStore[key];
}
window.fStore[key] = value;
}
}
But get this error:
TypeError: Cannot set properties of undefined (setting 'name')
at Object.store (/framework.js:15:24)
at /:12:15
I want the page to render 'Joe' by getting the key from f-text, finding the key's value in window.fStore, then setting the element.innerHTML as the value. Framework.store() takes a key and a value, if there is no value it returns the value from the key in window.fStore, if there is then it sets window.fStore[key] to the value.
You need to check whether window.fStore exists first.
window.Framework = {
store: (key, value = '') => {
if(!window.fStore) window.fStore = {}
if (value === '') {
return window.fStore[key];
}
window.fStore[key] = value;
}
}
Framework.store('name', 'Joe');
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text')) {
const textValue = element.getAttribute('f-text');
element.innerHTML = window.fStore[textValue];
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p f-text="name"></p>
</body>
</html>
You might also need to wait till the window loads first. Some browsers will give you a headache if you dont
window.addEventListener('load', e=>{
window.Framework = {
store: (key, value = '') => {
if(!window.fStore)
window.fStore = {};
if (value === '')
return window.fStore[key];
window.fStore[key] = value;
}
}
window.Framework.store('name', 'Joe');
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text'))
element.innerHTML = window.fStore[element.getAttribute('f-text')];
});
}
I'm new to JavaScript and I've been trying to get the title text to switch between different texts for a day now. I've gathered some code snippets and put them together, so I'm not quite sure what's going on.
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
function switchingText(); {
document.getElementByID("title").innerHTML = "Text";
sleep(2000);
document.getElementByID("title").innerHTML = "Text2";
sleep(2000);
switchingText();
}
I would appreciate any help greatly.
This is a sample solution for your dilemma:
const title= document.getElementById("title");
const switchHeading = () => {
if (title.innerHTML== "Text"){
title.innerHTML = "Text2";
}else{
title.innerHTML = "Text";
}
}
setInterval(() => {
switchHeading()
}, 2000);
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="">
</head>
<body>
<h1 id="title">Text</h1>
<script src="./script.js" async defer></script>
</body>
</html></html>
Helpful links:
W3Schools Set Interval
All you need to do is get the title element like you did but instead of changing the InnerHTML, change the value, like so:
document.getElementByID('title').value="Text"
Hope you found what you were looking for.
Instead of doing
document.getElementByID("title").innerHTML = "Text";
in the switching text function,
you need to do
document.title = 'your text'
I'm trying to create a word game that will choose a random item from a list but some of the items have different weights so they show up less often. I want the function to be called once the user presses a HTML button. I have the code working fairly well right now (to the console). My question is how can I get the output from the function into the html web page. If anyone could help me with this, it would be a huge help.
Here's my code:
var item = {
'apple':10,
'banana':10,
'orange':10,
'grapes':1,
}
function testGame(input) {
var array = [];
for(var item in input) {
if(input.hasOwnProperty(item) ) {
for(var i=0; i<input[item]; i++ ) {
array.push(item);
}
}
}
return array[Math.floor(Math.random() * array.length)];
}
console.log(testGame(item));
I have some HTML code too, just don't know where or how to write the button code properly to produce the outcome I'm looking for.
Here's the HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Randomizer Game</title>
</head>
<body>
<h1>Game</h1>
<script src="index.js"></script>
<button onclick="testGame();">Test</button>
</body>
</html>
You can make new function where you can call it on button just like you called it with console.log:
<button onclick="start();">Test</button>
and call your randomizing function inside:
function start() {
testGame(item)
}
Then inside function testGame don't use return, just save random word result in variable and print it in HTML:
var result = array[Math.floor(Math.random() * array.length)];
console.clear();
console.log(result);
document.getElementById("result").innerHTML=result;
I have added div result in HTML:
<div id="result"></div>
EXAMPLE SNIPPET:
var item = {
'apple': 10,
'banana': 10,
'orange': 10,
'grapes': 1,
}
function testGame(input) {
var array = [];
for (var item in input) {
if (input.hasOwnProperty(item)) {
for (var i = 0; i < input[item]; i++) {
array.push(item);
}
}
}
var result = array[Math.floor(Math.random() * array.length)];
console.clear();
console.log(result);
document.getElementById("result").innerHTML=result;
}
function start() {
testGame(item)
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Randomizer Game</title>
</head>
<body>
<h1>Game</h1>
<div id="result"></div>
<script src="index.js"></script>
<button onclick="start();">Test</button>
</body>
</html>
Use getElementById to get an element by id, for this I gave your button an id. With addEventListener you can add an event (here: click) to be call a function.
Doing here your randomizing. Get the element where you want the output again with getElementByIdand add with textContent your answer to it.
const ITEMS = {
'apple':10,
'banana':10,
'orange':10,
'grapes':1,
}
document.getElementById('btn').addEventListener('click', function testGame() {
var array = [];
for(var item in ITEMS) {
if(ITEMS.hasOwnProperty(item) ) {
for(var i=0; i<ITEMS[item]; i++ ) {
array.push(item);
}
}
}
document.getElementById('item').textContent = array[Math.floor(Math.random() * array.length)];
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Randomizer Game</title>
</head>
<body>
<h1>Game</h1>
<button id='btn'>Test</button>
<div id='item'></div>
</body>
</html>