JS Function to add items to an <ul> in an HTML file - javascript

I am total beginner (1 week learning this ...and already thinking about quitting before getting crazy :D) and this is my first question, so please, if I do something wrong, just let me know.
I am trying to solve a small Javascript exercise about adding a list to a HTML file with a JS function.
I have created this function, but It is not working. I would say the problem is that I don't know how to indicate the bands name variable inside the ".createTextNode()" .
This is the function I have in a JS file :
function addBands() {
for (i = 0, i < 0, i++) {
var banda = document.createElement("LI");
var nombre= document.createTextNode([0]);
banda.appendChild(nombre);
document.getElementById("band-list").appendChild(banda)
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1> MY FAVORITE BANDS</h1>
<ul id="band-list">
</ul>
<script src="exercisesJS.js"></script>
<script>addBands(['Dire Straits', 'Kansas', 'Steely Dan'])</script>
</body>
</html>
The output should be a list with the name of the 3 bands in the Function, or any other bands (could be 3, or 6 ...etc...

Always check the error console. It's currently telling you that
for (i = 0, i < 0, i++) {
is a syntax error. You mean:
for (i = 0; i < 0; i++) {
That fixes the syntax. However that's still logically wrong, since it means your loop will never run (since the iterator variable i starts at 0, and is told to run while it is under 0, a condition it fails right from the beginning.)
Looking at your code, there's other problems. You're passing an array of band names to the function, but the function isn't set up to receive it. So, we need:
function addBands(bands) {
That means the inner part of the function can access what was passed to it. It also means we can base our loop on the number of bands that were passed, and use the iterator band as the textual output.
function addBands(bands) { //<-- receive incoming array
for (i = 0; i < bands.length; i++) { //iterate bands.length times
var banda = document.createElement("LI");
var nombre= document.createTextNode(bands[i]); //output iterator band name
banda.appendChild(nombre);
document.getElementById("band-list").appendChild(banda)
}
}
While we're here, there's a couple of other optimisations we can make. Firstly, there's no sense in freshly looking up the ul element each time the loop runs. So let's cache it outside the loop. Secondly, while createTextNode() is fine, you may be interested to know that it's easier to just use textContent on the parent node. Putting it all together:
function addBands(bands) { //<-- receive incoming array
let ul = document.getElementById("band-list"); //cache UL
for (i = 0; i < bands.length; i++) {
var banda = document.createElement("LI");
banda.textContent = bands[i];
ul.appendChild(banda)
}
}

Refer to #Utkanos's answer to exactly understand what went wrong in your code. Given that, I suggest the following solution, which uses ES2015 .forEach method of arrays, for looping over your provided list.
function addBands(liArray) {
liArray.forEach(liText => {
const li = document.createElement('li')
const liTextNode = document.createTextNode(liText)
li.appendChild(liTextNode)
document.getElementById("band-list").appendChild(li)
})
}
addBands(['Dire Straits', 'Kansas', 'Steely Dan'])
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1> MY FAVORITE BANDS</h1>
<ul id="band-list">
</ul>
<script></script>
</body>
</html>

Related

How to track clicks of a button?

I’m trying to track the clicks on a button on my website. I’ve tried adding the following but to no success. I’m a noob to JS..
function trackButton(e) {
onPage.innerHTML = ++i;
}
var i = 0;
var onPage = document.getElementById(‘track’);
var clickCount = document.getElementById(‘bttn’);
clickCount.addEventListener('click', function (e) {
addOne(e);
}, false);
You did some mistake:
The function addOne doesn't exist, it's trackbutton
it's i++ to increment a value, not ++i
And some tips for you:
Use let and const (ES6) for the variable, not var
And the e for the event is useless here, you are not using it, so it's not mandatory here
Do these change and it must work !
UPDATE:
To increment a value ++i works, see the documentation
Change the quotes ‘‘ with " " or ' '.
Like so: document.getElementById(‘track‘) to document.getElementById('track')
I was checkin your code and its almost all right, i think that the problem its in your addOne function, here is a way to resolve the problem.
i creat the button and paragraph elements in html and in javascript a variable n where we are going to storage the clicks tha the user did and we increment n when the function its called in the button's event
var n = 0;
var button = document.getElementById('track');
button.addEventListener('click', trakeo);
var texto = document.getElementById('number');
function trakeo(){
n++
texto.innerHTML = n;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A Great Demo on CodePen</title>
</head>
<body>
<button id="track">Button</button>
<p id="number"></p>
</body>
</html>
Try to use
addEventListener('click',)
https://www.w3schools.com/jsref/met_element_addeventlistener.asp

.innerHTML: Cannot set property 'innerHTML' of null

First, I am completely new to coding and have been using self-teaching tools to learn Javascript in my free time. I've learned enough to start building my own projects. My first attempt is to build a randomizer (in this case, random restaurant names). The Javascript works through my tests in the console as do the buttons. However, I cannot seem to get the .innerHTML to work and I'm not sure what I'm missing. I've done several searches here and none of the solutions I've found seem to be working.
The error I'm getting is listed in the title and it is appearing at line 29.
Here is Javascript:
var randomRestaurant = {
restaurantName: [],
findRestaurant: function() {
var restaurantName = Math.random();
if (restaurantName < 0.20) {
this.restaurantName.push("China Taste");
}
else if (restaurantName < 0.40) {
this.restaurantName.push("Pizza Johns");
}
else if (restaurantName < 0.60) {
this.restaurantName.push("Liberatore's");
}
else if (restaurantName < 0.80) {
this.restaurantName.push("Bill Bateman's");
}
else {
this.restaurantName.push("R&R Taqueria");
}
},
clearRestaurant: function() {
randomRestaurant.restaurantName.splice(0, 1);
}
};
var randomRestaurantButton = document.getElementById('randomRestaurantName');
randomRestaurantButton.addEventListener('click', function() {
randomRestaurant.findRestaurant();
document.getElementById("restaurantNameDisplay").innerHTML = randomRestaurant.restaurantName[0]; //<--line 29
});
var randomRestaurantButton = document.getElementById('refreshRestaurant');
randomRestaurantButton.addEventListener('click', function() {
randomRestaurant.clearRestaurant();
randomRestaurant.findRestaurant();
document.getElementById("restaurantNameDisplay").innerHTML = randomRestaurant.restaurantName[0];
});
And here is my HTML:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div><h1>Random Restaurant!</h1>
<button id="randomRestaurantName">Click me for a random restaurant!</button>
</div>
<br>
<h2="restaurantNameDisplay"></h2="restaurantNameDisplay">
<div>
<br>
<button id="refreshRestaurant">Nah. Give me another one.</button>
</div>
</body>
<script src="script.js"></script>
</html>
Thanks for your help and hopefully it's not due to something stupid like a typo.
There are some problems here.
the h2 tag id should be
<h2 id="restaurantNameDisplay"></h2>
your buttons are set on the same variable name, change the second to
var refreshRestaurantButton = document.getElementById('refreshRestaurant');
refreshRestaurantButton.addEventListener('click', function () {
randomRestaurant.clearRestaurant();
randomRestaurant.findRestaurant();
document.getElementById("restaurantNameDisplay").innerHTML = randomRestaurant.restaurantName[0];
});
If it's still not working, you should call your script after the page load event.
so insert your javascript code to a function (e.g. "myFunc()") and change your html body tag to:
body onload="myFunc()">
Most probably this line <h2="restaurantNameDisplay"></h2="restaurantNameDisplay"> should be
<h2 id="restaurantNameDisplay"></h2>

javascript onclick event not firing correctly

I have this click event attached to each button and when I click on each of them, it is printing the output meant for the third button. I'm not sure what is going on.
<!DOCTYPE html>
<html>
<head>
<title>JS Bin</title>
</head>
<body>
<button>test1</button>
<button>test2</button>
<button>test3</button>
</body>
<script>
var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', function() {
console.log('You clicked element #' + i);
});
}
</script>
</html>
when I click on any of the buttons, it is printing
"You clicked element #3"
Simple solution to this:
<!DOCTYPE html>
<html>
<head>
<title>JS Bin</title>
</head>
<body>
<button>test1</button>
<button>test2</button>
<button>test3</button>
</body>
<script>
var nodes = document.getElementsByTagName('button');
console.log(nodes);
for (var i = 0; i < nodes.length; i++) {
//converted click function into an IIFE so it executes then and there only
nodes[i].addEventListener('click', (function (j) {
return function () {
console.log('You clicked element #' + j);
}
})(i));
}
</script>
</html>
You should go through two concepts to understand this thing
1) Closures
2) Javascript is single-threaded and synchronous. So how does it handle events?
Here is what it is happening in your code:
==> for loop gets executed synchronously as it is part of javascript engine post which javascript handles event queue which is a FIFO (first in first out)
==> When for loop finished value of i is three which remains in memory until the function inside it executes
==> Each time it takes a value 3 and prints it.
When this button is listening to event, at that time the value of i is nodes.length -1 that is 2. Because loop has already finished it's execution and have set value of i to 2.
So it is consoling You clicked element #3.
Such issues arise because of scope & closure
Create an IIFE and pass the value of i.
Hope this snippet will be useful
var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
(function(i){
nodes[i].addEventListener('click', function() {
console.log('You clicked element #' + i);
});
}(i))
}
Check this jsfiddle
This is other way using jQuery.
$("button").each(function(e) {
$(this).data("number", e);
}).bind("click", function(e) {
console.log("You clicked element #" + $(this).data("number"));
});
https://jsfiddle.net/ChaHyukIm/uxsqu70t/3/
This is the closure with function inside a loop issue.
JavaScript closure inside loops – simple practical example
Watch out for this!
Side note: questions around this issue are frequently asked in interviews to demonstrate proficiency with JS.

Am I writing out too much data to a page with a javascript loop?

I am trying to build a subnet calculator as my own personal project to learn programming. The part I am having an issue with is I would like to present classful subnet ranges, example 172.16.1.1/30 would present 16384 subnets...
172.16.0.0 - 172.16.0.3
172.16.0.4 - 172.16.0.7
172.16.0.8 - 172.16.0.11
... etc.
So when I write it out to a website, it locks up the browser for a few seconds and takes longer than I think it should. I researched as much as I could and I found entering entering 'setTimeout' will help with the locking up and handles the ClassB subnets (maxing out at 65536 with /32). However when I go into the hundreds of thousands and millions I still have issues. I created a test script to play with numbers and see what works. Being very new to this I am out of ideas. Here is my test code...
HTML:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<div id="subnetRange" style="border : solid 2px #ff0000; background : #000000; color : #ffffff; padding : 4px; width : 250px; height : 350px; overflow : auto; "></div>
<script type="text/javascript" src="pump.js"></script>
</body>
</html>
JAVASCRIPT:
var i = 0;
var myArray = new Array();
document.getElementById('subnetRange').innerHTML = "Loading..."; //carry on pumping?
function doCalculation()
{
//Surrounding loop to break the time out up by 1000 increments
for ( var x = 0; x < 2000; x++) {
myArray[i] = i;
i = i + 1;
var percent_complete=i;
}
return percent_complete;
}
function pump()
{
var percent_complete=doCalculation();
if (percent_complete<100000)
{
//pump();
setTimeout(pump, 1);
}
if (percent_complete >= 100000) {
document.getElementById('subnetRange').innerHTML = myArray.join("<br />"); //carry on pumping?
}
}
//setTimeout(pump, 1);
pump();
I hope I have giving enough information to help out. If not please ask and I will give you information to the best of my ability.
Thank you
You're splitting the computation of doCalculation in chunks of 2000, but then once the array has a length of 100000 you do
document.getElementById('subnetRange').innerHTML = myArray.join("<br />");
That's the wrong way round. Not filling an array is the heavy work (that happens in sub-milliseconds), but the DOM manipulation. You need to take that apart, for example like this:
function pump() {
var oldItemCount = myArray.length;
var percent_complete = doCalculation();
var newItems = myArray.slice(oldItemCount);
document.getElementById('subnetRange').innerHTML += newItems.join("<br />");
// ^^^^^^^^^^^
if (percent_complete < 100000) {
setTimeout(pump, 1);
}
}
(updated Demo)
Notice the code above is only for understanding the issue, further improvements could be made: newItems could be the return value of doCalculation, the slicing would become unnecessary then. And the innerHTML += might actually slow down older browsers which cannot optimize this (they serialize the DOM, do the string concatenation, and parse the huge HTML string again). You may need to find a better way to append small chunks of DOM elements.
setTimeout takes miliseconds... maybe increase it from 1 to like 25 or 35?
setTimeout(pump, 35);

MooTools/JavaScript variable scope

I am trying to make each number displayed clickable. "1" should alert() 80, "2" should produce 60, etc.
However, when the alert(adjust) is called, it only shows 0, not the correct numbers. However, if the commented out alert(adjust) is uncommented, it produces the correct number on page load, but not on clicking.
I was wondering why the code inside addEvents cannot access the previously defined variable adjust.
<html>
<head>
<script type="text/javascript" charset="utf-8" src="mootools.js"></script>
<script type="text/javascript" charset="utf-8">
window.addEvent('domready', function() {
var id_numbers = [1,2,3,4,5];
for(var i = 0; i<id_numbers.length; i++) {
var adjust = (20 * (5 - id_numbers[i]));
// alert(adjust);
$('i_' + id_numbers[i]).addEvents({
'click': function() {
alert(adjust);
}
});
}
});
</script>
</head>
<body>
<div id="i_1">1</div>
<div id="i_2">2</div>
<div id="i_3">3</div>
<div id="i_4">4</div>
<div id="i_5">5</div>
</body>
</html>
Thanks.
You are having a very common closure problem in that for loop.
Variables enclosed in a closure share the same single environment, so by the time the click callbacks are called, the for loop would have run its course, and the adjust variable will be left pointing to the last value it was assigned.
You can solve this with even more closures, using a function factory:
function makeClickHandler(adjust) {
return function() {
alert(adjust);
};
}
// ...
for(var i = 0; i<id_numbers.length; i++) {
var adjust = (20 * (5 - id_numbers[i]));
$('i_' + id_numbers[i]).addEvents({
'click': makeClickHandler(adjust)
});
}
This can be quite a tricky topic, if you are not familiar with how closures work. You may to check out the following Mozilla article for a brief introduction:
Mozilla Dev Center: Working with Closures

Categories