How do I use the input from my box to my function? - javascript

So I created a 16 x 16 grid where I can etch a sketch on that grid. It's working well. Right now, I have to call the function createGrid(number) everytime I want to change the size of the grid. I have created a text input boxes as you can see on my code. So instead of having to write it again everytime and refresh the page, I want to be able to use the input from this box to change the size of the grid.
One of the ways that I've tried is by creating a new variable such as:
let number = inputFromBox.value;
and then write createGrid(number) . But it doesn't work. Is there any way how to make this work ? by using the input from the box ? Please help. Thank you !
let container = document.querySelector('.container');
let rows = document.getElementsByClassName('gridRow');
let duplicateOfInput = document.getElementById('duplicateOfInput');
let button = document.getElementById('submit');
let inputFromBox = document.getElementsByClassName('size-box');
button.addEventListener('click', createGrid);
createGrid(16);
draw();
// createGrid(anyNumber);
// draw();
// duplicateOfInput.textContent = `${input}`;
// const rainbow = document.getElementsByClassName('rainbow');
let reset = document.getElementById('clear-button');
reset.addEventListener('click', clearGrid);
function createGrid(number) {
makeRow(number);
makeColumn(number);
draw();
}
function makeRow(numberOfRow) {
container.innerHTML = "";
for (let i = 0; i <numberOfRow; i++) {
let row = document.createElement('div');
container.appendChild(row);
row.classList.add('gridRow');
}
}
function makeColumn(numberOfColumn) {
for ( let i = 0; i < rows.length; i++) {
for ( let j = 0; j < numberOfColumn; j++) {
let column = document.createElement('div');
rows[j].appendChild(column);
column.classList.add('gridColumn');
}
}
}
//adds event listener to all divs with class "column"
//added in global scope to allow drawing on page load
//this refers to the element triggering the mouseover event listener
function draw() {
let columns = document.getElementsByClassName("gridColumn");
for (let i = 0; i < columns.length; i++) {
columns[i].addEventListener("mouseover", changeColor);
}
function changeColor() {
let blue = document.getElementById('blue');
let eraser = document.getElementById('eraser');
let black = document.getElementById('black');
let rainbow = document.getElementById('rainbow');
if (blue.checked) {
this.style.backgroundColor = 'blue';
} else if (eraser.checked) {
this.style.backgroundColor = 'beige';
} else if (black.checked) {
this.style.backgroundColor = 'black';
} else if (rainbow.checked) {
let randomColor = Math.floor(Math.random()*16777215).toString(16);
this.style.backgroundColor = '#' + randomColor;
}
}
}
//eraser function loops through all column divs and sets background to "" in DOM
function clearGrid() {
let columns = document.getElementsByClassName("gridColumn");
for (let i = 0; i < columns.length; i++) {
columns[i].style.backgroundColor = '';
}
}
#import url('https://fonts.googleapis.com/css2?family=Asap:wght#400;600;700&display=swap');
body {
display: flex;
height: 100%;
width: 100%;
flex-direction: column;
background-color: beige;
font-family: Asap, sans-serif;
margin: 0;
padding: 0;
justify-content: center;
align-content: center;
align-items: center;
background-repeat: no-repeat;
}
.header {
display: flex;
flex: 1;
justify-content: center;
}
#header-title {
font-family: Asap, sans-serif;
font-size: 18px;
}
#setGridSize {
display: inline-flex;
justify-content: center;
align-items: center;
flex: 1;
gap: 12px;
}
#guide {
text-align: center;
margin: 1px;
font-family: Asap, sans-serif;
color: red;
font-size: 13px;;
}
.canvas {
display: flex;
justify-content: center;
align-items: center;
}
.container {
display: flex;
flex-direction: column;
border: 1px solid green;
width: 550px;
height: 550px;
}
/* .gridColumn {
display: inline-flex;
border: 1px solid beige;
margin: -1px 0;
width: 30px;
height: 30px;
} */
.gridColumn {
flex: 1;
border: 1px solid beige;
}
.gridRow {
display: flex;
flex: 1;
}
.default {
background: beige;
}
#button-container {
margin: 3px;
}
#clear-button {
margin: 2px;
}
<!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>DOM Manipulation and Events</title>
<script src="javascript.js" defer></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1 class="header"> Let's sketch ! </h1>
<div id="setGridSize">
<p id="header-title"> Grid Size :</p>
<input type="text" placeholder="Size of Board" class="size-box">
<span id = "duplicateOfInput"></span>
<button id="submit" > Submit </button>
</div>
<p id="guide"> Enter a number between 2 to 99 </p>
<div class="canvas">
<div class="container"></div>
</div>
<div class="buttons">
<form id="button-container">
<input type="radio" id="blue" name="pen" value="blue-pen"><label for = "blue-pen"> Blue </label>
<input type="radio" id="eraser" name="pen" value="eraser"><label for = "eraser" > Eraser </label>
<input type="radio" id="black" name="pen" value="black-pen"> <label for = "black" > Black </label>
<input type="radio"id="rainbow" name="pen" value="black-pen"> <label for = "rainbow" > Rainbow </label>
</form>
</div>
<button id = "clear-button" > Clear </button>
</body>
</html>

You can use let inputFromBox = document.getElementById('size-box'); and with let number = inputFromBox.value; you can get the value within the click function.
CodePen

You're using document.getElementsByClassName, which is most likely returning an array. Try using inputFromBox[0].value or switching to finding it by an ID.

You may find it easier to use CSS Grid. It allows you to minimise the amount of code you write because you don't have to create rows and columns separately.
Additionally if you set up some CSS variables you can hook the value from the input directly into the stylesheet to update the dimensions of the grid.
// Cache the elements
const grid = document.querySelector('.grid');
const submit = document.querySelector('.submit');
const input = document.querySelector('.size');
// When the button is clicked call `createGrid`
submit.addEventListener('click', createGrid);
function createGrid() {
// Get the value from the input
const { value } = input;
// Array to hold the box strings
const boxes = [];
// Loop to create a grid of boxes - for each
// box push it into the array
for (let i = 1; i <= value * value; i++) {
boxes.push(`<div class="box">${i}</div>`);
}
// `join` the array and update the innerHTML
// of the grid element
grid.innerHTML = boxes.join('');
// Pass the value directly into the spreadsheet.
// This changes the default `--grid-dimension` to the new value
document.documentElement.style.setProperty('--grid-dimension', value);
}
:root { --box-width: 30px; --grid-dimension: 13; }
.grid { margin-top: 1em; display: grid; grid-template-columns: repeat(var(--grid-dimension), var(--box-width)); gap: 0.2em; }
.box { display: flex; justify-content: center; align-items: center; height: var(--box-width); width: var(--box-width); background-color: lightgreen; }
.box:hover { background-color: lightblue; cursor: pointer; }
<input type="text" placeholder="Size of grid" class="size">
<button type="button" class="submit">Create grid</button>
<div class="grid"></div>

Related

Updating books cards after deletions

I currently working on Library application and I'm trying to update the data set attribute of each book card after deletion of one book. For example if I have added three books and they all have the values corresponding to their position in the array. 0-2 if I delete the first one . I want the data-book-number attribute to update to the respective positions these items now have in the array . So 0-1, in my current implementation I can delete book cards after adding them but their data-book-number remain the same . I need them to update them as that's how I'm checking if the button and the book match to remove that specific book from the array and DOM.
const addBtn = document.getElementById("add");
const modal = document.querySelector(".addModal");
const cancelBtn = document.getElementById("cancel");
const Name = document.getElementById("Name");
const authorName = document.getElementById("author");
const pageNumber = document.getElementById("number");
const newBookBtn = document.getElementById("bookBtn");
const bookContainer = document.getElementById("content");
// constructor
function Book(title, author, numPages, read) {
this.title = title;
this.author = author;
this.numPages = numPages;
this.read = read;
}
Book.prototype.info = function () {
return `${this.title} by ${this.author}, ${this.numPages} pages, Read? ${this.read}`;
};
// Lib array
let myLibrary = [];
//functions to add books to array
function addBookToLib() {
let title = Name.value;
let author = authorName.value;
let numberPage = pageNumber.value;
let isRead = document.querySelector("input[name=read]:checked").value;
const word = new Book(title, author, numberPage, isRead);
myLibrary.push(word);
}
// functions to loop over the array and display the details on a div
let count = 0;
const loopOver = () => {
for (let i = 0; i < myLibrary.length; i++) {
if (count <= i) {
i + count;
let newCard = document.createElement("div");
createBook(newCard, i);
let delBtn = document.createElement("button");
createDelBtn(delBtn, i);
newCard.appendChild(delBtn);
bookContainer.appendChild(newCard);
count++;
changeClr(newCard, i);
let allDelBtn = document.querySelectorAll(".numberBtn");
deleteBook(allDelBtn, newCard);
}
}
};
// Change books color
function changeClr(item, valueAt) {
if (myLibrary[valueAt].read === "Yes") {
item.classList.add("read");
} else if (myLibrary[valueAt].read === "No") {
item.classList.add("unread");
}
}
// create each book
function createBook(item, valueAt) {
item.dataset.bookNumber = valueAt;
item.classList.add("book");
item.innerHTML = `<h4>Title</h4>
<p>${myLibrary[valueAt].title}</p>
<h4>Author</h4>
<p>${myLibrary[valueAt].author}</p>
<h4>Number of pages</h4>
<p>${myLibrary[valueAt].numPages}</p>
<h4>Have you read this book?</h4>
<p>${myLibrary[valueAt].read}</p> `;
}
// create delete btn
function createDelBtn(btnName, valueAt) {
btnName.innerText = "Remove Book";
btnName.dataset.bookNumber = valueAt;
btnName.classList.add("numberBtn");
}
// delete a book function
function deleteBook(allBtn, book) {
allBtn.forEach((btn1) => {
btn1.addEventListener("click", () => {
let dataB1 = book.dataset.bookNumber;
let dataBT1 = btn1.dataset.bookNumber;
if (dataB1 === dataBT1) {
let selectedDiv = document.querySelector(
`[data-book-number="${dataB1}"]`
);
myLibrary.splice(dataB1, 1);
bookContainer.removeChild(selectedDiv);
count--;
}
});
});
}
// // function to change data-set after delete button has been pressed
// function changeAttr(allBooks, valueAt) {
// allBooks.forEach((book) => {
// book.dataset.bookNumber = valueAt;
// });
// }
// clear the modal box
const clearModal = () => {
Name.value = " ";
authorName.value = " ";
pageNumber.value = 0;
document.querySelector('input[name="read"]:checked').checked = false;
};
// display the modal
addBtn.addEventListener("click", () => {
modal.style.visibility = "visible";
Name.focus();
});
// hide modal and call clear modal function
cancelBtn.addEventListener("click", () => {
modal.style.visibility = "hidden";
clearModal();
});
// Run the method to add new object and pass it into the array
newBookBtn.addEventListener("click", (e) => {
addBookToLib();
loopOver();
clearModal();
modal.style.visibility = "hidden";
});
:root {
--clr1: #737373;
--cl2: ;
}
* {
padding: 0px;
margin: 0px;
}
body {
background-image: url(./images/susan-q-yin-2JIvboGLeho-unsplash.jpg);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
}
.container {
display: grid;
height: 100vh;
grid-template-columns: repeat(4, 1fr);
}
header {
grid-column: 2/5;
/* background-color: var(--clr1); */
}
aside {
grid-row: 1/2;
/* background-color: var(--clr1); */
}
/* main books */
main {
grid-area: 2 / 1 / 4 / 5;
position: relative;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-template-rows: repeat((auto-fit, minmax(300, 1fr)));
row-gap: 10px;
}
.book {
place-self: center;
width: 300px;
height: 200px;
background-color: grey;
border-radius: 5px;
display: flex;
flex-direction: column;
justify-content: center;
}
/* modal style */
.addModal {
visibility: hidden;
position: absolute;
place-self: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 250px;
height: 300px;
background-color: lightgrey;
border-radius: 5px;
}
.addModal > h3 {
margin-bottom: 10px;
}
.addModal form {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 3px;
width: 150px;
}
.newBook {
padding: 5px;
border-radius: 5px;
border-color: lightgrey;
}
/* if read */
.read {
border-left: 8px solid blue;
}
/* if not read */
.unread {
border-left: 8px solid red;
}
.numberBtn {
width: 6rem;
}
/* Responsiveness */
#media screen and (max-width: 500px) {
.book {
width: 200px;
height: 150px;
place-self: center;
}
aside {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 4rem;
}
.book {
padding: 0.8rem;
}
.book h4 {
font-size: 1.1rem;
margin-left: 5px;
}
.book p {
margin-left: 5px;
}
}
<!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" />
<title>My Library</title>
</head>
<body>
<div class="container">
<header>
<h1>Welcome to your library</h1>
<button id="add">New Book</button>
</header>
<!-- sidebar -->
<aside>
<h3>Read</h3>
<h3>Unread</h3>
</aside>
<main id="content">
<!-- modal pop up -->
<div class="addModal">
<h3>Add a Book</h3>
<form method="post" id="newForm">
<label for="Name">Book Name</label>
<input type="text" name="Name" id="Name" />
<label for="author">Author</label>
<input type="text" name="author" id="author" />
<label for="numPages">Number of Pages</label>
<input type="number" min="0" id="number" />
<label for="read">Have you Read it?</label>
<div class="answer-container">
<input type="radio" name="read" value="Yes" /> Yes
<input type="radio" name="read" value="No" /> No
</div>
</form>
<div class="button-contain">
<button class="newBook" id="bookBtn" type="submit">Add Book</button>
<button id="cancel" class="newBook">Cancel</button>
</div>
</div>
</main>
</div>
<script src="./script.js"></script>
</body>
</html>

Buttons not working xxxxx.addEventListener is not a function

I created a 16 x 16 grid where I can etch a sketch on that grid. It's working with the default colour that I use. When I try adding buttons to change colours to sketch. I can't seem to make it work. I've tried various methods and writing it with various ways but again and again I failed. I want when I click on the buttons, it changes colour when I sketch. I'll include below the previous code that's working and one of the ways that I've tried. Any expert/master please have a look on my code.
let container = document.querySelector('.container');
let rows = document.getElementsByClassName('gridRow');
let columns = document.getElementsByClassName('gridColumn');
const blue = document.getElementsByClassName('blue');
const eraser = document.getElementsByClassName('eraser');
const black = document.getElementsByClassName('black');
let reset = document.getElementById('reset');
function createGrid(number) {
makeRow(number);
makeColumn(number);
changeColours();
}
function makeRow(numberOfRow) {
for (let i = 0; i <numberOfRow; i++) {
let row = document.createElement('div');
container.appendChild(row);
row.classList.add('gridRow');
}
}
function makeColumn(numberOfColumn, selection) {
for ( let i = 0; i < rows.length; i++) {
for ( let j = 0; j < numberOfColumn; j++) {
let column = document.createElement('div');
The part below is what I tried, erase it if you want it to work with just one colour
if (selection == 'blue') {
column.addEventListener('mouseenter', function() {
column.classList.add('blue');
})
} else if (selection == 'eraser') {
column.addEventListener('mouseenter', function() {
column.classList.add('eraser');
})
} else if (selection == 'black') {
column.addEventListener('mouseenter', function() {
column.classList.add('black');
})
} else {
column.addEventListener('mouseenter', function() {
column.classList.add('colored');
})
}
// column.addEventListener('mouseleave', () => {
// column.classList.remove('colored');
// })
Just erase part of the code above if you want to make it work
rows[j].appendChild(column);
column.classList.add('gridColumn');
}
}
}
The part below is what I tried, erase it if you want it to work with just one colour
blue.addEventListener('click', function() {
makeColumn(number, 'blue');
})
eraser.addEventListener('click', function() {
makeColumn(number, 'white');
})
black.addEventListener('click', function() {
makeColumn(number, 'black');
})
Just erase part of the code above if you want to make it work
createGrid(16);
#importurl('https://fonts.googleapis.com/css2family=Asap:wght#400;600;700&display=swap');
body {
display: flex;
height: 100%;
width: 100%;
flex-direction: column;
background-color: beige;
font-family: Asap, sans-serif;
margin: 0;
padding: 0;
justify-content: center;
align-content: center;
align-items: center;
}
.header {
display: flex;
flex: 1;
justify-content: center;
}
#setGridSize {
display: inline-flex;
justify-content: center;
flex: 1;
gap: 12px;
}
#guide {
text-align: center;
margin: 1px;
font-family: Asap, sans-serif;
color: red;
font-size: 13px;;
}
.container {
display: flex;
justify-content: center;
align-content: center;
align-items: center;
border: 1px solid black;
width: 550px;
height: 550px;
}
.gridColumn {
display: inline-flex;
border: 1px solid beige;
margin: -1px 0;
width: 30px;
height: 30px;
}
.colored{
background: red;
}
.buttons {
display: flex;
flex: 1;
gap: 20px;
margin: 10px;
}
.blue {
background: blue;
}
.eraser {
background: white;
}
.black {
background: black;
}
<!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>DOM Manipulation and Events</title>
<script src="javascript.js" defer></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1 class="header"> Let's sketch ! </h1>
<div id="setGridSize">
<p> Grid size </p> <input type="text" placeholder="Size of Board" class="size-box">
<button id="submit" > Submit </button>
</div>
<p id="guide"> Enter a number between 2 to 99</p>
<div class="container"></div>
<div class="buttons">
<button class="blue"> Blue </button>
<button class="eraser" > Eraser </button>
<button class="black"> Black </button>
<button class="rainbow" > Rainbow </button>
<button class="reset" > Reset</button>
</div>
</body>
</html>
Maybe this small example will help you
const grid = document.querySelector('.grid');
const colorSelector = document.querySelector('input[type="color"]')
const COUNT = 8
for (let i = 0; i < COUNT; i++) {
for (let j = 0; j < COUNT; j++) {
const btn = document.createElement('button')
grid.appendChild(btn)
}
}
const btns = Array.from(grid.children)
btns.forEach(btn => {
btn.addEventListener('mouseenter', event => {
btn.style.backgroundColor = colorSelector.value;
})
})
#import "https://cdn.jsdelivr.net/gh/KunalTanwar/normalize/css/normalize.inter.min.css";
body {
height: 100%;
display: grid;
place-items: center;
}
.grid {
--btn-count: 8;
--btn-size: 24px;
display: grid;
grid-template-columns: repeat(var(--btn-count), var(--btn-size));
grid-template-rows: repeat(var(--btn-count), var(--btn-size));
}
.grid button {
border: 1px solid #d1d1d1;
}
<input type="color">
<div class="grid"></div>

How to create a grid that includes resizable divs - never exceeds the size of the main container?

I am able to create the grid using a for loop. I then prompt for the number of rows and number of columns as the x and y values, respectively. I am having trouble understanding how one would go about creating divs that resize to fit the size of a container - with the container never exceeding the size of the page(?). Any suggestions?
let x = parseInt(prompt("how many rows?"));
let y = parseInt(prompt("how many columns?"));
const reset = document.getElementById("reset");
reset.addEventListener("click", function() {
location.reload();
});
const container = document.getElementById("container");
const rows = document.getElementsByClassName("gridRow");
const cells = document.getElementsByClassName("cell");
function makeGrid() {
makeRows(x);
makeCols(y);
}
function makeRows(rowNum) {
for (i = 0; i < rowNum; i++) {
let row = document.createElement("div");
container.appendChild(row).className = "gridRow";
}
}
function makeCols(colNum) {
for (i = 0; i < rows.length; i++) {
for (j = 0; j < colNum; j++) {
let col = document.createElement("div");
rows[j].appendChild(col).className = "cell";
}
}
}
makeGrid();
:root {
display: flex;
justify-content: center;
align-items: center;
}
.cell {
border: 1px solid gray;
min-width: 30px;
min-height: 30px;
margin: 4px;
display: inline-flex;
}
.cell:hover {
cursor: pointer;
background-color: antiquewhite;
}
.cell:active {
background-color: antiquewhite;
}
<!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" />
<script src="script.js" defer></script>
<link rel="stylesheet" href="style.css" />
<title>Etch-A-Sketch</title>
</head>
<body>
<button id="reset" class="reset">RESET</button>
<div id="container"></div>
</div>
</body>
</html>
I'm not sure I understand the whole question, but here is an example that will share the width and height of the grid itself.
The key is to use the flex layout and fix the initial grid size using vw and vh units.
I have simplified the code to focus on a minimal example.
function makeCells(rows, columns) {
let grid = document.getElementById("grid");
for (i = 0; i < rows; i++) {
let row = document.createElement("div");
row.className = "row";
for (j = 0; j < columns; j++) {
let column = document.createElement("div");
column.className = "cell";
row.appendChild(column);
}
grid.appendChild(row);
}
}
makeCells(4, 3);
.grid {
display: flex;
flex-direction: column;
align-items: stretch;
border: 1px solid red;
width: 50vw; /* 50% of viewport width */
height: 50vh; /* 50% of viewport height */
}
.row {
display: flex;
flex-direction: row;
align-items: stretch;
border: 1px solid blue;
height: 100%;
}
.cell {
border: 1px solid green;
height: 100%;
width: 100%;
}
<div id="grid" class="grid"></div>

JavaScript querySelectorAll() still discovers DOM elements that have been removed

The Problem
I'm creating a treasure hunt web app that allows you to dynamically add and remove point from the hunt. I do this through the .createElement() and .remove() methods respectively.
When all points have been configured, I grab all the elements (each node is created with a custom web component) with a querySelectorAll(), iterate through them, grab all the info (title, location, clue etc.) and create an object for each point, which is then put in an array. However, if I remove a node before or after I try to save, the deleted element is not removed from the list returned by querySelectorAll(). It throws the error:
Uncaught TypeError: markers[i].shadowRoot.querySelector(...) is null
when reaching the point of any deleted points.
Method for web component removal
// Deletes point marker
deletePoint() {
const delPoint = this.shadowRoot.querySelector(".del-btn");
let pointMarker = delPoint.parentNode.parentNode.parentNode;
pointMarker.remove();
};
Add and save functions
const addPoint = document.querySelector(".add");
const savePoints = document.querySelector(".save");
var data = [];
// Defines markers in preperation for later
let markers = null
// Adds point-marker element to markers div
addPoint.addEventListener("click", () => {
const pointContainer = document.querySelector(".markers");
const node = document.createElement("point-marker");
pointContainer.appendChild(node);
});
// Grabs all point-marker elements, grabs relevant data and adds it to data array
savePoints.addEventListener("click", () => {
// clears data
data = []
markers = document.querySelectorAll("point-marker");
// Iterates through markers
for (i = 0; i < markers.length; i++) {
console.log(`i: ${i}`)
// Grabs all relevant info
let name = markers[i].shadowRoot.querySelector(".name").textContent;
let location = markers[i].shadowRoot.querySelector(".location").textContent;
let clue = markers[i].shadowRoot.querySelector("#clue").value;
// Saves all relevant info in object form
point = {
id: `${i}`,
name: `${name}`,
location: `${location} ${i}`,
clue: `${clue}`
}
// Adds point to data
data.push(point)
}
console.log(data)
});
I'm fairly certain it's an issue with the .remove() method not fully removing the element from the DOM, as it doesn't cause an issue when an element is added, but cannot find another method.
Here's the full code as a snippet if it's of any help:
// === script.js ====
// Declares template variable, containing the html template for the component
const template = document.createElement("template");
template.innerHTML = `
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.3/css/all.css" integrity="sha384-SZXxX4whJ79/gErwcOYf+zWLeJdY/qpuqC4cAa9rOGUstPomtqpuNWT9wdPEn2fk" crossorigin="anonymous">
<link rel="stylesheet" href="css/style.css">
<style>
#import url('https://fonts.googleapis.com/css2?family=Roboto:wght#400;700&display=swap');
.point-marker {
color: var(--tertiary-color);
background-color: var(--secondary-color);
padding: 2rem;
border-radius: 20px;
margin: 1rem 0;
}
.point-marker h2 {
line-height: 1rem;
}
.point-marker textarea {
width: 100%;
height: 100px;
border-radius: 20px;
resize: vertical;
padding: .5rem;
margin: 1rem 0;
}
.btn {
background-color: var(--primary-color);
border: none;
padding: .5rem 1rem;
min-width: 200px;
color: var(--tertiary-color);
border-radius: 10px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
font-size: medium;
cursor: pointer;
}
.del-btn {
background-color: var(--fail-color);
}
.btns {
display: flex;
width: 100%;
justify-content: space-evenly;
}
.coll-content {
max-height: 0;
overflow: hidden;
transition: max-height 250ms ease-in-out;
}
.collapse-icon {
font-size: large;
}
.const-content {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
}
</style>
<section class="point-marker">
<div class="const-content">
<h2 class="name">New Point</h2>
<i class="fas fa-minus collapse-icon"></i>
</div>
<div class="coll-content">
<p>Location: <p class="location">location</p></p>
<p>Clue:</p>
<textarea name="clue" id="clue" cols="30" rows="10"></textarea>
<div class="btns">
<button class="btn loc-btn">SET CURRENT LOCATION</button>
<button class="btn del-btn">DELETE POINT</button>
</div>
</div>
</section>
`;
// Declares class PointMarker and casts it as an HTML element
class PointMarker extends HTMLElement {
// Initialises the class every time new object is made
constructor() {
super();
// Declares shadow DOM and sets it to open
this.attachShadow({
mode: "open"
});
this.shadowRoot.appendChild(template.content.cloneNode(true));
setTimeout(() => {
const coll = this.shadowRoot.querySelector(".const-content");
coll.nextElementSibling.style.maxHeight = `${coll.nextElementSibling.scrollHeight}px`;
}, 100)
const name = this.shadowRoot.querySelector(".name")
name.contentEditable = "true";
};
// Collapses or expands the collapsable content
expandCollapse() {
const coll = this.shadowRoot.querySelector(".const-content");
let content = coll.nextElementSibling;
if (content.style.maxHeight) {
content.style.maxHeight = null;
} else {
content.style.maxHeight = `${content.scrollHeight + 30}px`;
};
};
// Deletes point marker
deletePoint() {
this.disconnectedCallback();
const delPoint = this.shadowRoot.querySelector(".del-btn");
let pointMarker = delPoint.parentNode.parentNode.parentNode;
pointMarker.remove();
pointMarker = null;
};
// Adds event listener on all elements with class of const-content or del-btn
connectedCallback() {
this.shadowRoot.querySelector(".collapse-icon").addEventListener("click", () => this.expandCollapse());
this.shadowRoot.querySelector(".del-btn").addEventListener("click", () => this.deletePoint());
console.log("connectedCallback() called");
console.log(this.isConnected)
};
// Adds event listener on all elements with class of del-btn
disconnectedCallback() {
this.shadowRoot.querySelector(".collapse-icon").removeEventListener("click", () => this.expandCollapse());
this.shadowRoot.querySelector(".del-btn").removeEventListener("click", () => this.deletePoint());
console.log("disconnectedCallback() called");
console.log(this.isConnected)
};
};
// Defines <point-marker>
window.customElements.define("point-marker", PointMarker);
const addPoint = document.querySelector(".add");
const savePoints = document.querySelector(".save");
// Defines markers in preperation for later
// Adds point-marker element to markers div
addPoint.addEventListener("click", () => {
const pointContainer = document.querySelector(".markers");
const node = document.createElement("point-marker");
pointContainer.appendChild(node);
});
// Grabs all point-marker elements, grabs relevant data and adds it to data array
savePoints.addEventListener("click", () => {
// clears data
let data = []
markers = document.querySelectorAll("point-marker");
// Iterates through markers
for (i = 0; i < markers.length; i++) {
// Grabs all relevant info
let name = markers[i].shadowRoot.querySelector(".name").textContent;
let location = markers[i].shadowRoot.querySelector(".location").textContent;
let clue = markers[i].shadowRoot.querySelector("#clue").value;
// Saves all relevant info in object form
let point = {}
point = {
id: `${i}`,
name: `${name}`,
location: `${location} ${i}`,
clue: `${clue}`
}
// Adds point to data
data.push(point)
console.log(data)
}
return data;
});
/* style.css */
#import url('https://fonts.googleapis.com/css2?family=Roboto:wght#400;700&display=swap');
:root {
--primary-color: #FA4D05;
--secondary-color: #333;
--tertiary-color: #fff;
--success-color: #97FD87;
--fail-color: #FF5555;
--bg-color: #E5E5E5;
--font-color: #808080;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
}
html {
scroll-behavior: smooth;
}
body {
min-height: 100vh;
line-height: 2;
color: var(--primary-color);
}
h1 {
font-size: 36px;
}
h2 {
font-size: 24px;
}
nav {
display: flex;
background-color: var(--secondary-color);
justify-content: space-between;
align-items: center;
height: 65px;
padding-left: 5rem;
/* color: var(--primary-color); */
}
nav ul {
list-style: none;
display: flex;
justify-content: space-evenly;
width: 50%;
}
main {
display: flex;
flex-direction: column;
padding: 2rem;
}
main h1 {
margin-bottom: 1rem;
}
.btn {
background-color: var(--primary-color);
border: none;
padding: .5rem 1rem;
min-width: 200px;
color: var(--tertiary-color);
border-radius: 10px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
font-size: medium;
cursor: pointer;
}
.add-point {
background-color: var(--bg-color);
color: var(--font-color);
margin: 1rem 0;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.save {
background-color: var(--success-color);
}
<!-- index.html -->
<!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="https://use.fontawesome.com/releases/v5.15.3/css/all.css" integrity="sha384-SZXxX4whJ79/gErwcOYf+zWLeJdY/qpuqC4cAa9rOGUstPomtqpuNWT9wdPEn2fk" crossorigin="anonymous">
<title>Create A Hunt</title>
</head>
<body>
<header>
<nav>
<h2>HOME</h2>
<ul>
<li>
<h2>HUNT</h2>
</li>
<li>
<h2>CREATE</h2>
</li>
</ul>
</nav>
</header>
<main>
<h1>CREATE A HUNT</h1>
<div class="markers">
</div>
<button class="btn add-point add">
<h2>Add Point +</h2>
</button>
<button class="btn add-point save">
<h2>Save Points</h2>
</button>
</main>
<script src="script.js"></script>
<script src="/components/pointMarker.js"></script>
</body>
</html>
TL;DR
Elements removed with the .remove() method are still picked up by the .querySelectorAll() method, presumably because it does not remove it from the DOM fully.
// Deletes point marker
deletePoint() {
this.disconnectedCallback();
const delPoint = this.shadowRoot.querySelector(".del-btn");
let pointMarker = delPoint.parentNode.parentNode.parentNode;
pointMarker.remove();
pointMarker = null;
};
This does not remove the point marker. It removes the contents of the point marker but the point marker is still there.
// Deletes point marker
deletePoint() {
this.disconnectedCallback();
this.remove();
};
This removes the actual element from the page, and your code then works just fine.

Stop named function from executing itself

I am trying to build a simple webpage, which simply checks the value of the strings in two input fields, such that when the Test button is clicked, a previously hidden div will show the boolean value to be returned (If isomorphic return true, else, return false).
This is my code:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Isomorphics App</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="styles.css" />
</head>
<body>
<div class="container">
<div class="content">
<div class="header">
<p class="title">Isomorphics</p>
<span> </span>
<p class="description">Find out if any two strings are isomorphic.</p>
<span> </span>
<p>Enter any two words (strings) in the fields below:</p>
</div>
<div class="input-container">
<input class="input" id="s" placeholder="First string" />
<input class="input" id="t" placeholder="Second string" />
<button class="input-button" id="submit-button">Test</button>
</div>
<div class="isomorphic-state-container" id="isomorphic-state-container">
<div class="isomorphic-state-holder" id="isomorphic-state-holder">
<p class="isomorphic-state" id="isomorphic-state">ili</p>
</div>
</div>
</div>
</div>
<script src="main.js"></script>
</body>
</html>
main.js
let s = document.getElementById('s').innerText;
let t = document.getElementById('t').innerText;
console.log(s);
document.getElementById('submit-button').onclick = isomorphic(s, t);
console.log(isomorphic(s, t));
function isomorphic(str1, str2) {
if (str1.length !== str2.length) {
alert('Please enter two strings of equal length.');
}
let map = {};
for (let i = 0; i < str1.length; i++){
let a = str1[i];
let b = str2[i];
if (typeof map[a] === 'undefined') {
map[a] = b;
} else if (map[a] !== b) {
// alert(false);
document.getElementById('isomorphic-state-container').style.display = 'block';
document.getElementById('isomorphic-state-holder').style.backgroundColor = 'red';
document.getElementById('isomorphic-state').innerText = 'False'
}
for (var key in map) {
if (key !== a && b === map[key]) {
// alert(false);
document.getElementById('isomorphic-state-container').style.display = 'block';
document.getElementById('isomorphic-state-holder').style.backgroundColor = '#D64BFB';
document.getElementById('isomorphic-state').innerText = 'False'
}
}
}
document.getElementById('isomorphic-state-container').style.display = 'block';
document.getElementById('isomorphic-state-holder').style.backgroundColor = 'green';
document.getElementById('isomorphic-state').innerText = 'True'
}
styles.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: Roboto Mono;
font-size: 16px;
}
*:focus {
outline: none;
}
.header {
color: #FFFFFF;
text-align: center;
}
.header .title {
font-size: 36px;
}
.header .description {
font-size: 16px;
}
.container {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
background-color: #000000;
}
.container .content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
height: 65%;
width: 100%;
}
.container .content .input-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
width: 50%;
height: 150px;
}
.container .content .input-container .input {
width: 100%;
height: 40px;
font-size: 16px;
padding-left: 10px;
}
.container .content .input-container .input-button {
width: 100%;
height: 40px;
background-color: rgba(255, 255, 255, 0.1);
color: #FFFFFF;
font-size: 16px;
}
.container .content .input-container .input-button:hover {
cursor: pointer;
}
.isomorphic-state-container {
display: none;
width: 50%;
height: 40px;
}
.isomorphic-state-holder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.isomorphic-state {
color: #FFFFFF;
font-size: 16px;
}
After running this code, the javascript defaults the value to true, and doesn't work even when I click the button. Screenshot:
What should I do to make the code run only when the button is clicked? Or, is the named function causing the problem?
This line:
document.getElementById('submit-button').onclick = isomorphic(s, t);
calls your function and then assigns its return value to onclick, exactly the way x = foo() calls foo and assigns its result to x. To set a click event handler that way, you assign a function (not its result) to the onclick property, for instance:
document.getElementById('submit-button').onclick = function() {
isomorphic(s, t);
};
(You'll also want to remove the console.log(isomorphic(s, t)); line, since it also calls the function.)
If you want to get s and t as of when the button is clicked, instead of when the script first runs, move those lines into the click handler as well:
document.getElementById('submit-button').onclick = function() {
let s = document.getElementById('s').innerText;
let t = document.getElementById('t').innerText;
isomorphic(s, t);
};
Better yet, use modern event handling via addEventListener:
document.getElementById('submit-button').addEventListener("click", function() {
// ...handler code...
});
If you need to support obsolete browsers like IE8, this answer has a workaround to their lack of addEventListener support.
There are several other issues with your code, though. For instance, to get the value of an input element, you use its value property, not its innerText property (it doesn't have any inner text, because it's a void element). You're also not returning after your alert about unequal string lengths, which you probably want to do since otherwise you go ahead and run the body of the function even though you did the alert...

Categories