Button Event-listener is unresponsive - javascript

I'm following a tutorial and I made a button to show some content. However this button doesn't work and I'm at my wits end unable to figure out what could be causing this.
Can someone show why this doesn't work?
const users = document.querySelector('#user');
const getUsers = document.getElementById('getUsers');
getUsers.addEventListener('click', loadUsers);
var loadUsers = () => {
console.log('hello button clicked')
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.github.com/users', true);
xhr.onload = () => {
if (this.status == 200) {
let gusers = this.responseText
console.log(gusers);
}
}
xhr.send()
}
console.log(getUsers)
<h1>USER</h1>
<button id="getUsers">Get Users</button>
<div id="users"></div>

Order of your variable declarations matters in this scenario due to hoisting - move the loadUsers definition above the call.
JavaScript only hoists declarations, not initializations. If a
variable is declared and initialized after using it, the value will be
undefined.
The block-quote above from MDN explains why function declarations can be defined after they are called (reading code from top-to-bottom), but variables that are initialized after they are used would have a value of undefined.
const users = document.querySelector('#user');
const getUsers = document.getElementById('getUsers');
const loadUsers = () => {
console.log('Load users..');
}
getUsers.addEventListener('click', loadUsers);
<head>
<meta charset="UTF-8">
<title>Testing AJAX</title>
</head>
<body>
<h1>USER</h1>
<button id="getUsers">Get Users</button>
<div id="users"></div>
</body>
Or you could keep the function at the bottom but use a function declaration which will be hoisted:
const users = document.querySelector('#user');
const getUsers = document.getElementById('getUsers');
getUsers.addEventListener('click', loadUsers);
function loadUsers() {
console.log('Load users..');
}
<head>
<meta charset="UTF-8">
<title>Testing AJAX</title>
</head>
<body>
<h1>USER</h1>
<button id="getUsers">Get Users</button>
<div id="users"></div>
</body>

In addition to the correct answer have a look at your code that I have refactored below. Hope this helps.
// Get Elements
const usersList = document.querySelector('#usersList');
const usersBtn = document.querySelector('#usersBtn');
// Bind listener to usersButton
usersBtn.addEventListener('click', () => {
// XHR Request function
const xhr = new XMLHttpRequest();
xhr.open('GET','https://api.github.com/users')
xhr.send()
xhr.onload = function() {
if (xhr.status == 200) {
// Convert the response to JSON and assign it to data
const data = JSON.parse(xhr.responseText)
// Loop throug through data
for(let i = 0; i <data.length; i++) {
// Create LI element and append the user name
const listItem = document.createElement('li');
usersList.appendChild(listItem).innerHTML = data[i].login
}
}
}
})
<h1>USERS</h1>
<button id="usersBtn">Get Users</button>
<ul id="usersList"></ul>

Related

localStorage not working properly/localStorage overwriting itself

I'm attempting to create a simple to-do list and I've encountered two problems:
After refreshing the page, all the created elements are no longer visible on the page despite being in local storage.
After refreshing the page and submitting new values to the input, localStorage overwrites itself.
Despite that, the items displayed from the input fields are from the previous localStorage, which no longer exists (I really hope this makes sense).
const inputEl = document.getElementById("inputEl")
const submitBtn = document.getElementById("submit")
const clearBtn = document.getElementById("clearBtn")
const todoListContainer = document.getElementById("todoList")
const taskContainer = document.querySelector(".task")
const cancelBtn = document.querySelector(".cancelBtn")
const doneBtn = document.querySelector(".doneBtn")
const errorMsg = document.querySelector(".error")
let localStorageContent = localStorage.getItem("tasks")
let tasksItem = JSON.parse(localStorageContent)
let tasks = []
function createTask() {
if (inputEl.value.length != 0) {
const newDiv = document.createElement("div")
newDiv.classList.add("task")
const newParagraph = document.createElement("p")
const newCancelBtn = document.createElement("button")
newCancelBtn.classList.add("cancelBtn")
newCancelBtn.textContent = "X"
const newDoneBtn = document.createElement("button")
newDoneBtn.classList.add("doneBtn")
newDoneBtn.textContent = "Done"
todoListContainer.appendChild(newDiv)
newDiv.appendChild(newParagraph)
newDiv.appendChild(newCancelBtn)
newDiv.appendChild(newDoneBtn)
//^^ Creating a container for a new task, with all its elements and assigning the classes^^
tasks.push(inputEl.value)
inputEl.value = ""
for (let i = 0; i < tasks.length; i++) {
localStorage.setItem("tasks", JSON.stringify(tasks))
newParagraph.textContent = JSON.parse(localStorageContent)[i]
}
errorMsg.textContent = ""
} else {
errorMsg.textContent = "You have to type something in!"
errorMsg.classList.toggle("visibility")
}
}
submitBtn.addEventListener("click", () => {
createTask()
})
clearBtn.addEventListener("click", () => {
localStorage.clear()
})
HTML code below:
<!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">
<link rel="stylesheet" href="/style.css">
<script src="/script.js" defer></script>
<title>To-do list</title>
</head>
<body>
<h2 class="error visibility"></h2>
<div id="todoList">
<h1>To-Do List</h1>
<input type="text" name="" id="inputEl" placeholder="Add an item!">
<button type="submitBtn" id="submit">Submit</button>
<button id="clearBtn">Clear list</button>
<div class="task">
</div>
</div>
</body>
</html>
After refreshing the page, all the created elements are no longer visible on the page despite being in local storage
That is because you are rendering the HTML only after the click event and not on page load. To render the HTML for existing tasks stored in the localStorage you have to write a code that loops over your existing tasks in the tasksItem and applies the rendering logic to it.
I would suggest splitting the rendering code from your createTask() function and create a new function for it (for example renderTask()), then you can use it inside a loop on page load and also call the function once a new task is created in the createTask() function.
window.addEventListener('load', (event) => {
// Your read, loop and render logic goes here
})
After refreshing the page and submitting new values to the input, localStorage overwrites itself.
That's because you are actually overriding the tasks in the localStorage. To keep existing tasks, you have to use your tasksItem variable instead of the blank tasks array to create your tasks in and save them to the localStorage.
So, instead of:
tasks.push(inputEl.value)
You would use:
tasksItem.push(inputEl.value)
The same goes for:
for (let i = 0; i < tasksItem.length; i++) {
localStorage.setItem("tasks", JSON.stringify(tasksItem))
// …
}

P5JS Unable to access values of an object

I am using the LoadBytes function from P5.JS which returns an object but I am unable to access "object" via using ".bytes" or "['bytes']".
For some reason when running a live server it seems to work perfectly.
function get_rom () {
var fileList = document.getElementById("rom").files;
var fileReader = new FileReader();
if (fileReader && fileList && fileList.length) {
url = URL.createObjectURL(fileList[0])
return loadBytes(url)
}
}
function loadarom () {
object = get_rom()
print(object.bytes[1]) // <-- issue here
}
When using loadBytes() the returned object will not immediately have the bytes array populated. You need to use the callback parameter to loadBytes():
let loadBtn;
function setup() {
noCanvas();
noLoop();
loadBtn = createButton("Load Rom");
loadBtn.mousePressed(loadarom);
}
function get_rom(onSuccess) {
let fileList = document.getElementById("rom").files;
// Alternate method using FileReader has been commented out
// let fileReader = new FileReader();
/*
fileReader.addEventListener("load", function () {
// convert image file to base64 string
loadBytes(fileReader.result, onSuccess);
}); */
if (fileList && fileList.length) {
loadBytes(URL.createObjectURL(fileList[0]), onSuccess);
// fileReader.readAsDataURL(fileList[0]);
}
}
function loadarom() {
get_rom(obj => {
print(obj.bytes[0]);
})
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
</head>
<body>
<input id="rom" type="file" />
</body>
</html>
Barley don't need p5.js for such a easy task.
use blob.arrayBuffer()
<script type="module">
var file = new File(['abc'], 'abc.txt')
var bytes = new Uint8Array(await file.arrayBuffer())
console.log(bytes)
</script>
(SO! You should support top level await in a async IIFE fn or start adding type="module" to js-code

This keyword issue?

I asked this question earlier but the formatting wasnt too helpful so im reposting. The logic that I wanted to implement was everytime the button is clicked, the value from the input boxes goes to the addInventory function, creates a new Album, and pushed the newly created object to the products array. At the end of the logic, there is a FOR OF loop that invokes the gridChild function and creates a new div box for every element in the array.
The issue is that whenever I click the button, nothing happens and also the THIS keyword is undefined. The new object doesnt get pushed into the array and only works when the addInventory function is a called manually.
"use strict";
// DIV NESTING USING ONLY JS
const app = document.querySelector(`.app`);
const div = `<div class="grid-child">
</div>`;
const gridContainer = `<div class="grid-container"></div>`;
// New products will be pushed in here
let products = [];
const gridParent = function() {
// app.insertAdjacentHTML(`afterbegin`, gridContainer);
app.insertAdjacentHTML(`afterbegin`, gridContainer);
}
gridParent();
const gridContainerDiv = document.querySelector(`.grid-container`);
const gridChild = function() {
gridContainerDiv.insertAdjacentHTML(`afterbegin`, div);
}
// UI logic for product form
// Will create a new album object
// Need to use a prototype and class inheritance so the component is reusable and applicable for different
// types of products.
class Album {
constructor(title, artist, price) {
this.title = title;
this.artist = artist;
this.price = price;
}
}
const addInventory = (title, artist, price) => {
const newAdd = new Album(title, artist, price);
products.push(newAdd);
console.log(this);
};
// THIS WORKS, ADDS TO THE PRODUCT ARRAY NO PROBLEM
//
addInventory(`Internet`, `Donald Glover`, 15);
addInventory(`Black Pumas`, `Black Pumas`, 31);
const productName = document.getElementById(`product-name`);
console.log(productName);
const productPrice = document.getElementById(`product-price`);
console.log(productPrice);
const inputBtn = document.getElementById(`inputBtn`);
console.log(inputBtn);
// THIS DOESNT WORK, THE THIS KEYWORD IS UNDEFINED AND DOES NOT CALL THE ADDINVENTORY FUNCTION
inputBtn.addEventListener(`click`, (e) => {
e.preventDefault();
addInventory(productName.value, ` `, +productPrice.value);
console.log(this);
})
console.log(products);
// Product list mutation needs to happen before the loop function
// so that data will be the most updated version everytime
// A div box will be created for every element in the product array
for (const everyElement of products) {
gridChild();
console.log(everyElement);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Product div creator</title>
<link rel="stylesheet" href="css/style.css">
<script type="module" defer src="script.js"></script>
</head>
<body>
<div class="app">
</div>
<div class="inventory-gui">
<input type="text" id="product-name" name="product-name" data-product="info"><br>
<input type="number" id="product-price" name="product-price" data-product="info"><br><br>
<button type="submit" id="inputBtn">Make a new item</button>
</div>
</body>
</html>
The script is doing what it is supposed to do.
const addInventory = (title, artist, price) => {
const newAdd = new Album(title, artist, price);
products.push(newAdd);
console.log(this);
};
addInventory(`Internet`, `Donald Glover`, 15);
In the above script the this keyword will be undefined because addInventory is an arrow function and you are using strict mode.
inputBtn.addEventListener(`click`, (e) => {
e.preventDefault();
addInventory(productName.value, ` `, +productPrice.value);
console.log(this);
})
The callback of event listener has access to the addInventory function so it will be called when the button is clicked. this keyword will be undefined here because it is an arrow function.
`console.log(products);`
I think you are expecting the above line to execute again when you click on the button. When you click on the button only the callback given to event listener gets executed not the whole script. If you need to access the updated products array you can in the callback like after calling the addInvetory function.
See this sandbox for what I'm trying to say.

How to use variable in Handlebars?

My question is: How can i use variable ime in Handlebars template(sadrzaj-template)?
My HTML code:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>2 kolokvijum</title>
</head>
<body>
Ime autora: <input id="imeAutora" type="text" value="..."><br><br>
<button id="btnAutor" type="submit" onClick="autorIme()">Prikazi</button><br><br>
<script src="handlebars-v4.0.11.js"></script>
<script id="sadrzaj-template" type="text/x-hanldebars-template">
{{#each knjige}} {{#equal autor ime}}
<h2>{{naslov}}</h2>
<img src="{{slika}}">
<h4>{{brojstrana}} strana</h4>
<h3>Autor: {{autor}}</h3>
<h3>cena: {{cena}}</h3>
{{/equal}} {{/each}}
</script>
<div id="sadrzaj"></div>
<script>
function autorIme() {
var ime = document.querySelector("#imeAutora");
console.log(ime.value);
var ourRequest = new XMLHttpRequest();
ourRequest.open('GET', 'json.json');
ourRequest.onload = function() {
if (ourRequest.status >= 200 && ourRequest.status < 400) {
var data = JSON.parse(ourRequest.responseText);
createHTML(data);
} else {
console.log("We connected to the server, but it returned an error.");
}
};
ourRequest.onerror = function() {
console.log("Connection error");
};
ourRequest.send();
function createHTML(knjigeData) {
var knjigeTemplate = document.querySelector("#sadrzaj-template").innerHTML;
var compiledTemplate = Handlebars.compile(knjigeTemplate);
var ourGeneratedHTML = compiledTemplate(knjigeData);
var knjigeContainer = document.querySelector("#sadrzaj");
knjigeContainer.innerHTML = ourGeneratedHTML;
};
};
</script>
<script>
Handlebars.registerHelper('equal', function(lvalue, rvalue, options) {
if (arguments.length < 3)
throw new Error("Handlebars Helper equal needs 2 parameters");
if (lvalue != rvalue) {
return options.inverse(this);
} else {
return options.fn(this);
}
});
</script>
</body>
</html>
You need to pass it as an argument (as a field in the argument object actually) to the compiled template (which is actually a function). In your case, it is compiledTemplate(). Since you're already passing knjigeData to it, just add your variable as a field to the data object which eventually becomes knjigeData.
var data = JSON.parse(ourRequest.responseText);
data.ime = ime.value;
createHTML(data);
Now you can use it like {{ime}} if you want the variable's direct value in the template. Or you can use it like how you've done, like {{#equal autor ime}}

XMLHttpRequest.onload constructor in Javascript?

When making a new XMLHttpRequest, like so
let http = new XMLHttpRequest();
There is a method (property ?) called onload. My question is, why we can assign a function to it? like this
http.onload = function(){};
If onload is a property in XMLHttpRequest, the value of it will change when we assign a new value to it, like a function, right?
What is the XMLHttpRequest.onload constructor looks like?
I am really confused about this.
When we first started writing HTML event handlers, we would write
<button onclick="alert('Hey you')"> Say Hi</button>
When you translate the HTML into the DOM, you get a reference to an object and you can also set that event handler by setting a property to a function. The browser will call that function for you when the user clicks the button. This is a poor man's event system using properties as callbacks.
Those were the two ways to set an event handler:
XML attribute
DOM element reference by setting a property
After a while, they realized that multiple listeners may need to be set (overwriting a property would override the previous listener). We created hacks to allow setting multiple handlers by creating function that called other functions and there was a need for a real event system. The DOM element became an EventTarget, and you can now use a more common Event interface.
buttonEl.addEventListener('click', () => console.log('clicked'));
buttonEl.addEventListener('click', () => console.log('second handler'));
So we have three different ways of setting events. XmlHttpRequest also implements its own XMLHttpRequestEventTarget event target, so you can set "events" in both ways, through callback properties and addEventListener.
In your question, you said: "What is the XMLHttpRequest.onload constructor looks like?" You probably meant how can we override the onload property because a property doesn't have a constructor, its value has a constructor, but we typically assign it using a literal. It's initially unset (undefined), if it's never set, it won't get called.
It uses XMLHttpRequest() in its program;
Everything works on the ChromeDev per-browser. However, when I try to run the page in other browsers this method is blocked.
[enter image description here][1]
[1]: https://i.stack.imgur.com/mfE0k.png
In the MDN documentation it is written that currently the method is supported by all browsers.
Does anyone know why I can't run my script in other browsers?
function loadDocumentCsv() {
const xhttp = new XMLHttpRequest();
xhttp.onload = function () {
const request = this.responseText.toString();
const allRecords = downloadElement("#allRecords");
const isofixTrue = downloadElement("#isofixTrue");
const brandSort = downloadElement("#brandSort");
const sortMileageAndIsofixFalse = downloadElement("#sortMileageAndIsofixFalse");
allRecords.innerHTML = "";
isofixTrue.innerHTML = "";
brandSort.innerHTML = "";
sortMileageAndIsofixFalse.innerHTML = '';
const arrayData = request.replace(/\n/g, ",").split(",");
const newArray = arrayData.map((element, index) => {
element.trim();
if (index > 4) {
if (index % 5 === 2) {
const cutLetters = element.replace(/[^0-9]+/, "");
element = Number(cutLetters.substring(cutLetters.length - 4, cutLetters.length));
if (element == 0) element = 2007;
} else if (index % 5 === 3) {
element = Number(element.replace(/[^0-9]+/, ""));
} else if (index % 5 === 4) {
element = element.trim() === "TRUE";
}
}
return element;
});
const arrayOfArrays = [];
while (newArray.length) {
const addArrayToArrays = newArray.splice(0, 5);
arrayOfArrays.push(addArrayToArrays);
}
//header table
const header = arrayOfArrays.splice(0, 1);
const firstRow = (table) =>
header.forEach((el) => {
const item = templateHtml(el, "font-weight-bold");
table.appendChild(item);
});
//ALL CARS
firstRow(allRecords);
arrayOfArrays.forEach((element, index) => {
const item = templateHtml(element);
allRecords.appendChild(item);
const item2 = templateHtml(element);
element[4] === true || index === 0 ? isofixTrue.appendChild(item2) : "";
});
//isofix == TRUE
firstRow(isofixTrue);
//sort Brand
firstRow(brandSort);
arrayOfArrays.sort(sortedBrand);
arrayOfArrays.forEach((element) => {
const item = templateHtml(element, "font-weight-normal");
brandSort.appendChild(item);
});
// sortMileageAndIsofixFalse
firstRow(sortMileageAndIsofixFalse);
arrayOfArrays.sort((prev, next) => prev[3]- next[3]);
arrayOfArrays.forEach((element) => {
const item = templateHtml(element, "font-weight-normal");
element[4] === false ? sortMileageAndIsofixFalse.appendChild(item) : "";
});
};
xhttp.open("GET", "Cars.csv");
xhttp.send();
}
<!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>Document</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.6.0/dist/css/bootstrap.min.css"
integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.0/css/solid.css">
<link rel="stylesheet" href="./css/style.css">
</head>
<body id="body">
<section class="container loadClick mb-5">
<button type="button" onclick="loadDocumentCsv()" class="btn loadDocument font-weight-bold">Wyświetl wszystkie tabele</button>
</section>
<section class="container">
<h2>1. Wszystkie samochody</h2>
<div id="allRecords"></div>
</section>
<section class="container mt-5">
<h2>2. Samochody gdzie Isofix === TRUE</h2>
<div id="isofixTrue"></div>
</section>
<section class="container mt-5">
<h2>3. Posortowane samochody wg Marki</h2>
<div id="brandSort"></div>
</section>
<section class="container mt-5">
<h2>4. Samochody gdzie Isofix === FALSE, posortowane wg przebiegu od najmniejszego do największego</h2>
<div id="sortMileageAndIsofixFalse"></div>
</section>
<div id="fixed">
<i class="uis uis-arrow-circle-up"></i>
</div>
<script src="./index.js"></script>
</body>
</html>
The CSV file looks like this:
Brand,Model,Year of production,Mileage,Isofix
Alfa Romeo,156,1998,600 000,TRUE
Chrysler,Voyager,2007,342 121,FALSE
Smart,Cabrio,2005,422122,TRUE
Fiat,500,2001 unkown,121333,FALSE
Chrysler,Voyager,2005,421 325,FALSE
Smart,Liftback,2015,534531,TRUE

Categories