how to prevent grid from changing size - javascript

I made grid 16x16 with borders for each cell, then i added a button to toggle on/off borders of those cells, but it also changes overall size of my grid. how do i prevent this?
in the future i want to implement "change size" button that will increase numbers of cells but not change grid size. I'm sure i need to define grid size somehow but i don know how. Whatever i try either messes up grid size or cell size or both
here is my code
const grid = document.getElementById('grid');
const size = document.getElementById('size');
const eraser = document.getElementById('eraser');
const color = document.getElementById('color');
const gridBorder = document.getElementById('grid-borders');
// grid
function makeGrid(number) {
grid.style.gridTemplateColumns = `repeat(${number}, 1fr)`;
grid.style.gridTemplateRows = `repeat(${number}, 1fr)`;
for (let i = 0; i < number * number; i++) {
let cell = document.createElement('div');
grid.appendChild(cell).setAttribute('id', 'box');
}
}
makeGrid(16);
// drawing on hover
color.addEventListener('click', function () {
grid.addEventListener('mouseover', function (e) {
e.target.style.backgroundColor = 'black';
});
});
// erase functionality
eraser.addEventListener('click', function () {
grid.addEventListener('mouseover', function (e) {
e.target.style.backgroundColor = 'white';
});
});
// gird borders
const allBoxes = document.querySelectorAll('#box');
gridBorder.addEventListener('click', function () {
for (let i = 0; i < allBoxes.length; i++) {
if (allBoxes[i].style.border === '1px solid black') {
allBoxes[i].style.border = 'none';
} else {
allBoxes[i].style.border = '1px solid black';
}
}
});
body {
height: 100vh;
}
#grid {
display: grid;
justify-content: center;
border: 1px solid #ccc;
}
#box {
padding: 1em;
border: 1px solid black;
}
#title {
display: flex;
align-items: flex-end;
justify-content: center;
height: 230px;
}
#container {
display: flex;
height: 60%;
width: 1204px;
align-items: flex-start;
justify-content: flex-end;
gap: 20px;
}
#menu {
display: flex;
flex-direction: column;
gap: 10px;
}
<!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>Etch-a-Sketch</title>
<link rel="stylesheet" href="style.css" />
<script src="script.js" defer></script>
</head>
<body>
<div id="title">
<h1>Etch-a-Sketch</h1>
</div>
<main id="container">
<div id="menu">
<button id="size">Canvas Size</button>
<button id="color">Color</button>
<button id="eraser">Eraser</button>
<button id="grid-borders">Grid Borders</button>
</div>
<div id="grid"></div>
</main>
</body>
</html>

You can use outline instead of border. Change your CSS box definition and remove the border there as it will be used to query your box class.
NOTE: I added a box and border class initially when creating your boxes as querying multiple elements should be targeted using a class and not a unique ID.
Now that you have the class targeted, you can simple toggle classes with the click event and use css to add/remove -> toggle the outlines state using its corresponding toggled class style.
I also added a conditional to check which event is being fired in your hover state listener, this will prevent the grid from being toggled so only its children, then boxes are toggled.
Let me know if you have any issues with the code or if this isn't working for your needs and I can either remove this answer or edit to tailor any other issues you may be having.
const grid = document.getElementById('grid');
const size = document.getElementById('size');
const eraser = document.getElementById('eraser');
const color = document.getElementById('color');
const gridBorder = document.getElementById('grid-borders');
// grid
function makeGrid(number) {
grid.style.gridTemplateColumns = `repeat(${number}, 1fr)`;
grid.style.gridTemplateRows = `repeat(${number}, 1fr)`;
for (let i = 0; i < number * number; i++) {
let cell = document.createElement('div');
grid.appendChild(cell).id = 'box';
// added class border and box
cell.classList.add('border'); //--> border will be used to toggle outline in css
cell.classList.add('box') //--> box used to query all the dynamically created box elements
}
}
makeGrid(16);
// drawing on hover
color.addEventListener('click', function() {
grid.addEventListener('mouseover', function(e) {
// make sure event.target is not the grid itself
e.target !== grid ? e.target.style.backgroundColor = 'black' : null;
});
});
// erase functionality
eraser.addEventListener('click', function() {
grid.addEventListener('mouseover', function(e) {
// make sure event.target is not the grid itself
e.target !== grid ? e.target.style.backgroundColor = 'white' : null;
});
});
// grid borders
const allBoxes = document.querySelectorAll('.box');
gridBorder.addEventListener('click', function() {
// added a forEach method to toggle classes in order to track click state and style using css styling
allBoxes.forEach(box => {
box.classList.toggle('no-border');
box.classList.toggle('border');
})
});
body {
height: 100vh;
}
#grid {
display: grid;
justify-content: center;
border: 1px solid #ccc;
}
.box {
/* removed the initial outline &/or border property here so it can be added and removed (toggled) using JS el.classList.toggle */
padding: 1em;
}
#title {
display: flex;
align-items: flex-end;
justify-content: center;
height: 230px;
}
#container {
display: flex;
height: 60%;
width: 1204px;
align-items: flex-start;
justify-content: flex-end;
gap: 20px;
}
#menu {
display: flex;
flex-direction: column;
gap: 10px;
}
/* added the following classes to be toggled using JS depending on state of gridBorders button */
.border {
outline: 1px solid black;
}
.no-border {
outline: none;
}
.black-bg {
background: black;
}
.white-bg {
background: white;
}
<!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>Etch-a-Sketch</title>
<link rel="stylesheet" href="style.css" />
<script src="script.js" defer></script>
</head>
<body>
<div id="title">
<h1>Etch-a-Sketch</h1>
</div>
<main id="container">
<div id="menu">
<button id="size">Canvas Size</button>
<button id="color">Color</button>
<button id="eraser">Eraser</button>
<button id="grid-borders">Grid Borders</button>
</div>
<div id="grid"></div>
</main>
</body>
</html>

Instead of using an actual border around grid items, use grid-gap prop along with background-color of the grid itself:
#grid {
...
grid-gap: 1px;
background-color: black;
}
Refer to the documentation for more info.

Related

React JS Upload Image when user pastes an Image

I am developing a chat application on React JS, and I want an image to be uploaded if I copy paste an image on the chatbox. How do I trigger this?
What I need basically is:
An event that will be triggered when action "Paste" is performed.
A way to upload image into a file type input element from the clipboard.
You need to add an event listener on paste event, and get the items from the clipboard and check if its type is an image.
<html lang="en">
<head>
<meta charset="utf-8" />
<title>HTML DOM - Paste an image from the clipboard</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/css/demo.css" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter&family=Source+Code+Pro&display=swap"
/>
<style>
.container {
/* Center the content */
align-items: center;
display: flex;
justify-content: center;
/* Misc */
height: 32rem;
padding: 1rem 0;
}
.key {
background-color: #f7fafc;
border: 1px solid #cbd5e0;
border-radius: 0.25rem;
padding: 0.25rem;
}
.preview {
align-items: center;
border: 1px solid #cbd5e0;
display: flex;
justify-content: center;
margin-top: 1rem;
max-height: 16rem;
max-width: 42rem;
}
</style>
</head>
<body>
<div class="container">
<div>
<div><kbd class="key">Ctrl</kbd> + <kbd class="key">V</kbd> in this window.</div>
<img class="preview" id="preview" />
<input id="file_input" type="file" />
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('paste', function (evt) {
const clipboardItems = evt.clipboardData.items;
const items = [].slice.call(clipboardItems).filter(function (item) {
// Filter the image items only
return /^image\//.test(item.type);
});
if (items.length === 0) {
return;
}
const item = items[0];
const blob = item.getAsFile();
const imageEle = document.getElementById('preview');
imageEle.src = URL.createObjectURL(blob);
let file = new File([blob], "file name",{type:"image/jpeg", lastModified:new Date().getTime()}, 'utf-8');
let container = new DataTransfer();
container.items.add(file);
document.querySelector('#file_input').files = container.files;
});
});
</script>
</body>
</html>
Resources
Pase an image from the clipboard
Set file value from blob

"style.display = 'flex/none'" not working

So I thought it would be pretty simple to implement an if statement to have an element appear or disappear at the click of a button.
After a couple of hours now, I have not gotten further than getting the element to disappear. It registers the second click(tested with a console.log), but the display property does not change back to 'flex'.
I've also already tried different variations of 'getElementById' and 'querySelector' during my sentence.
const edit = document.getElementById('edit-btn');
const fill = document.querySelector('#filler');
edit.addEventListener('click', popInOut(fill))
function popInOut(e){
if(e.style.display=='flex'){
e.style.display='none'
}else if(e.style.display=='none'){
e.style.display='flex'
}
}
The 'filler' element is a bootstrap column with this styling.
#filler{
display:flex;
flex-direction: column;
background-color: #1c1f22;
position:absolute;
top:40%;
height:50%;
}
hey it is your query selector which is causing the problem
and also you can use your filler directly in function because its declared
in global scope
const edit = document.getElementById("edit-btn");
const filler = document.getElementById("filler");
edit.addEventListener("click", fix);
function fix() {
if (filler.style.display === "none") {
filler.style.display = "flex";
} else {
filler.style.display = "none";
}
}
body {
font-family: sans-serif;
}
#filler {
display: flex;
/* flex-direction: column; */
background-color: whitesmoke;
position: absolute;
top: 30%;
left: 20%;
height: 50%;
gap: 1rem;
}
#filler div:nth-child(even) {
background: turquoise;
}
#filler div:nth-child(odd) {
background: yellowgreen;
}
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
<link href="style.css"/>
</head>
<body>
<div id="app">
<div id="filler">
<div>Hare krishna</div>
<div>Radhe Shyam</div>
<div>Sita Ram</div>
</div>
<button id="edit-btn">Edit</button>
</div>
<script src="index.js"></script>
</body>
</html>

One event handler function not getting triggered after the execution of another event handler function defined in the same js file

I am trying to build a static todo app, this app stores the todo list in localStorage (browser memory).
there are 3 elements related to the error - 1 element is causing the error and the other 2 being affected by it.
a hide complete checkbox - hides the completed tasks when checked. (element causing the error)
complete todo checkbox - mark a todo as completed when checked.
delete note button - deletes a note.
I have attached the code files and screenshot, to get an overall idea about the problem and the code.
along with this, I have stored my code in this repo: https://github.com/AbhishekTomr/Todo
Problem - Once the hide completed checkbox is triggered, the complete todo and delete todo mechanism stops working.
Can someone please help me in fixing the code or let me know what is causing the issue?
Screenshot:
//js file : todoFunctions.js
//function for getting value stored in todoList
let getTodo = function(){
return Boolean(localStorage.getItem("todo"))?JSON.parse(localStorage.getItem("todo")):[];
}
//function to add or render any list on the screen
let render = function(list){
document.querySelector("#td").innerHTML = "";
list.forEach(function(item){
newTask(item);
}
)
}
//function for saving the task in the local storage
let saveTask = function(todo){
let tdList = JSON.stringify(todo);
localStorage.setItem("todo",tdList); //setting value for the first time or updating it
}
let newTask = function(node)
{
let li = document.createElement("li");
let status = document.createElement("input");
let remove = document.createElement("button");
li.textContent = node.td;
remove.setAttribute("class","remove");
remove.textContent="Delete";
status.setAttribute("type","checkbox");
status.setAttribute("class","status");
status.checked = node.status;
li.style.textDecoration = (node.status)?"line-through":"none";
let div = document.createElement("div");
div.setAttribute("class","task");
div.setAttribute("id",node.index);
div.appendChild(status);
div.appendChild(li);
div.appendChild(remove);
document.querySelector("#td").appendChild(div);
document.getElementById("new-task").value = ""; //clearing the input feild
}
//function for adding a new task
let addTask = function(todo){
let td = document.getElementById("new-task").value;
let status = false;
let node = {td : td,status : status};
todo.push(node);
saveTask(todo); // saving it to local storage
newTask(node);
}
// function for searching out task
let searchTask = function(todo,e){
let searchList = todo.filter(function(item){
return item.td.includes(e.target.value);
})
render(searchList); // showing the searched terms on the go..
}
//funtion to delete task
let deleteTodo=function(e,index,todo){
e.target.parentElement.remove();
todo.splice(index,1);
saveTask(todo);
}
//funtion for completing and undoing a task
let changeStatus = function(e,index,todo){
let state = e.target.checked;
let td = e.target.parentElement.children[1];
td.style.textDecoration = (state)?"line-through":"none";
todo[index].status = state;
saveTask(todo);
}
//function for hiding complete task
let hideCompleted = function(e,todo){
if(e.target.checked)
{
let filterLst = todo.filter(function(item){
return !item.status;
})
render(filterLst);
}else{
render(todo);
}
}
//js file :main.js
let todo = getTodo(); // get the todo List from storage
render(todo); // display the initial todo List
//functionality for the different events
document.getElementById("add-task").addEventListener("click",function(e){ //event when the add new task button is pressed
addTask(todo); //funtion for adding new task and displaying it on page
})
document.getElementById("search-txt").addEventListener("input",function(e){ //event for text typed in seach bar
searchTask(todo,e); //funtion for searching the tasks and displaying it on page
})
//event to delete todo
let btns = document.querySelectorAll(".remove");
btns.forEach(function(item,index){
item.addEventListener("click",function(e){
deleteTodo(e,index,todo);
})
})
//event for complete/uncomplete task
let check = document.querySelectorAll(".status");
check.forEach(function(item,index){
item.addEventListener("change",function(e){
changeStatus(e,index,todo);
console.log("i am triggered");
})
})
document.querySelector("#hide-check").addEventListener("change",function(e){ //event when hide completed is checked and unchecked
hideCompleted(e,todo);
})
/* css file : main.css */
*{
margin: 0;
padding: 0;
box-sizing: border-box;
/* border: 1px solid black; */
font-family: monospace;
}
main{
margin: 5px;
}
h1{
font-size: 3rem;
font-weight: bold;
font-variant: small-caps;
display: inline-block;
width: 250px;
text-align: center;
}
#td-options{
display: flex;
flex-flow: column nowrap;
justify-content:space-around;
align-items: flex-start;
font-size: 1.2rem;
width: 250px;
}
#search,#search input{
width: 100%;
height: 100%;
text-align: center;
border: 2px solid black;
}
#hide-completed{
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
font-size: 1.2rem;
margin: 5px 0;
}
#hide-check{
margin: 10px;
}
#todo{
/* border: 3px solid pink; */
background-color: pink;
margin: 1rem 0;
display: inline-block;
padding: 10px;
}
.task{
border: 1px solid green;
display: flex;
justify-content: flex-start;
align-items: center;
font-size: 1.2rem;
}
.task li{
list-style-position: inside;
margin: .5rem;
text-transform: capitalize;
}
.task button{
padding: 0 .5rem;
border-radius: 2px;
background-color: gray;
color: whitesmoke;
}
.task button:hover{
cursor: pointer;
}
#add-todo{
display: flex;
flex-flow: row nowrap;
width: 255px;
justify-content: space-between;
height: 25px;
}
#add-todo button{
padding: 0 .5rem;
border-radius: 3px;
}
<!-- html file: index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="main.css">
<title>Todo</title>
</head>
<body>
<main>
<!-- mainheading -->
<h1>My Todo App</h1>
<!--todoOptions-->
<div id="td-options">
<div id="search">
<input type="text" id="search-txt" placeholder="search your Todo">
</div>
<div id="hide-completed">
<label for="hide-check">
Hide Completed Tasks
</label>
<input type="checkbox" id="hide-check" name="hide">
</div>
</div>
<!-- todo list -->
<div id="todo">
<ol id="td"></ol>
</div>
<!-- add todo -->
<div id="add-todo">
<input type="text" placeholder="Add A New To Do Task" id="new-task">
<button id="add-task">Add ToDo</button>
</div>
<!-- js files -->
<script src="todoFunctions.js"></script>
<script src="main.js"></script>
</main>
</body>
</html>
The function render goes through a list and creates the relevant elements for a task to show it on the screen and then appends them.
At the start a list is collected from localstorage and then all the required event listeners are added.
When hideCompleted is called it creates a list from the remaining elements that are not completed, or just uses the complete todo list, and re-renders it. This creates all the elements OK so everything looks alright on the screen.
BUT no event listeners are added so the delete button and so on do not do anything.
My suggestion would be to make the event listener creation code for .remove etc into a function. Call that on start up and when you recreate the list on screen.

eventListener is not running after the page is updated

I have written this code to get the squares of a grid to change their background color to black upon a mouseover event. It works when the page initially loads, but if I create a new grid the mouseover event no longer works.
I updated the original post with a snippet. Sorry I didn't do that from the beginning.
let number = 16;
makeGrid(number);
function makeGrid(number) {
for (let i=0; i < number; i++) {
for (let j=0; j < number; j++) {
const rows = document.createElement('div');
const container = document.getElementById('container')
rows.setAttribute('class', 'rows');
container.appendChild(rows);
}
}
container.style.gridTemplateColumns = `repeat(${number}, 1fr)`;
container.style.gridTemplateRows = `repeat(${number}, 1fr)`;
}
//create new grid with on button
let newGrid = document.getElementById('newGrid');
newGrid.addEventListener('click', () => {
let number = prompt('Enter a number');
let container = document.getElementById('container');
container.textContent = '';
makeGrid(number);
})
//change background color to black
let changeClass = document.querySelectorAll('.rows');
changeClass.forEach((item) => {
item.addEventListener('mouseover', e => {
item.style.backgroundColor = 'black';
})
})
body {
background-color: rgb(5, 51, 5) ;
}
#container {
margin: auto;
width: 500px;
height: 500px;
display: grid;
border-style: solid;
border-width: thin;
border-color: lightslategray;
background-color: white;
}
.rows{
}
.black { background-color: black;
}
#header {
text-align: center;
}
#button {
text-align: center;
}
#footer {
text-align: center;
}
#newGrid {
background-color: lightgray;
color: darkcyan;
font-size: 20px;
padding: 12px 28px;
border-radius: 0px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Etch-a-Sketch</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1 id='header'>Etch-a-Sketch</h1>
<br>
<div id='button'>
<button id='newGrid' class='button'>New Grid</button>
</div>
<br>
<br>
<div id='container'></div>
<br>
<footer id='footer'>Made by: Joe Maniaci</footer>
<script src="main.js"></script>
</body>
</html>
When you query the DOM with document.querySelectorAll('.rows') and add the event listeners, there is only one "grid" in the DOM at that time. When a "grid" is subsequently added to the DOM, as triggered by the user's click event, you must instantiate event listeners on the newly added DOM nodes too.
A way to avoid this problem and a better approach overall in your situation is to use delegated event listeners. For example:
document.addEventListener('mouseover', e=>{
if(e.target.matches(‘.myClickableItemClass’){
e.target.style.backgroundColor = 'black';
}
}
Learn more about event delegation here: https://medium.com/#bretdoucette/part-4-what-is-event-delegation-in-javascript-f5c8c0de2983

click a button to delete itself and its parent div

--- UPDATED QUESTION ---
Thanks for all the answers. I wrote the JS code to delete the parent div when clicking its corresponding button in my JS PRACTICE!!!
However, the same JS code does not work in my real JS project where all the parent div are created dynamically. The complete code can be found below.
There is no error but the JS code just does not work. Any ideas?
BELOW IS THE SIMPLIFIED **REAL JS PROJECT ** COMPLETE CODE
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload Imgs</title>
<style type="text/css">
.container {
width: 100%;
}
.display-area {
width: 100%;
height: auto;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
}
img {
max-width: 100%;
}
.image-preview {
width: 80%;
min-height: 300px;
border: 2px dashed #dddddd;
display: block;
/*default text*/
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: #cccccc;
}
.newbtns {
border: 0;
background: lightgrey;
text-shadow: 1px 1px 1px white;
border: 1px solid #999;
position: absolute;
display: block;
}
</style>
</head>
<body>
<div class="container">
<div id='inputFiles'><input type="file" class="file" name="image_uploads" accept="image/png, image/jpeg, image/jpg"
multiple></div>
<div class="display-area" id='imgDisplay'>
</div>
<div id="defaultContent">
<p>No images</p>
</div>
<button type="button" value="Reload page" onclick="window.location.reload()">Reload Page</button>
</div>
</div>
</body>
<script>
var input = document.querySelector('input');
var uploadBox = document.getElementById('uploadBox');
var defaultContent = document.getElementById('defaultContent');
var imgDisplay = document.getElementById('imgDisplay')
//upload & preview
input.addEventListener('change', function () {
var imgFiles = input.files;
defaultContent.style.display = 'none';
for (var i = 0; i < imgFiles.length; i++) {
var imgDiv = document.createElement('div');
imgDiv.className = 'imgBox';
imgDiv.id = 'box' + i;
imgDiv.style.width = "20%";
var images = document.createElement('img');
images.src = URL.createObjectURL(imgFiles[i]);
var newbtn = document.createElement("button");
newbtn.type = "button";
newbtn.className = "newbtns";
newbtn.innerHTML = "X";
newbtn.style.color = "orange";
newbtn.style.background = 'red';
newbtn.id = 'newbtn' + i;
imgDiv.appendChild(newbtn);
imgDiv.appendChild(images);
imgDisplay.appendChild(imgDiv);
}
});
allButtons = document.getElementsByTagName('button');
for (var n = 0; n < allButtons.length; n++) {
if (allButtons[n].getAttribute('id') === 'newbtn' + n) {
allButtons[n].onclick = function () {
this.parentNode.parentNode.removeChild(this.parentNode);
}
} else { };
}
</script>
</html>
you can do something like this:
const buttonOne = document.getElementById('btn1');
const buttonTwo = document.getElementById('btn2');
buttonOne.addEventListener("click", () => deleteElementAndThisChildNodes('box1'))
buttonTwo.addEventListener("click", () => deleteElementAndThisChildNodes('box2'))
function deleteElementAndThisChildNodes(parentId) {
document.getElementById(parentId).remove()
}
To each of your button elements add onclick="DeleteParent(this)" then outside of your dynamic divs include the following:
<script type="text/javascript">
function DeleteParent(button){
button.parentElement.remove();
}
</script>
You can do this:
const display = document.getElementById("imgdisplayarea");
display.addEventListener("click", e => {
if(e.target.tagName === 'BUTTON'){
//if an element within a display div for a button, remove your father
e.target.parentNode.remove();
}
});
Here is a very simple example that works exactly how you want it (based on your question):
function disable() {
document.getElementById("demo").style.display = "none";
}
<div id="demo">
<button onclick="disable()">
Click Me
</button>
<h3>
This is part of the div
</h3>
</div>

Categories