let questions = [
{
title: "Commonly used data types DO NOT include:",
choices: ["strings", "booleans", "alerts", "numbers"],
answer: "alerts"
},
{
title: "The condition in an if / else statement is enclosed within ____.",
choices: ["quotes", "curly brackets", "parentheses", "square brackets"],
answer: "parentheses"
}
]
let main = document.getElementById('main');
let content = document.getElementById('content');
let title = document.getElementById('quiz-title');
let instr = document.getElementById('instructions');
let btn = document.getElementById('start-button');
let time = document.getElementById('time');
let h2;
let btns;
let newElement;
let correct;
let questionCounter;
let seconds;
const timer = () => {
time.textContent = seconds;
seconds--;
if (seconds < 0) {
clearInterval(timerStart);
questionCounter = 4;
}
}
var timerStart = setInterval(timer, 1000);
const startQuiz = () => {
title.style.display = 'none';
instr.style.display = 'none';
btn.style.display = 'none';
questionCounter = 0;
newElement = document.createElement('h2');
newElement.textContent = questions[questionCounter].title;
newElement.id = questionCounter;
content.appendChild(newElement);
questions[questionCounter].choices.forEach(function(choice) {
newElement = document.createElement('button');
newElement.textContent = choice;
newElement.setAttribute('class', 'btn btn-success btn-block');
newElement.id = choice;
content.appendChild(newElement);
})
seconds = 10;
timerStart;
};
const advanceQuestion = () => {
document.getElementById(`${questionCounter}-answered`).style.display = 'none';
questionCounter++;
if (questionCounter < 2) {
newElement = document.createElement('h2');
newElement.textContent = questions[questionCounter].title;
newElement.id = questionCounter;
content.appendChild(newElement);
questions[questionCounter].choices.forEach(function(choice) {
newElement = document.createElement('button');
newElement.textContent = choice;
newElement.setAttribute('class', 'btn btn-success btn-block');
newElement.id = choice;
content.appendChild(newElement);
})
} else {
newElement = document.createElement('h2');
newElement.textContent = 'Good job!';
content.appendChild(newElement);
newElement = document.createElement('p');
newElement.textContent = `Your score is ${time.innerHTML}`;
content.appendChild(newElement);
}
}
const checkAnswer = (Event) => {
if (questions[questionCounter].choices.indexOf(Event.target.id) == -1) {
return;
} else {
newElement = document.createElement('h4');
newElement.textContent = 'Answered';
newElement.id = `${questionCounter}-answered`;
content.appendChild(newElement);
}
h2 = document.getElementById(questionCounter)
h2.style.display = 'none';
for (let i = 0; i < questions[questionCounter].choices.length; i++) {
btns = document.getElementById(questions[questionCounter].choices[i]);
btns.style.display = 'none';
}
setTimeout(advanceQuestion, 1000);
}
btn.onclick = startQuiz;
document.onclick = checkAnswer;
main {
text-align: center;
}
section {
width: 80%;
margin: 0 auto;
}
.btn {
margin: 0.5rem 0;
}
#timer {
position: absolute;
top: 0;
right: 0;
}
<header>
<h6 id="high-scores">View Highscores</h6>
<h6 id="timer">Time: <span id="time">0</span></h6>
</header>
<main id="main">
<section id="content">
<h1 id="quiz-title">Coding Quiz Challenge</h1>
<p id="instructions">Try to answer the following code related questions within the time limit. Keep in mind that incorrect answers will penalize your score/time by ten seconds!</p>
<button id="start-button" type="button" class="btn btn-success">Begin Quiz</button>
</section>
</main>
I am new to JavaScript and have been tasked with writing a code quiz application (no jQuery allowed). The application works as follows: the questions are loaded one at a time as answered and a question counter is incremented as it goes. Once the question counter hits the threshold, the quiz ends and the score is displayed. I have added a timer and there is no issue if the quiz is finished within the time given but if the timer reaches 0, the whole application stops working after the clearInterval runs.
const timer = () => {
time.textContent = seconds;
seconds--;
if (seconds < 0) {
clearInterval(timerStart);
questionCounter = 4;
}
}
const timerStart = setInterval(timer, 1000);
The timer is set to 75 seconds in the calling function. The application advances based on the onclick method for what it's worth, and that seems to stop taking inputs after the clearInterval() call. I can post the rest of the code if needed but it is a bit verbose as I am new to this.
EDIT
const advanceQuestion = () => {
if (correct === false) {
document.getElementById(`${questionCounter}-incorrect`).style.display = 'none';
} else {
document.getElementById(`${questionCounter}-correct`).style.display = 'none';
}
questionCounter++;
if (questionCounter < 5) {
newElement = document.createElement('h2');
content.appendChild(newElement);
questions[questionCounter].choices.forEach(function(choice) {
newElement = document.createElement('button');
content.appendChild(newElement);
})
} else {
newElement = document.createElement('h2');
newElement.textContent = 'Good job!';
content.appendChild(newElement);
newElement = document.createElement('p');
newElement.textContent = `Your score is ${time.innerHTML}`;
content.appendChild(newElement);
}
const checkAnswer = (Event) => {
if (Event.target.id === questions[questionCounter].answer) {
newElement = document.createElement('h4');
newElement.textContent = 'Correct!';
content.appendChild(newElement);
} else {
newElement = document.createElement('h4');
newElement.textContent = 'Incorrect';
content.appendChild(newElement);
}
h2 = document.getElementById(questionCounter)
h2.style.display = 'none';
for (let i = 0; i < questions[questionCounter].choices.length; i++) {
btns = document.getElementById(questions[questionCounter].choices[i]);
btns.style.display = 'none';
}
setTimeout(advanceQuestion, 1000);
}
const startQuiz = () => {
title.style.display = 'none';
questionCounter = 0;
newElement = document.createElement('h2');
content.appendChild(newElement);
questions[questionCounter].choices.forEach(function(choice) {
newElement = document.createElement('button');
content.appendChild(newElement);
})
seconds = 10;
timerStart;
btn.onclick = startQuiz;
document.onclick = checkAnswer;
Related
My pagination System currently looks like this:
What my pagination system currently looks like
However, I only want 10 page links to appear at a time. Instead of all of the links.
I have this code:
const nav = document.getElementById('nav');
const content = document.getElementById('content');
let pageIndex = 0;
let itemsPerPage = 3;
let finalPage = Math.ceil(items.length / itemsPerPage) - 1;
console.log(finalPage)
loadItems();
function loadItems() {
content.innerHTML = "";
for (let i = pageIndex * itemsPerPage; i < (pageIndex * itemsPerPage) + itemsPerPage; i++) {
if (!items[i]) break;
const item = document.createElement('div');
item.innerHTML = `
<div>
<img src="${items[i].src}"/>
</div>
<div>
<button>${items[i].desc}</button>
</div>
`;
content.append(item);
}
firstPage();
minusPage();
loadPageNav();
plusPage();
lastPage();
}
function loadPageNav() {
for (let i = 0; i < items.length / itemsPerPage; i++) {
const button = document.createElement('button');
button.classList.add('btn');
button.innerHTML = i + 1;
button.addEventListener('click', (e) => {
pageIndex = e.target.innerHTML - 1;
loadItems();
});
nav.append(button);
}
}
function plusPage() {
const button = document.createElement('button');
button.classList.add('btn');
button.innerHTML = "›";
button.addEventListener('click', () => {
pageIndex = pageIndex + 1;
loadItems();
});
nav.append(button);
if (pageIndex === finalPage) {
button.disabled = true;
}
}
function minusPage() {
const button = document.createElement('button');
button.classList.add('btn');
button.innerHTML = "‹";
button.addEventListener('click', () => {
pageIndex = pageIndex - 1;
loadItems();
});
nav.append(button);
if (pageIndex === 0) {
button.disabled = true;
}
}
function firstPage() {
nav.innerHTML = "";
const button = document.createElement('button');
button.classList.add('btn');
button.innerHTML = "«";
button.addEventListener('click', () => {
pageIndex = 0;
loadItems();
});
nav.append(button);
if (pageIndex === 0) {
button.disabled = true;
}
}
function lastPage() {
const button = document.createElement('button');
button.classList.add('btn');
button.innerHTML = "»";
button.addEventListener('click', () => {
pageIndex = finalPage;
loadItems();
});
nav.append(button);
if (pageIndex === finalPage) {
button.disabled = true;
}
}
This code produces the output shown above. However, I want to produce this output:
What my pagination system should look like
From my understanding, the loadPageNav function is the one that draws your page numbers. Pay attention to the maximum value your for loop goes too.
For example if you have 100 items (items.length = 100) and want to display only 5 items per page then
items.length / itemsPerPage is 100 / 5 = 20.
You have 20 pages and in your loop you create and add a button for each of those. If you want to limit the number of buttons drawn, then you need to limit the maximum value you assign to i.
I made an image slider that work, but now I'm trying to add an animation effect to it, and I believe JavaScript may be the answer for that.
I'm trying to add the possibility to click the arrow buttons and have the image slide left or right depending on whether the left or right arrow is being clicked.
Is that possible.
Here is my HTML file.
<body>
<div id="hcg-slider-1" class="hcg-slider">
<div class="hcg-slide-container">
<div class="hcg-slider-body">
<a class="hcg-slides animated" style="display:block">
<span class="hcg-slide-number">1/5</span>
<img src="https://www.html-code-generator.com/images/slider/1.png" alt="image 1">
<span class="hcg-slide-text">image 1</span>
</a>
</div>
<a class="hcg-slide-prev" href="#">❮</a>
<a class="hcg-slide-next" href="#">❯</a>
</div>
<div class="hcg-slide-dot-control"></div>
</div>
<script>
(function(){
//If you want to include more images, add the link name and URL of the image in the array list below.
let images_list = [
{"url":"photos/headers/ABY-header.png",
"link":"",
"name": "just text"},
{"url":"photos/headers/TMN-header.png",
"link":"",
"name": "just text"},
{"url":"photos/headers/TW-header.png",
"link":"",
"name": "just text"},
{"url":"photos/headers/NY-header.png",
"link":"",
"name": "just text"},
];
let slider_id = document.querySelector("#hcg-slider-1");
// append all images
let dots_div = "";
let images_div = "";
for (let i = 0; i < images_list.length; i++) {
// if no link without href="" tag
let href = (images_list[i].link == "" ? "":' href="'+images_list[i].link+'"');
images_div += '<a'+href+' class="hcg-slides animated"'+(i === 0 ? ' style="display:block"':'')+'>'+
'<span class="hcg-slide-number">'+(i+1)+'/'+images_list.length+'</span>'+
'<img src="'+images_list[i].url+'" alt="'+images_list[i].name+'">'+
'<span class="hcg-slide-text">'+images_list[i].name+'</span>'+
'</a>';
dots_div += '<span class="hcg-slide-dot'+(i === 0 ? ' dot-active':'')+'" data-id="'+i+'"></span>';
}
slider_id.querySelector(".hcg-slider-body").innerHTML = images_div;
slider_id.querySelector(".hcg-slide-dot-control").innerHTML = dots_div;
let slide_index = 0;
let images = slider_id.querySelectorAll(".hcg-slides");
let dots = slider_id.querySelectorAll(".hcg-slide-dot");
let prev_button = slider_id.querySelector(".hcg-slide-prev");
let next_button = slider_id.querySelector(".hcg-slide-next");
function showSlides() {
if (slide_index > images.length-1) {
slide_index = 0;
}
if (slide_index < 0) {
slide_index = images.length-1;
}
for (let i = 0; i < images.length; i++) {
images[i].style.display = "none";
dots[i].classList.remove("dot-active");
if (i == slide_index) {
images[i].style.display = "block";
dots[i].classList.add("dot-active");
}
}
}
prev_button.addEventListener("click", function(event) {
event.preventDefault();
slide_index--;
showSlides();
}, false);
next_button.addEventListener("click", function(event){
event.preventDefault();
slide_index++;
showSlides();
}, false);
function dot_click(event) {
slide_index = event.target.dataset.id;
showSlides();
}
for (let i = 0; i < dots.length; i++) {
dots[i].addEventListener("click", dot_click, false);
}
})();
</script>
I managed to add a sliding animation using JavaScript. Here's a good guide as to how it can be done.
https://www.cssscript.com/animated-image-slider/
This is the code I added.
const content = document.querySelector(".content");
const slider = document.querySelector(".slider");
const sliderImage = Array.from(document.querySelectorAll(".slider-image"));
const btnChevron = document.querySelectorAll(".btn-chevron");
let i = 0;
let reset = (container, clase) => {
container.forEach(item => item.classList.remove(clase));
}
let createInfo = text => {
const sliderInfo = document.createElement("p");
sliderInfo.className = "slider-info";
sliderInfo.textContent = text;
content.appendChild(sliderInfo);
};
let createIndicators = () => {
const container = document.createElement("div");
container.className = "indicator";
content.appendChild(container)
sliderImage.forEach(image => {
let indicator = document.createElement("p");
indicator.textContent = sliderImage.indexOf(image) + 1;
container.appendChild(indicator);
})
}
let Image = (index) => {
const indicators = document.querySelectorAll('.indicator p');
const sliderInfo = document.querySelector('.slider-info');
sliderImage[index].classList.add('slider-image-active');
reset(indicators, 'indicator-active');
indicators[i].classList.add('indicator-active');
if (content.hasElement(".slider-info")) return sliderInfo.textContent = sliderImage[index].dataset.info;
createInfo(sliderImage[index].dataset.info);
}
let setPosition = (index) => {
let width = sliderImage[index].getBoundingClientRect().width;
slider.style.transform = `translateX(-${width * index}px)`;
}
let moveImage = () => {
if (i === sliderImage.length) {
i = 0; // Si el contador ya llego al ultimo item, lo manda al primer item.
} else if (i == -1) {
i = sliderImage.length - 1; // Si llego al primero lo manda hasta el ultimo.
}
reset(sliderImage, 'slider-image-active');
setPosition(i);
Image(i);
};
btnChevron.forEach(btn => {
btn.addEventListener('click', () => {
if (btn.dataset.action == "right") {
i++;
return moveImage();
}
i--;
return moveImage();
})
})
createIndicators();
Image(i);
I am trying to disable the "decrease" when the counter goes below 0 and re-enable it, when the counter goes up.
But when I press the "increase" button the "decrease" button stays disable.
Here is what I have so far:
let countEl = document.getElementById('count')
let decreaseEl = document.getElementById('decrease')
let resetEl = document.getElementById('reset')
let increaseEl = document.getElementById('increase')
let saveEl = document.getElementById("save")
let savedEl = document.getElementById('savedCount')
let message = "Invalid Value";
let invalid = false
let countNo = 0;
decreaseEl.addEventListener('click', function(){
countNo--;
if(countNo < 0){
countEl.textContent = message;
invalid = true
decreaseEl.disabled = true;
}else{
countEl.textContent = countNo;
invalid = false
if(invalid = false){
decreaseEl.disabled = false;
}
}
})
resetEl.addEventListener('click', function(){
countEl.textContent = 0;
countNo = 0;
savedEl.textContent = "Counts:";
})
increaseEl.addEventListener('click',function(){
countNo++;
countEl.textContent = countNo;
invalid = false
})
saveEl.addEventListener('click',function(){
if(invalid == true){
savedEl.textContent += "";
}else{
savedEl.textContent += " " + countNo + "-";
countNo = 0;
countEl.textContent = 0;
}
})
Cause you are added event listener on the button, you are disabling and undisabling the button inside the event listener.
You should do something like this:
increaseEl.addEventListener('click', function() {
countNo++;
countEl.textContent = countNo;
invalid = false;
if (countNo > 0) {
decreaseEl.disabled = false;
}
});
I would expect something like this:
let countEl = document.getElementById('count')
let decreaseEl = document.getElementById('decrease')
let resetEl = document.getElementById('reset')
let increaseEl = document.getElementById('increase')
let saveEl = document.getElementById("save")
let savedEl = document.getElementById('savedCount')
let message = "Invalid Value";
let invalid = false
let countNo = 0;
decreaseEl.addEventListener('click', function() {
countNo--;
const neg = countNo < 0;
countEl.textContent = neg ? message : countNo;
decreaseEl.disabled = neg;
if (neg) setTimeout(function() {
countNo = 0;
countEl.textContent = countNo
}, 2000); // optional
})
resetEl.addEventListener('click', function() {
countEl.textContent = 0;
countNo = 0;
savedEl.textContent = "Counts:";
})
increaseEl.addEventListener('click', function() {
countNo++;
countEl.textContent = countNo;
decreaseEl.disabled = countNo <= 0;
invalid = false
})
<span id="count">0</span>
<button id="increase">Increase</button>
<button id="decrease">Decrease</button>
<button id="reset">Reset</button>
<button id="save">Save</button>
<span id="savedCount">0</span>
Inside increaseEl.addEventListener add if(countNo=>0) decreaseEl.disabled = false;
let countEl = document.getElementById('count')
let decreaseEl = document.getElementById('decrease')
let resetEl = document.getElementById('reset')
let increaseEl = document.getElementById('increase')
let message = "Invalid Value";
let invalid = false
let countNo = 0;
decreaseEl.addEventListener('click', function(){
countNo--;
if(countNo <0){
countEl.textContent = message;
invalid = true
decreaseEl.disabled = true;
}else{
countEl.textContent = countNo;
invalid = false
if(invalid = false){
decreaseEl.disabled = false;
}
}
})
increaseEl.addEventListener('click',function(){
countNo++;
if(countNo=>0) decreaseEl.disabled = false;
countEl.textContent = countNo;
invalid = false
})
<button id="count">0</button>
<button id="increase">Increase</button>
<button id="decrease">Decrease</button>
As you have your answer as to why your doesn't work you may find this example useful/interesting. It's based from code I wrote as an answer to a similar question a couple of days ago.
All of the information about how it works is in that link, but basically it provides you with a way to handle the buttons from one listener (on the container), and there's no need for a global count variable.
// Cache the elements
const counterElement = document.querySelector('#counterValue');
const container = document.querySelector('#container');
const decrease = document.querySelector('button[data-type="decrease"]');
// Add a listener to the container
container.addEventListener('click', handleClick(), false);
// Initialise the count variable
function handleClick(count = 0) {
// Return a new function (the closure)
// as the function that will be called when
// the buttons are clicked
return function(e) {
// Get the button type from the clicked button
const { type } = e.target.dataset;
if (type === 'decrease') --count;
if (type === 'increase') {
// If the decrease button is disabled
// re-enable it
if (decrease.disabled) {
decrease.disabled = false;
}
++count;
}
// Disable the decrease button if it's zero
if (count === 0) decrease.disabled = true;
counterElement.textContent = count;
}
}
.counter-value {font-size: 36px; font-weight: 900; }
.button {color: #ffffff; background-color: #0967d2; font-size: 14px; border-width: 0; border-radius: 4px; padding: 10px;}
button { cursor: pointer; }
button:disabled { opacity: 30%; cursor: default;}
<p id="counterValue" class="counter-value">0</p>
<div id="container">
<button data-type="decrease" class="button" disabled>DECREASE</button>
<button data-type="increase" class="button">INCREASE</button>
</div>
The issue that you are having curretly is, you are settting the disabled status of your #decrease button onli inside the click event of #decrease button. This works in setting the disabled status of button to true.
You are setting the disabled status of the button to false inside the else statement of countNo < 0 inside the click event of #decrease button. Does this have any impact on your functionality? NO!!!. Why? Because when the number is negative, the button will be disabled and the click event wont trigger and the disable status wont be reset to false.
The enabling of the button should be handled in the #increase button click event instead. You can directly rest the disabled status to false if the button is disabled. Because the count wont go negative inside this function.
Working Fiddle
let countEl = document.getElementById('count')
let decreaseEl = document.getElementById('decrease')
let resetEl = document.getElementById('reset')
let increaseEl = document.getElementById('increase')
let saveEl = document.getElementById("save")
let savedEl = document.getElementById('savedCount')
let message = "Invalid Value";
let invalid = false
let countNo = 0;
decreaseEl.addEventListener('click', function () {
countNo--;
if (countNo < 0) {
countEl.textContent = message;
invalid = true
decreaseEl.disabled = true;
} else {
countEl.textContent = countNo;
invalid = false
// This doesnot have any impact
// if (invalid = false) {
// decreaseEl.disabled = false;
// }
}
})
resetEl.addEventListener('click', function () {
countEl.textContent = 0;
countNo = 0;
savedEl.textContent = "Counts:";
})
increaseEl.addEventListener('click', function () {
countNo++;
countEl.textContent = countNo;
invalid = false
if (decreaseEl.disabled) {
decreaseEl.disabled = !decreaseEl.disabled;
}
})
saveEl.addEventListener('click', function () {
if (invalid == true) {
savedEl.textContent += "";
} else {
savedEl.textContent += " " + countNo + "-";
countNo = 0;
countEl.textContent = 0;
}
})
<button id="count">0</button>
<button id="increase">Increase</button>
<button id="decrease">Decrease</button>
<button id="reset">Reset</button>
<button id="save">Save</button>
<p id="savedCount"></p>
My goal is to display two images that last for 5 seconds each at the beginning, then images from a different folder will display for 3 seconds each. I currently display the images from the folder using set Interval and am trying to display the initial two images at the beginning using setTimeout. Right now when I run this, the page shows the code I have for the setInterval at the top of the page and the second image (fun-src) from my setTimeout blocks below. The struc-src image does not show at all. How can I align these two images with the rest of the images and make them appear only for 5 seconds and disappear after? I am new to javascript so I would appreciate any help with how to achieve this. Thank you.
<!DOCTYPE html>
<html>
<section id="img-container"></section>
<img id="Scan" struc-src="img1.png" fun-src="img2.png"/>
<script type="text/javascript">
//my attempt to display the two pictures
var scans = document.getElementById("Scan");
var strucScan = scans.getAttribute("struc-src");
var funScan = scans.getAttribute("fun-src");
scans.src = structScan;
scans.style.display = "block";
setTimeout(() => {
scans.style.display = "none";
scans.src = funcScan;
scans.style.display = "block";
}, 5000);
//everything below this line works
const numOfPictures = 200;
const picturesNumberLength = 3;
let imageIndex = 1;
let imagesArray = [];
const imagesContainer = document.getElementById("img-container");
for (let i = 1; i < numOfPictures + 1; i++) {
const img = document.createElement("img");
img.src = `foldername/homeFolder_${(i+"").padStart(picturesNumberLength,"0")}.png`;
img.classList.add("slides");
img.style.width = "80%";
img.style.display = "none";
imagesContainer.appendChild(img);
imagesArray.push(img);
}
imagesArray[0].style.display = "block";
setInterval(() => {
imagesArray[imageIndex].style.display = "block";
if (imageIndex > 0) imagesArray[imageIndex-1].style.display = "none";
else imagesArray[numOfPictures-1].style.display = "none";
imageIndex++;
if (imageIndex >= numOfPictures) imageIndex = 0;
}, 3000);
</script>
</html>
You just need an extra timeout.
0s: scans.src = structScan;
5s: scans.src = funScan;
10s: scans.style.display = "none"; imagesArray[0].style.display = "block";
var scans = document.getElementById("Scan");
var structScan = scans.getAttribute("struc-src");
var funScan = scans.getAttribute("fun-src");
scans.src = structScan;
setTimeout(() => {
scans.src = funScan;
}, 5000);
//everything below this line works
const numOfPictures = 5;
const picturesNumberLength = 3;
let imageIndex = 1;
let imagesArray = [];
const imagesContainer = document.getElementById("img-container");
for (let i = 1; i < numOfPictures + 1; i++) {
const img = document.createElement("img");
img.src = `https://via.placeholder.com/150x150&text=home${(i+"").padStart(picturesNumberLength,"0")}.png`;
img.classList.add("slides");
img.style.display = "none";
imagesContainer.appendChild(img);
imagesArray.push(img);
}
setTimeout(() => {
scans.style.display = "none";
imagesArray[0].style.display = "block";
setInterval(() => {
imagesArray[imageIndex].style.display = "block";
if (imageIndex > 0) imagesArray[imageIndex-1].style.display = "none";
else imagesArray[numOfPictures-1].style.display = "none";
imageIndex++;
if (imageIndex >= numOfPictures) imageIndex = 0;
}, 3000);
}, 10000);
<section id="img-container"></section>
<img id="Scan" struc-src="https://via.placeholder.com/150x150&text=img1.png" fun-src="https://via.placeholder.com/150x150&text=img2.png"/>
Move html outside of the script tag. You should add
<img id="Scan" struc-src="img1.png" fun-src="img2.png"/> underneath or within your section above.
Here are the relevant bits of the client-side code:
function simpsonsShow(forest) {
alert(document.getElementById("simpsons"));
var ind = simpsonsIndex(forest).toFixed(2); // simpsonsIndex is a function not shown here
document.getElementById("simpsons").innerHTML = "";
document.getElementById("simpsons").innerHTML =
document.getElementById("simpsons").innerHTML + ind;
}
document.addEventListener("DOMContentLoaded", function () {
document.querySelector("div#intro button").addEventListener("click", function clicked() {
document.getElementById("intro").style.display = "none";
document.getElementById("sim").style.display = "block";
document.getElementById("simpsons").style.display = "block";
let content = document.getElementById("inputForest").value;
let forest = forestGenerate(content);
const ind = simpsonsShow(forest);
let button = document.createElement("button");
button.appendChild(document.createTextNode("generate"));
button.addEventListener("click", function () {
forest = forestGenerate(content);
simpsonsShow(forest);
});
document.getElementById("sim").appendChild(button);
});
});
When that simpsonsShow function is ran a second time, all of a sudden document.getElementById("simpsons") becomes null even though upon first try, it's a proper HTML Div Element.
Here are the relevant parts of the HTML:
<head>
<script src="sim.js"></script>
</head>
<body>
<div id="content">
<div id="intro">
</div>
<div id="sim" class="hidden">
<h2>the current Simpson's Index is:
</h2>
<div id="simpsons">
</div>
</div>
</div><!--close id="content"-->
</body>
</html>
I've added the code snippet: The website works by pressing generate, then continually pressing generate. The error pops up once you press generate a 2nd time
function forestGenerate(content) {
const forest = [];
if (content.length === 0) {
const possible = ["", "🌲", "🌳", "🌴", "🌵", "🌶", "🌷", "🌸", "🌹", "🌺", "🌻", "🌼", "🌽", "🌾", "🌿", "🍀", "🍁", "🍂", "🍃"];
for (let i = 0; i < 8; i++) {
let text = '';
for (let i = 0; i < 8; i++) {
text += possible[Math.floor(Math.random() * possible.length)];
}
forest.push(text);
}
}
else {
const possible = [...content, ""];
for (let i = 0; i < 8; i++) {
let text = '';
for (let i = 0; i < 8; i++) {
text += possible[Math.floor(Math.random() * possible.length)];
}
forest.push(text);
}
}
for (let i = 0; i < forest.length; i++) {
let row = document.createElement("div");
let newContent = document.createTextNode(forest[i]);
row.appendChild(newContent);
row.addEventListener("click", function () {
row.style.backgroundColor = "grey";
row.setAttribute("pinned", "yes");
});
document.getElementById("sim").appendChild(row);
}
return forest;
}
function simpsonsShow(forest) {
const simpsonsIndex = forest =>
1 - Object.entries(
[...forest.join("")].reduce(
(counts, emoji) => ({ ...counts, [emoji]: (counts[emoji] || 0) + 1 }),
{}
)
).reduce(([top, bottom], [species, count]) => [top + (count * (count - 1)), bottom + count], [0, 0])
.reduce((sumLilN, bigN) => sumLilN / (bigN * (bigN - 1)))
alert(document.getElementById("simpsons"));
var ind = simpsonsIndex(forest).toFixed(2);
document.getElementById("simpsons").innerHTML = "";
document.getElementById("simpsons").innerHTML = document.getElementById("simpsons").innerHTML + ind;
}
document.addEventListener("DOMContentLoaded", function () {
let element = document.getElementById("sim");
element.classList.add("hidden");
let element1 = document.getElementById("pushtray");
element1.classList.add("hidden");
document.querySelector("div#intro button").addEventListener("click", function clicked() {
document.getElementById("intro").style.display = "none";
document.getElementById("sim").style.display = "block";
document.getElementById("simpsons").style.display = "block";
let content = document.getElementById("inputForest").value;
let forest = forestGenerate(content);
const ind = simpsonsShow(forest);
if (ind <= .7) {
let over = document.createElement("div");
let newContent = document.createTextNode("WARNING: Simpson's Index Dropped To" + simpsonsIndex);
over.appendChild(newContent);
document.getElementById("pushtray").appendChild(over);
document.getElementById("pushtray").style.zIndex = "100";
document.getElementById("pushtray").style.right = "50px";
document.getElementById("pushtray").style.position = "fixed";
document.getElementById("pushtray").style.display = "block";
}
let button = document.createElement("button");
button.appendChild(document.createTextNode("generate"));
button.addEventListener("click", function () {
const curr = document.getElementById("sim").querySelectorAll("div");
for (let i = 0; i < curr.length; i++) {
if (!curr[i].hasAttribute("pinned")) {
document.getElementById("sim").removeChild(curr[i]);
}
}
document.getElementById("sim").removeChild(button);
forest = forestGenerate(content);
simpsonsShow(forest);
document.getElementById("sim").appendChild(button);
});
document.getElementById("sim").appendChild(button);
});
});
<!doctype html>
<html>
<head>
<title>FOREST SIMULATOR</title>
<script src="sim.js"></script>
<link rel="stylesheet" href="base.css" type="text/css" media="screen" title="no title" charset="utf-8">
<link href="https://fonts.googleapis.com/css?family=Lato|Playfair+Display" rel="stylesheet" >
</head>
<link href="https://fonts.googleapis.com/css?family=Lato|Playfair+Display" rel="stylesheet">
<body>
<div id="content">
<h1>FOREST SIMULATOR</h1>
<style>
.hidden{
display:none;
}
</style>
<div id="intro">
starting forest (leave empty to randomize):
<br />
<textarea id="inputForest" name="inputForest" cols="16" rows="8"></textarea>
<br />
<button>generate</button>
</div>
<div id="sim" class="hidden">
<h2>the current Simpson's Index is:
</h2>
<div id="simpsons">
</div>
</div>
<div id="pushtray" class="overlay">
</div>
</div><!--close id="content"-->
</body>
</html>
#simpsons is a child of #sim. The problem is in this code here:
const curr = document.getElementById("sim").querySelectorAll("div");
for (let i = 0; i < curr.length; i++) {
if (!curr[i].hasAttribute("pinned")) {
document.getElementById("sim").removeChild(curr[i]);
}
}
It effectively removes all div children of #sim which don't have a pinned attribute. Try removing only divs after the first index, thereby keeping #simpsons (which is the first div inside #sim):
for (let i = 1; i < curr.length; i++) {
function forestGenerate(content) {
const forest = [];
if (content.length === 0) {
const possible = ["", "🌲", "🌳", "🌴", "🌵", "🌶", "🌷", "🌸", "🌹", "🌺", "🌻", "🌼", "🌽", "🌾", "🌿", "🍀", "🍁", "🍂", "🍃"];
for (let i = 0; i < 8; i++) {
let text = '';
for (let i = 0; i < 8; i++) {
text += possible[Math.floor(Math.random() * possible.length)];
}
forest.push(text);
}
} else {
const possible = [...content, ""];
for (let i = 0; i < 8; i++) {
let text = '';
for (let i = 0; i < 8; i++) {
text += possible[Math.floor(Math.random() * possible.length)];
}
forest.push(text);
}
}
for (let i = 0; i < forest.length; i++) {
let row = document.createElement("div");
let newContent = document.createTextNode(forest[i]);
row.appendChild(newContent);
row.addEventListener("click", function() {
row.style.backgroundColor = "grey";
row.setAttribute("pinned", "yes");
});
document.getElementById("sim").appendChild(row);
}
return forest;
}
function simpsonsShow(forest) {
const simpsonsIndex = forest =>
1 - Object.entries(
[...forest.join("")].reduce(
(counts, emoji) => ({ ...counts,
[emoji]: (counts[emoji] || 0) + 1
}), {}
)
).reduce(([top, bottom], [species, count]) => [top + (count * (count - 1)), bottom + count], [0, 0])
.reduce((sumLilN, bigN) => sumLilN / (bigN * (bigN - 1)))
var ind = simpsonsIndex(forest).toFixed(2);
document.getElementById("simpsons").innerHTML = "";
document.getElementById("simpsons").innerHTML = document.getElementById("simpsons").innerHTML + ind;
}
document.addEventListener("DOMContentLoaded", function() {
let element = document.getElementById("sim");
element.classList.add("hidden");
let element1 = document.getElementById("pushtray");
element1.classList.add("hidden");
document.querySelector("div#intro button").addEventListener("click", function clicked() {
document.getElementById("intro").style.display = "none";
document.getElementById("sim").style.display = "block";
document.getElementById("simpsons").style.display = "block";
let content = document.getElementById("inputForest").value;
let forest = forestGenerate(content);
const ind = simpsonsShow(forest);
if (ind <= .7) {
let over = document.createElement("div");
let newContent = document.createTextNode("WARNING: Simpson's Index Dropped To" + simpsonsIndex);
over.appendChild(newContent);
document.getElementById("pushtray").appendChild(over);
document.getElementById("pushtray").style.zIndex = "100";
document.getElementById("pushtray").style.right = "50px";
document.getElementById("pushtray").style.position = "fixed";
document.getElementById("pushtray").style.display = "block";
}
let button = document.createElement("button");
button.appendChild(document.createTextNode("generate"));
button.addEventListener("click", function() {
const curr = document.getElementById("sim").querySelectorAll("div");
for (let i = 1; i < curr.length; i++) {
if (!curr[i].hasAttribute("pinned")) {
document.getElementById("sim").removeChild(curr[i]);
}
}
document.getElementById("sim").removeChild(button);
forest = forestGenerate(content);
simpsonsShow(forest);
document.getElementById("sim").appendChild(button);
});
document.getElementById("sim").appendChild(button);
});
});
.hidden {
display: none;
}
<div id="content">
<h1>FOREST SIMULATOR</h1>
<div id="intro">
starting forest (leave empty to randomize):
<br />
<textarea id="inputForest" name="inputForest" cols="16" rows="8"></textarea>
<br />
<button>generate</button>
</div>
<div id="sim" class="hidden">
<h2>the current Simpson's Index is:
</h2>
<div id="simpsons">
</div>
</div>
<div id="pushtray" class="overlay">
</div>
</div>