Simple Vanilla JS game - javascript

I tried write simple vanilla JS memories game. I have a problem when i tried use query selector for divs which are created in Square class. Console.log returns empty nodelist or array. I suspect this function is done before divs are created. How Can I gain this elementy by query selector or another method ?
// CLASS SQUARE
export class Square {
constructor (number,type,field) {
this.number = number;
this.type = type;
this.field = field;
this.colors = ['red','red','blue','blue','orange','orange','green','green','gold','gold','pink','pink','cadetblue','cadetblue','purple','purple','beige','beige','cyan','cyan']
this.divs = [];
}
creator() {
for(let i = 0; i < this.number; i++) {
let create = document.createElement(this.type)
this.field.appendChild(create)
create.classList.add('square')
create.setAttribute('id','square')
this.divs.push(create);
console.log(this.divs)
}
}
addColor() {
this.divs.forEach((div)=>{
let chooseColor = Math.floor(Math.random()* this.colors.length);
div.style.backgroundColor = this.colors[chooseColor]
this.colors.splice(chooseColor,1)
})
}
addShadow() {
this.divs.forEach((div)=>{
const shadow = () => {
div.classList.add('shadow')
}
setTimeout(shadow,2000)
})
}
}
// CLASS SHADOW
export class Shadow {
constructor(shadowDivs) {
this.shadowDivs = shadowDivs;
}
revealDiv() { // THERE IS MY PROBLEM
this.shadowDivs.forEach((shadowDiv)=>{
shadowDiv.addEventListener('click',()=>{
console.log(this.shadowDivs)
shadowDiv.classList.remove('shadow')
})
})
}
}
//MAIN JS FILE
import { Square } from "./Square.js";
import { Shadow } from "./Shadow.js";
class Game {
elements = {
number: 20,
type: 'div',
field: document.getElementById('field'),
divs: document.querySelectorAll('.shadow') //THERE I TRY GAIN ELEMENTS
}
init() {
const squareCreator = new Square(this.elements.number,this.elements.type,this.elements.field);
const shadows = new Shadow(this.elements.divs) // AND THERE I CREATE NEW SHADOW
squareCreator.creator();
squareCreator.addColor();
squareCreator.addShadow();
// squareCreator.revealDiv()
shadows.revealDiv()
}
}
const game = new Game();
game.init()
<!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="style.css">
</head>
<body>
<div class="gameField" id='field'></div>
<script src="js/script.js" type="module"></script>
</body>
</html>

You can simply do this:
addEventListener("DOMContentLoaded", () => {
game.init();
});

Remove the timeout
addShadow() {
this.divs.forEach((div)=>{
div.classList.add('shadow')
})
}
Reorder the ini() and use the querySelector after the addShadow else there will be nothing to select.
init() {
const squareCreator = new Square(this.elements.number,this.elements.type,this.elements.field);
squareCreator.creator();
squareCreator.addColor();
squareCreator.addShadow();
const shadows = new Shadow(document.querySelectorAll('.shadow')) // AND THERE I CREATE NEW SHADOW
shadows.revealDiv()
}

Related

Handle events with a class

I like to handle events within a class. That is, I need to access my instance in the event handler and use data encapsulated within the instance. When an event is triggered, this becomes the HTMLElement as expected. I could not find a nice method to reach the instance associated with the element.
A global variable does not help if there are two instances of the class assigned to two buttons. In this sample code button B1 increases p2 rather than p1.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="increment.js"></script>
</head>
<body>
<p>p1: <span id="idP1">1</span></p>
<p>p2: <span id="idP2">2</span></p>
<button id="idB1">B1</button>
<button id="idB2">B2</button>
<script>
const p1 = document.querySelector("#idP1");
const p2 = document.querySelector("#idP2");
const b1 = document.querySelector("#idB1");
const b2 = document.querySelector("#idB2");
const i1 = new Increment(p1, b1, 100);
const i2 = new Increment(p2, b2, 200);
</script>
</body>
</html>
let increment: Increment;
class Increment {
target: HTMLElement;
data = 0;
constructor(target: HTMLElement, button: HTMLElement, initialValue: number) {
increment = this; // problem
this.target = target;
this.data = initialValue;
this.someDataUpdate();
button.addEventListener("click", this.eventHandler);
}
eventHandler(e: Event) {
console.log("e.target:", e.target);
console.log("this:", this);
console.log("increment.date:", increment.data);
increment.someDataUpdate();
// this.plusOne(); // produces error
}
someDataUpdate() {
this.data += 2;
this.target.innerHTML = (this.data).toString();
}
}

How to make a function that creates toggle buttons for a setTimeout loop?

I am trying to create a function that lets me create an on/off toggle button for setTimeout loops and I am having difficulty figuring out how to get this to work the way I want it. The only way I was able to get the loop to stop was by declaring a variable in the global scope that stores the Id value for the setTimeout loop but which defeats the purpose. This also causes an issue if I try to create a second toggle button for a different loop as all the buttons access the same variable. Help would be appreciated. Thank you
<!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>
<div id="mainContainer">
<h1 id='pointA'>Point A</h1>
<p id='pointB'>Point B</p>
</div>
</body>
<script>
function createCustomToolbar(){
var myToolbarCustom = document.createElement('div');
var targetPage= document.querySelector('body');
var myCSS=myToolbarCustom.style;
myToolbarCustom.id='myToolbarCustom';
targetPage.append(myToolbarCustom);
targetPage.style.padding='transform 0.4s ease';
targetPage.style.padding='40px';
myCSS.position='fixed';
myCSS.top=0;
myCSS.left=0;
myCSS.width='100%';
myCSS.height='35px';
myCSS.border=0;
myCSS.background='#e3e3e3'
myCSS.zIndex=9999999;
}
var x;
function toggleCreator(bttnName,callback,targetEle){
var state=false;
var mybttn=document.createElement('button');
mybttn.className='myCustomTog';
mybttn.innerHTML=bttnName;
var bttnTarget=document.getElementById('myToolbarCustom');
bttnTarget.appendChild(mybttn)
mybttn.onclick = function() {
state = !state;
if (state) {
mybttn.innerHTML = bttnName + " ON";
x = callback(targetEle);
} else {
mybttn.innerHTML = bttnName + " OFF";
clearTimeout(x);
}}
}
createCustomToolbar();
toggleCreator("start",testToggle,document.getElementById('pointA'));
toggleCreator("start",testToggle,document.getElementById('pointB'));
var i=0;
function testToggle(myTarget){
x= setTimeout(function(){
myTarget.innerHTML=i;
i++;
testToggle(myTarget);
},1000)
}
</script>
</html>
You're going to have multiple buttons and multiple timeOuts (one for each).
I'd create an empty array to store the timeOuts and use x as an index for this array.
var x = 0;
var timeouts_arr = [];
Inside testToggle...
x++;
myTarget.setAttribute('timeout_index', x);
timeouts_arr[x] = setTimeout(function(){...
inside mybttn.onclick...
state = !state;
if (state) {
mybttn.innerHTML = bttnName + " ON";
callback(targetEle); // don't use the x variable here
} else {
mybttn.innerHTML = bttnName + " OFF";
var timeout_index = mybttn.getAttribute('timeout_index');
clearTimeout(timeout_arr[timeout_index]);
}}

Pure Javascript- Change class of onclick element generated by function

I'm new in coding and have been around a problem for a few days but found no solution.
I'm trying to build a canvas with a grid made out of squares and, when a square is clicked, it changes its background color.
I'm learning pure javascript and my problem is that I can't find a way to refer to the clicked element and change only its class, because my grid is generated by a function. I am doing it this way because I want to be able to change the canvas size in future.
Any suggestions on how to proceed?
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Square Canvas Game</title>
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/css2?family=Merriweather:ital,wght#0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&display=swap" rel="stylesheet">
</head>
<body onload="javascript:setInitialCanvas()">
<header>
<h1>Square artist</h1>
</header>
<div id="gameInterfaceContainer">
<button id="clearCanvas" onclick="clearAllSquares()">Clear All</button>
<div id="canvas">
</div>
</div>
<script src="script.js"></script>
</body>
</html>
JS
function setInitialCanvas(){
for (let i =0; i<400; i++){
let initialSquare = document.createElement("div");
document.getElementById("canvas").appendChild(initialSquare);
initialSquare.className = "canvasSquare clearSquare";
initialSquare.onclick = changeSquareColor(this);
initialSquare.style.backgroundColor = "white";
initialSquare.id = "square" + i;
}
}
function changeSquareColor(this){
let squareColor = ["white","blue","black","green","red","yellow","gray","brown"];
let colorIndex = 0;
while (squareColor[colorIndex] != this.style.backgroundColor){
colorIndex += 1;
}
if(this.style.backgroundColor == squareColor[squareColor.length]){
this.style.backgroundColor = squareColor[0];
}else
colorIndex += 1;
this.style.backgroundColor = squareColor[colorIndex];
}
Thank you!
*Edit: Adding codepen (that doesn't work) https://codepen.io/ccue92/pen/qBNKLQY
The reason it's not working is you're not binding the changeSquareColor function to onclick event, you are binding the result of it.
You need to change onclick binding like this:
initialSquare.onclick = function(event) {
changeSquareColor(event.target);
}
And do not use "this" as a variable name, because it's a reserved word for the current scope:
function changeSquareColor(square) {
let squareColor = ["white","blue","black","green","red","yellow","gray","brown"];
let colorIndex = 0;
while (squareColor[colorIndex] != square.style.backgroundColor){
colorIndex += 1;
}
if(square.style.backgroundColor == squareColor[squareColor.length]){
square.style.backgroundColor = squareColor[0];
} else {
colorIndex += 1;
}
square.style.backgroundColor = squareColor[colorIndex];
}

Close Dropdown when other Dropdown is toggled active

i am trying to close let´s say Dropdown A automatically, when Dropdown B gets classList.toggle("active) (in this case i toggle the ClassList with a Click)
I can open (classList.toggle("active)) and close (classList.toggle("inactive)) it manually, but i want to close it automatically.
Right now i got this:
function dropdown() {
let employerBranding = document.querySelector(".employer-branding");
let marketing = document.querySelector(".marketing");
let corporateOverall = document.querySelector(".corporate-overall");
let technicalData = document.querySelector(".technical-data");
let categoryModules = [employerBranding, marketing, corporateOverall, technicalData];
let categoryDropdown = $(".category-dropdown");
for (let i = 0; i < categoryModules.length; i++) {
categoryModules[i].addEventListener("click", function () {
categoryDropdown.slideDown();
});
}
}
dropdown();
The Problem is now: when i click on one of the 4 Modules of course it opens all of the Dropdowns.
How can i trigger the correct Dropdown to the correct Module, so only one (the one below the clicked Module) opens up
&&
How can i add with another click a .slideUp() to slide it up again?
Here is a very basic example of what you want to achieve:
const one = document.querySelector('#one');
const two = document.querySelector('#two');
const toggle = (e) => {
if (e.target.id === 'one') {
one.classList.toggle('active');
two.classList.remove('active');
} else {
two.classList.toggle('active');
one.classList.remove('active');
}
}
one.addEventListener('click', (e) => toggle(e));
two.addEventListener('click', (e) => toggle(e));
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
div {
display: inline;
margin: 20px;
padding: 100px;
background-color: #444;
}
.active {
padding: 200px 100px;
}
</style>
</head>
<body>
<div id="one"></div>
<div id="two"></div>
</body>
</html>
Generally (and especially if creating more dropdowns), I would suggest a more sophisticated approach involving looping through all of them, such as Rounin mentioned.
Whenever a dropdown is activated with a click:
Close all the dropdowns (ie. loop through the dropdowns and set each to .inactive)
Open the activated dropdown
After a little while a came up with this solution
function closeDropdown() {
// let employerBrandingDropdown = document.querySelector(".employer-branding-dropdown");
let employerBrandingDropdown = $('.employer-branding-dropdown');
let marketingDropdown = $(".marketing-dropdown");
let corporateOverallDropdown = $(".corporate-overall-dropdown");
let technicalDataDropdown = $(".technical-data-dropdown");
let dropdownArray = [employerBrandingDropdown, marketingDropdown, corporateOverallDropdown, technicalDataDropdown];
window.addEventListener('mouseup', function (event) {
for (let i = 0; i < dropdownArray.length; i++) {
let categoryDropdown = dropdownArray[i];
if ($(event.target !== categoryDropdown) && $(event.target).parent() !== categoryDropdown) {
$(categoryDropdown).stop().slideUp();
}
}
})
}

Trying to change the id of an element with Javascript

I am trying to change the id of a div whenever I hover over a selection in my navbar. I just can't see where my problem is. When I inspect the page and hover over the navbar selections, the div id is not changing.
This is my javascript:
// Run JS after contents are loaded
document.addEventListener("DOMContentLoaded", initialiseWebPage);
function initialiseWebPage() //Loads DOM content
{
var navImage = document.getElementById("picture");
// Variables for each Nav Selection
const home = document.getElementById("Home");
const portfolio = document.getElementsById("Portfolio");
const services = document.getElementsById("Services");
const achievements = document.getElementsById("Achievements");
const hobbies = document.getElementsById("Hobbies");
// Event listeners
home.addEventListener("mouseover", changeIdHome);
portfolio.addEventListener("mouseover", changeIdPortfolio);
services.addEventListener("click", changeIdServices);
achievements.addEventListener("mouseover", changeIdAchievements);
hobbies.addEventListener("mouseover", changeIdHobbies);
function changeIdHome()
{
navImag.id = "Home";
}
function changeIdPortfolio()
{
navImage.id = "Portfolio";
}
function changeIdServices()
{
navImage.id = "Services";
}
function changeIdAchievements()
{
navImage.id = "Achievements";
}
function changeIdHobbies()
{
navImage.id = "Hobbies";
}
}
This is the beginning of my HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My Website</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="https://use.fontawesome.com/releases/v5.0.8/js/all.js"></script>
<script src="Script.js"></script> <!-- Link document to javascript file -->
<link href="style.css" rel="stylesheet">
<link rel="stylesheet" href="https://m.w3newbie.com/you-tube.css">
</head>
<body>
<!-- Navigation -->
<div class="container-fluid">
<div id="picture">
I am pretty sure I linked my js correctly to my html.
as you can see at the bottom of the HTML, the div that im trying to play around has the id of "picture".
I can't 100% confirm this, however, on many of your constants you used document.getElementsById and it's supposed to be document.getElementById.
Heres the changed JavaScript code
document.addEventListener("DOMContentLoaded", initialiseWebPage);
function initialiseWebPage() //Loads DOM content
{
var navImage = document.getElementById("picture");
// Variables for each Nav Selection
const home = document.getElementById("Home");
const portfolio = document.getElementById("Portfolio");
const services = document.getElementById("Services");
const achievements = document.getElementById("Achievements");
const hobbies = document.getElementById("Hobbies");
// Event listeners
home.addEventListener("mouseover", changeIdHome);
portfolio.addEventListener("mouseover", changeIdPortfolio);
services.addEventListener("click", changeIdServices);
achievements.addEventListener("mouseover", changeIdAchievements);
hobbies.addEventListener("mouseover", changeIdHobbies);
function changeIdHome()
{
navImag.id = "Home";
}
function changeIdPortfolio()
{
navImage.id = "Portfolio";
}
function changeIdServices()
{
navImage.id = "Services";
}
function changeIdAchievements()
{
navImage.id = "Achievements";
}
function changeIdHobbies()
{
navImage.id = "Hobbies";
}
}
I hope this helps!
Every element should have unique id.
You can't get array from one element:
document.getElementsById()
So just replace it with:
document.getElementById()

Categories

Resources