im creating to-do list without any tutorial and i stucked when i tried to code clear button. I have a problem because this code removing only half of 'li' items in my list. I checked length of document.querySelectorAll('li) and it return correct value of list length , and i think in each loop execution i delete first element because document.querySelector('li) return only first element. Could you help me? And another question : Is somewhere in web program that can i see step by step exection of my code with DOM? I found few sites but there i can only debug code without html and css.
There is my code:
let clear = document.querySelector('.clear');
let input = document.querySelector('.input');
let submit = document.querySelector('.submit');
let list = document.querySelector('.list');
submit.addEventListener('click', function() {
const el = document.createElement('li');
list.appendChild(el);
let items = document.querySelectorAll('li');
for (i = 0; i < items.length; i++) {
items[items.length - 1].textContent = input.value;
}
});
clear.addEventListener('click', function() {
console.log(document.querySelectorAll('li').length);
for (i = 0; i < document.querySelectorAll('li').length; i++)
list.removeChild(document.querySelector('li'));
console.log(list);
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
max-height: 100vh;
max-width: 90vw;
}
h1 {
text-align: center;
}
.wrap {
padding: 50px;
border: 2px solid black;
border-radius: 20%;
background-color: red;
}
.list {
width: 100%;
margin-left: 100px;
font-size: 2rem;
font-weight: bold;
}
<!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="wrap">
<h1>To do list</h1>
<input type="text" placeholder="Add an item!" class="input" />
<button class="submit">Submit</button>
<button class="clear">Clear List</button>
</div>
<ol class="list"></ol>
<script src="script.js"></script>
</body>
</html>
In your case, your loop was calculating its length each time of the loop while i was still incrementing
I you had two elements, it would have deleted the first item, then at the second loop, the list would have a length of 1 and i would be equals to 1 so the loop would break
You can use a for of loop instead of a for i loop and remove the child by reference
clear.addEventListener('click', function() {
const childs = document.querySelectorAll('li')
for (const child of childs){
list.removeChild(child)
}
});
Note : this can also be done using a for i loop if you instantiate the list
clear.addEventListener('click', function() {
const childs = document.querySelectorAll('li')
for (let i = 0; i < childs.length; i++){
list.removeChild(childs[i])
}
});
I invent a new way to solve it
clear.addEventListener('click', function () {
for (i = 0; i < document.querySelectorAll('li').length + i; i++)
list.removeChild(document.querySelector('li'));
console.log(list);
});
In each iteration I add 'i' . So i update length
Related
I'm running my code in node.js I've seen that running code in node could play a part. But this was never a problem. It keeps saying my variable that points to my element 'document is not defined'. I don't know what I'm doing wrong, I'm linking it correctly but still confused I also tried putting my scripts on the bottom and top with defer.
let seconds = 00;
let tens = 00;
let appendTens = document.querySelector('#tens');
let appendSeconds = document.querySelector('#seconds');
let stop = document.querySelector('#button-stop');
let start = document.querySelector('#button-start');
let reset = document.querySelector('#button-reset');
let interval; //store timer values
// this function will run when start is clicked
const startTimer = () =>{
tens++
if(tens < 9){
appendTens.textContent = `0${tens}`
}
if(tens > 9){
appendTens.textContent = tens;
}
if(tens > 99){
seconds++
appendSeconds.textContent = `0${seconds}`;
tens = 0;
appendTens.textContent = "0" + 0;
}
if(seconds > 9){
appendSeconds.textContent = seconds;
}
};
start.onclick = function(){
interval = setinterval(startTimer)
}
<!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>Stopwatch</title>
<link rel="stylesheet" href="stopwatch.css">
</head>
<body>
<div class="wrapper">
<h1>Stopwatch</h1>
<h2>Vanilla JavaScript Stopwatch</h2>
<p><span id="seconds">00</span>:<span id="tens">00</span></p>
<button id="button-start">Start</button>
<button id="button-stop">Stop</button>
<button id="button-reset">Reset</button>
</div>
<script type="module" src="stopwatcj.js"></script>
</body>
</html>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-self: center;
background-color: rgb(248, 180, 55);
}
.wrapper {
margin-top: 10%;
text-align: center;
color: #fff;
}
.wrapper button {
background-color: rgb(248, 180, 55);
border: 1px solid white;
padding: 10px 20px;
color: #fff;
}
.wrapper button:hover {
cursor: pointer;
opacity: 30%;
}
document which is a part of Html DOM is not a part of nodejs. Since you might be using nodejs to compile your js code that's why you are getting this error. Please try to run this simply in browser.
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.
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
Please, take a look at this fiddle (I am using Vue.js to generate lots of DOM nodes here, but my question doesn't seem to be a Vue related issue): https://jsfiddle.net/dmaevsky/kswj23r1/117/ .
When I am monitoring performance using Chrome's performance tool while pressing the button in a rapid succession, I am seeing 42ms 'Update Layer Tree' rendering delay, which makes sense, since the stuff is moving on page, so why not (I am still wondering btw whether there's a way to eliminate this).
However, things get awry when I uncomment the line 30, thus manually setting the td's styles in Javascript:
//nr = 50, nc = 50;
for (let i = 0; i < nr; i++) {
for (let j = 0; j < nc; j++) {
this.$refs[i + ':' + j][0].style.color = 'blue';
}
}
When I now monitor the performance I see all 2500 TD nodes added to the "Recalculating styles" in Chrome when 'Shift down' button is pressed. I just cannot see why that would make any sense ? Using a class instead of setting styles manually does not cause this to happen.
This is just an attempt to understand the browser's style invalidation logic, not a real application, so the number of DOM nodes here is intentionally kept higher than one would reasonably need in a real world application, though close enough.
UPDATE:
This actually DOES seem to be a Vue.js issue finally. I have re-written the code in pure JS (complete HTML below), and I do not observe the same effect anymore.
<!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>Create table test</title>
<style>
body {
font-family: Helvetica;
}
#app {
background: #fff;
padding: 20px;
}
#app {
position: relative;
}
#pane {
position: absolute;
}
.table {
table-layout: fixed;
border-collapse: collapse;
margin: 0;
padding: 0;
border: hidden;
width: 8000px;
height: 1600px;
}
.cell {
width: 100px;
height: 20px;
line-height: 16px;
box-sizing: border-box;
padding: 2px;
background: white;
overflow: hidden;
}
</style>
</head>
<body>
<noscript>
<strong>Need JS</strong>
</noscript>
<button onclick="shift()">Shift down</button>
<div id="app">
<div id="pane"></div>
</div>
</body>
</html>
<script>
var pos = 0;
function shift() {
pos++;
document.getElementById('pane').style.top = pos + 'px';
}
function createTable(nr, nc) {
let table = document.createElement('table');
table.className = 'table';
for (let i = 0; i < nr; i++) {
let tr = document.createElement('tr');
for (let j = 0; j < nc; j++) {
let td = document.createElement('td');
let span = document.createElement('span');
span.innerHTML = i + j;
td.className = 'cell';
td.style.color = 'blue';
td.appendChild(span);
tr.appendChild(td);
}
table.appendChild(tr);
}
return table;
}
document.getElementById('pane').appendChild(createTable(80, 80));
</script>
Question to Vue.js experts then: what does Vue do to DOM to warrant the observed behavior in the first fiddle???
I'm working on a bootcamp and some guys and I have created a group. We are trying to dynamically create html elements using pure JavaScript. The elements are there but we get an error running and we want to be able to go back and use/grab those elements later. Any advice on what we are doing wrong would be much appreciated.
The HTML:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JavaScript Grid</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="grid"></div>
<script src="js/script.js" type="text/javascript"></script>
</body>
</html>
The js:
(function() {
var grid = document.getElementById("grid");
for (var i = 0; i < 5; i++) {
var temp = document.createElement("DIV");
temp.className = "row";
grid.appendChild(temp);
}
var row = grid.getElementsByClassName("row");
})();
The CSS:
.grid {
height: 100%;
width: 100%;
overflow: auto;
background-color: black;
}
.row {
overflow: auto;
}
.box {
padding: 0;
box-sizing: border-box;
background-color: white;
height: 100px;
float: left;
}
Thanks!
I see no error on your code.
your divs are collected and you can play with them around. take a look to the jsfiddle and open your console. (press F12 or inspect element on right mouse click).
(function() {
var grid = document.getElementById("grid");
for (var i = 0; i < 5; i++) {
var temp = document.createElement("DIV");
temp.className = "row";
grid.appendChild(temp);
}
var row = grid.getElementsByClassName("row");
row[row.length - 1].style.borderColor = "blue";
row[0].style.borderColor = "red";
})();
and the css
.row {
background.color:#f2f2f2;
border: 1px solid #dadada;
margin-bottom:10px;
height:20px;
}
http://jsfiddle.net/hu7a7k0s/