how do I stop my grid from going outside its container div? - javascript

I have this interactive grid and when I hover over the cell it colors the inside divs background color but its is coloring past the container its in, how do I get it to only color inside the container? it seems to stop at the containers top and sides but the bottom can be colored downwards the whole page. This is my code
***HTML**
<!DOCTYPE html>
<html>
<head>
<title> Claudias Etch-A-Sketch</title>
<link rel= "stylesheet" href="style.css">
</head>
<body>
<section class="back">
<h1>
<center> Claudia Etch-A-Sketch!</center>
</h1>
</section>
<section>
<center><div>
<button class="Black">Black</button>
<button class="Pink">Pink</button>
<button class="Clear">Eraser</button>
</div></center>
</section>
<section>
<center><div id="container"> </div></center>
</section>
</body>
<script src ="javascript.js"> </script>
</html>
**JAVASCRIPT**
const container = document.getElementById("container");
const btn = document.querySelector('button');
let y = document.querySelectorAll('button');
y.forEach(button => {
button.addEventListener('click', () => {
let choice = button.innerHTML;
switch (choice) {
case "Pink":
random();
break;
case "Black":
blackchange();
break;
case "Eraser":
reset();
break;
}
});
});
var currentMouseoverFunction = function() {};
function blackchange() {
container.removeEventListener('mouseover', currentMouseoverFunction);
currentMouseoverFunction = function(e) {
e.target.classList.remove('pink');
e.target.classList.remove('reset');
e.target.classList.add('black');
};
container.addEventListener('mouseover', currentMouseoverFunction);
};
function random() {
container.removeEventListener('mouseover', currentMouseoverFunction);
currentMouseoverFunction = function(e) {
e.target.classList.remove('black');
e.target.classList.remove('reset');
e.target.classList.add('pink');
};
container.addEventListener('mouseover', currentMouseoverFunction);
};
function reset() {
container.removeEventListener('mouseover', currentMouseoverFunction);
currentMouseoverFunction = function(e) {
e.target.classList.remove('black');
e.target.classList.remove('pink');
e.target.classList.add('reset');
};
container.addEventListener('mouseover', currentMouseoverFunction);
};
function makeRows(rows, cols) {
container.style.setProperty('--grid-rows', rows);
container.style.setProperty('--grid-cols', cols);
for (c = 0; c < (rows * cols); c++) {
let cell = document.createElement("div");
container.appendChild(cell).className = "griditem";
};
};
makeRows(24, 24);
**CSS**
#container{
width:500px;
height:500px;
margin-top: 30px;
margin-bottom: 300px;
background:#eee;
}
#container div {
position: center;
float:left;
height: 35px;
width: 35px
}
.black {
background: black;
}
.pink {
background: pink;
}
.reset {
background: transparent;
}

The divs in #container are floated left with float:left; in the CSS. Float takes elements out of the normal layout of the page, so the #container's height doesn't expand with its content. To fix this, remove the explicit height:500px; height definition of #container, and add a clear element after the .griditem divs inside #container.
const container = document.getElementById("container");
const btn = document.querySelector('button');
let y = document.querySelectorAll('button');
y.forEach(button => {
button.addEventListener('click', () => {
let choice = button.innerHTML;
switch (choice) {
case "Pink":
random();
break;
case "Black":
blackchange();
break;
case "Eraser":
reset();
break;
}
});
});
var currentMouseoverFunction = function() {};
function blackchange() {
container.removeEventListener('mouseover', currentMouseoverFunction);
currentMouseoverFunction = function(e) {
e.target.classList.remove('pink');
e.target.classList.remove('reset');
e.target.classList.add('black');
};
container.addEventListener('mouseover', currentMouseoverFunction);
};
function random() {
container.removeEventListener('mouseover', currentMouseoverFunction);
currentMouseoverFunction = function(e) {
e.target.classList.remove('black');
e.target.classList.remove('reset');
e.target.classList.add('pink');
};
container.addEventListener('mouseover', currentMouseoverFunction);
};
function reset() {
container.removeEventListener('mouseover', currentMouseoverFunction);
currentMouseoverFunction = function(e) {
e.target.classList.remove('black');
e.target.classList.remove('pink');
e.target.classList.add('reset');
};
container.addEventListener('mouseover', currentMouseoverFunction);
};
function makeRows(rows, cols) {
container.style.setProperty('--grid-rows', rows);
container.style.setProperty('--grid-cols', cols);
for (c = 0; c < (rows * cols); c++) {
let cell = document.createElement("div");
container.appendChild(cell).className = "griditem";
};
let cell = document.createElement("span");
container.appendChild(cell).style.display = "block";
cell.style.clear = "both";
};
makeRows(24, 24);
#container {
width: 500px;
margin-top: 30px;
margin-bottom: 300px;
background: #eee;
}
#container div {
position: center;
float: left;
height: 35px;
width: 35px
}
.black {
background: black;
}
.pink {
background: pink;
}
.reset {
background: transparent;
}
<section class="back">
<h1>
<center> Claudia Etch-A-Sketch!</center>
</h1>
</section>
<section>
<center>
<div>
<button class="Black">Black</button>
<button class="Pink">Pink</button>
<button class="Clear">Eraser</button>
</div>
</center>
</section>
<section>
<center>
<div id="container"> </div>
</center>
</section>
As a side note, your .griditem divs are 35px wide each, so you should make the #container 840px wide if you want this to be a 24x24 square grid.

Your code seems a little strange in that it uses some outdated solutions.
First, don't use <center>, it's obsolete. You should keep the appearance (including alignment) of your page inside the CSS.
Second, you don't have to use float (hardly ever). It is a common source of bugs like the one you're experiencing. Instead you could use flexbox, so your container would look like:
#container{
display: flex; /* Puts all children inside a flexbox */
flex-wrap: wrap; /* And allows the flexbox to span multiple lines */
margin-top: 30px;
margin-bottom: 300px;
background:#eee;
}
Now, for the children, this solution allows them to distort slightly to adjust to the width of the parent:
.griditem {
flex-basis: 35px; /* Set a base width */
flex: 1; /* Allows it to stretch */
height: 35px;
}
Flexbox will prove to be a very important tool in your toolset. Give it a go!
Edit: a simpler solution - sorry.
I'm usually so excited to tell people about flexbox that I sometimes overlook simpler solutions. As I see it, the reason you resorted to float is because you added a ton of divs and they stacked on top of each other - divs are "blocks" and blocks take up the hole line.
You could just have created them as spans, which as "inlines" concatenate one after the other (like letters) and do the line-break naturally. But then setting dimensions for them wouldn't work - they take their width from their content.
The simpler solution by far would be to give the divs the rule display: inline-block. Now they'd have the right size, would be inline and wrap nicely.
But then you'd find there's a mysterious space separating the lines, which is not margin nor padding. That's actually whitespace between the divs. Ugh!
The easiest way to fix that is giving the container font-size: 0 - the space is there, it just doesn't take up... space.
Anyway, the css would look like:
#container{
font-size: 0;
margin-top: 30px;
margin-bottom: 300px;
background:#eee;
}
.griditem {
display: inline-block;
height: 35px;
width: 35px;
}

Related

Mobile browsers add a gap at the end of final element via css gap property

I'm back on Stack Overflow after a long time because I'm truly stuck at an issue I cannot get around even after hours piling up in front of the screen.
I have made a simple widget using CSS + HTML + JavaScript which scrolls elements in an overflowing-x container.
It works in a simple way, there is JavaScript code that adds a 205 value to the property scrollLeft of the overflowing container. The number comes from the fixed width of the images + the gap value which is 5px. Here is the code:
HTML:
<div id="controlContainer">
<a class="adButton" onclick="Scroll(-1)">❮</a>
<div id="topics">
<div class="adItem" onclick="ChangeTopic(1)">
<p>History</p>
<img src="images/other_samples/hundredgates.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(2)">
<p>Oceans</p>
<img src="images/other_samples/goldensea.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(3)">
<p>Sports</p>
<img src="images/other_samples/kite_surf.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(4)">
<p>Travel</p>
<img src="images/other_samples/antiparos_church.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(5)">
<p>Nightlife</p>
<img src="images/other_samples/nightlife.png">
</div>
</div>
<a class="adButton" onclick="Scroll(1)">❯</a>
</div>
CSS:
#controlContainer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 20px;
}
#topics {
display: inherit;
gap: 5px;
overflow:hidden;
white-space: nowrap;
scroll-behavior: smooth;
}
.adItem {
position: relative;
}
.adItem img {
width: 200px;
height: 200px;
object-fit: cover;
border-radius: 20px;
}
.adItem p {
position: absolute;
left: 16px;
top: 8px;
text-align: center;
color: #ffff;
font-family: Georgia, "Times New Roman", Times, serif;
font-size: 50px;
margin: 0px;
user-select: none;
pointer: default:
}
And finally JS, which still needs some work tbh:
var LastClick;
var Delay = 300;
var SelectedElement;
var adControl;
var currentScroll;
window.onload = function () {SelectedElement = document.getElementById("ad1"); adControl = document.getElementById("topics"); resizeController();};
window.onresize = debounce(() => resizeController());; //resize the container when the screen does
//window.addEventListener('DOMContentLoaded', (event) => {SelectedElement = document.getElementById("ad1")});
function Scroll(n) {
if (LastClick >= (Date.now() - Delay)) {
return;
}
if (n == 1) {
adControl.scrollLeft += 205;
checkPos();
} else if (n == -1) {
adControl.scrollLeft -= 205;
checkPos();
}
LastClick = Date.now();
console.log(adControl.scrollLeft);
}; // This function is what's handling scrolling. THey are called via onclick events on the HTML Button elements
function checkPos() {
var elementWidth = adControl.scrollLeft;
if (elementWidth % 5 === 0) {
// do nothing
} else {
var newWidth = Math.ceil(elementWidth/5)*5;
console.log("old width: %s, new width: %s", elementWidth, newWidth)
adControl.scrollLeft = newWidth;
}
}; //Some position checks... it basically calculates if scrollLeft is divisible by 5, because all images are 200px long plus the 5px gap, so that should always be a multiple of 5.
function ChangeTopic(id) {
SelectedElement.style.display = "none";
SelectedElement = document.getElementById("ad" + id);
SelectedElement.style.display = "flex";
}; //That just changes the topic of another element.
function debounce(func, timeout = 1000){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}; //This is a debounce function for the resize event, it prevents it from firing it too much.
function resizeController() {
adControl.style.maxWidth = "";
var elementWidth = adControl.offsetWidth;
var scroll = adControl.ScrollLeft;
var itemNo = (Math.floor(elementWidth / 200))
if (itemNo > 3) {
itemNo = 3
};
var newWidth = (itemNo*200);
newWidth = newWidth+(5*itemNo)
adControl.style.maxWidth = (newWidth + "px");
if (currentNo = itemNo) {
adControl.scrollLeft = scroll;
}
}; //resizes the container if need be (for mobile or tablet devices)
It actually works very well on Desktop, but on mobile, the CSS gap property which adds the gap between the images also adds a gap at the last element, like this:
That's even when I use a different browser from Firefox, like Chrome
On desktop, this gap does not exist, regardless of browser once again:
What is this? And how can I solve it? The main problem this causes is it will scroll in that tiny 5 gap space, which throws the position of my elements out of place, making them look like this:
I've thought of different methods like checking the property of ScrollLeft to detect when the view is out of the elements, but that property is completely unpredictable. For instance, when I scroll to the beginning of the element, it's not going to be necessarily zero, and even if I reach the end, the 205 value will be added even if there is not any space on the container. So that isn't reliable.
In short, I'd either need some kind of method to keep that gapping behaviour in check or solve the root problem altogether.
Yes... I'm not using any framework at all, my entire project is built on pure JavaScript. I'm not sure why I did this to myself, but oh well, all the challenge I guess.
Try and resize your font on the paragraph elements in your
div class="adItem" it appears to be overlapping the container and causing what would appear to be extra padding and i don't think it's happening on the others because the text is not long enough on others.
var LastClick;
var Delay = 300;
var SelectedElement;
var adControl;
var currentScroll;
window.onload = function () {SelectedElement = document.getElementById("ad1"); adControl = document.getElementById("topics"); resizeController();};
window.onresize = debounce(() => resizeController());; //resize the container when the screen does
//window.addEventListener('DOMContentLoaded', (event) => {SelectedElement = document.getElementById("ad1")});
function Scroll(n) {
if (LastClick >= (Date.now() - Delay)) {
return;
}
if (n == 1) {
adControl.scrollLeft += 205;
checkPos();
} else if (n == -1) {
adControl.scrollLeft -= 205;
checkPos();
}
LastClick = Date.now();
console.log(adControl.scrollLeft);
}; // This function is what's handling scrolling. THey are called via onclick events on the HTML Button elements
function checkPos() {
var elementWidth = adControl.scrollLeft;
if (elementWidth % 5 === 0) {
// do nothing
} else {
var newWidth = Math.ceil(elementWidth/5)*5;
console.log("old width: %s, new width: %s", elementWidth, newWidth)
adControl.scrollLeft = newWidth;
}
}; //Some position checks... it basically calculates if scrollLeft is divisible by 5, because all images are 200px long plus the 5px gap, so that should always be a multiple of 5.
function ChangeTopic(id) {
SelectedElement.style.display = "none";
SelectedElement = document.getElementById("ad" + id);
SelectedElement.style.display = "flex";
}; //That just changes the topic of another element.
function debounce(func, timeout = 1000){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}; //This is a debounce function for the resize event, it prevents it from firing it too much.
function resizeController() {
adControl.style.maxWidth = "";
var elementWidth = adControl.offsetWidth;
var scroll = adControl.ScrollLeft;
var itemNo = (Math.floor(elementWidth / 200))
if (itemNo > 3) {
itemNo = 3
};
var newWidth = (itemNo*200);
newWidth = newWidth+(5*itemNo)
adControl.style.maxWidth = (newWidth + "px");
if (currentNo = itemNo) {
adControl.scrollLeft = scroll;
}
}; //resizes the container if need be (for mobile or tablet devices)
#controlContainer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 20px;
}
#topics {
display: inherit;
gap: 5px;
overflow:hidden;
white-space: nowrap;
scroll-behavior: smooth;
}
.adItem {
position: relative;
}
.adItem img {
width: 200px;
height: 200px;
object-fit: cover;
border-radius: 20px;
}
.adItem p {
position: absolute;
left: 16px;
top: 8px;
text-align: center;
color: #ffff;
font-family: Georgia, "Times New Roman", Times, serif;
font-size: 50px;
margin: 0px;
user-select: none;
pointer: default:
}
<div id="controlContainer">
<a class="adButton" onclick="Scroll(-1)">❮</a>
<div id="topics">
<div class="adItem" onclick="ChangeTopic(1)">
<p>History</p>
<img src="images/other_samples/hundredgates.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(2)">
<p>Oceans</p>
<img src="images/other_samples/goldensea.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(3)">
<p>Sports</p>
<img src="images/other_samples/kite_surf.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(4)">
<p>Travel</p>
<img src="images/other_samples/antiparos_church.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(5)">
<p>Nightlife</p>
<img src="images/other_samples/nightlife.png">
</div>
</div>
<a class="adButton" onclick="Scroll(1)">❯</a>
</div>

Randomize color of each div continuously

I'm trying to replicate what is seen in this gif:
Grid Layout
I have to randomize the colors of each div inside the grid-container every second using setInterval() and querySelectorAll() after clicking the button on the top left corner, and display the current time as seen in the GIF. I know that the colors are randomly selected and actually use the CSS rgba() function. Here's what I have in my html file:
<!DOCTYPE html>
<html>
<head>
<style>
.item1 { grid-area: header; }
.item2 { grid-area: menu; }
.item3 { grid-area: main; }
.item4 { grid-area: right; }
.item5 { grid-area: footer; }
.grid-container {
display: grid;
grid-template-areas:
'header header header header header header'
'menu main main main right right'
'menu footer footer footer footer footer';
grid-gap: 10px;
background-color: #2196F3;
padding: 10px;
}
.grid-container > div {
background-color: rgba(255, 255, 255, 0.8);
text-align: center;
padding: 20px 0;
font-size: 30px;
}
.flex-container {
display: flex;
background-color: DodgerBlue;
}
.flex-container > div {
background-color: #f1f1f1;
margin: 10px;
padding: 20px;
font-size: 30px;
}
</style>
<script>
var r = Math.round(Math.random()*255);
var g = Math.round(Math.random()*255);
var b = Math.round(Math.random()*255);
function changeColor(){
for (const elem of document.querySelectorAll('div')) {
r = Math.round(Math.random()*255);
g = Math.round(Math.random()*255);
b = Math.round(Math.random()*255);
elem.style.backgroundColor = "rgb("+r+","+g+","+b+", 0.8)";
}
var myTimer = setInterval(changeColor, 1000);
var running = true;
}
</script>
</head>
<body>
<h1>Grid Layout</h1>
<p>This grid layout contains six columns and three rows:</p>
<div class="flex-container">
<button onclick="changeColor()">GO!</button>
<p id="timeinfo">TIME</p>
</div>
<div class="grid-container">
<div class="item1">Header</div>
<div class="item2">Menu</div>
<div class="item3">Main</div>
<div class="item4">Right</div>
<div class="item5">Footer</div>
</div>
</body>
</html>
I'm lost in regards to that. Any insight would be appreciated. Thanks!
You need to take the setInterval() function out of the changeColor() function. You are creating a new interval with each subsequent changeColor() recursive call, and that is what's causing the speed up until we become epileptic. Plus add the more specific selector others have mentioned ('.grid-container > div'). This is something that is probably better done in a reactive style, rather than looping through an array of elements every second, but if you are just getting into html/js/css this is a pretty good little test. I would probably do something more like this:
var r = Math.round(Math.random()*255);
var g = Math.round(Math.random()*255);
var b = Math.round(Math.random()*255);
var myTimer;
function startSwitching() {
myTimer = setInterval(changeColor, 1000);
}
function stopSwitching() {
clearInterval(myTimer);
}
function changeColor(){
for (const elem of document.querySelectorAll('.grid-container > div')) {
r = Math.round(Math.random()*255);
g = Math.round(Math.random()*255);
b = Math.round(Math.random()*255);
elem.style.backgroundColor = "rgb("+r+","+g+","+b+", 0.8)";
}
}
You need to be careful using intervals and make sure you clean up after yourself, or you could crash the browser. Also, you didn't really ask a question, so it's unclear what exactly you are wanting to change.
Add document.querySelectorAll('.grid-container > div') in for loop, so that only divs inside grid-container will change color.
document.querySelectorAll('div') actually returns a list of elements. In order to change the styling, you'll want to iterate through the elements. Here's one way you could go about doing that:
for (const elem of document.querySelectorAll('.grid-container > div')) {
r = Math.round(Math.random()*255);
g = Math.round(Math.random()*255);
b = Math.round(Math.random()*255);
elem.style.backgroundColor = "rgb("+r+","+g+","+b+")";
}

Javascript -DOM-calculator display digits on a screen

Im implementing a calculator, and I'm stuck trying to display the digit on the screen. I iterate trough all my digit to get them, but when I try to replace them in order to display them in my div with the id #nums it won't work. this is the function i'm stuck with
buttons.forEach(button => {
button.addEventListener('click', function(){
console.log('it work')
document.querySelector('#nums').textContent = buttons.innerHTML
})
})
here is a fiddle to see more
function add(a, b) {
return a + b
}
function substract(a, b) {
return a - b
}
function sum(arr) {
result = 0;
for (var i = 0; i < arr.length; i++) {
result += arr[i]
}
return result
}
/*
function multiply_range(arr){
result = 1;
for(var i = 0; i < arr.length; i++){
result *= arr[i]
}
return result
}
*/
function multiply(a, b) {
return a * b
}
function divide(a, b) {
return a / b
}
var sum = document.getElementById('sum');
var substract = document.getElementById('minus')
var multiply = document.getElementById('multiply')
var divide = document.getElementById('divide')
function operate(operator, a, b) {
if (operator === sum) {
return add(a, b);
} else if (operator === substract) {
return substract(a, b);
} else if (operator === multiply) {
return multiply(a, b);
} else if (operator === divide) {
return divide(a, b);
}
}
operate(sum, 1, 1);
var display_value = document.querySelector('#nums');
const buttons = document.querySelectorAll('.number-btn')
// loop through all the buttons
// Object.keys(buttons) transform my object in a array
/*
Object.keys(buttons).forEach(button => {
button.addEventListener('click', function(){
console.log('it work')
})
})
*/
buttons.forEach(button => {
button.addEventListener('click', function() {
console.log('it work')
document.querySelector('#nums').textContent = buttons.innerHTML
})
})
/*
var btn_1 = document.querySelector('#btn-1')
btn_1.addEventListener('click', function(){
console.log('it work')
document.querySelector('#nums').textContent = btn_1.textContent
})
*/
/*
document.querySelector('#nums').textContent = 0;
*/
/*
document.getElementsByClassName('number-btn').addEventListener('click', function(){
display_value == document.queryselector('nums');
})
*/
body {
background-color: black;
}
.container {
display: grid;
grid-template-columns: auto auto auto auto;
grid-gap: 10px;
padding: 10px;
width: 85%;
height: 300px;
margin: 0 auto;
background-color: #cc1515;
}
#btn-equals {
grid-row-start: 2;
grid-column-start: 4;
grid-row-end: 6;
grid-column-end: 4;
}
.number-btn {
border: 0.5px solid black;
background-color: white;
font-size: 30px;
}
.operator-btn {
border: 0.5px solid black;
background-color: black;
color: white;
font-size: 30px;
}
.results {
margin: 0 auto;
width: 90%;
height: 50px;
background-color: white;
}
.contour {
background-color: lightblue;
position: absolute;
top: 30%;
left: 35%;
width: 400px;
margin: auto;
vertical-align: middle;
}
#nums {
font-size: 40px;
text-align: right;
}
#operator {
font-size: 30px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="contour">
<p>The calculator</p>
<div id="results" class="results">
<div id="nums">55</div>
</div>
<div class="container">
<button id="sum" class="operator-btn">+</button>
<button id="minus" class="operator-btn">-</button>
<button id="multiply" class="operator-btn">x</button>
<button id="divide" class="operator-btn">/</button>
<button id="btn-7" class="number-btn">7</button>
<button id="btn-8" class="number-btn">8</button>
<button id="btn-9" class="number-btn">9</button>
<button id="btn-4" class="number-btn">4</button>
<button id="btn-5" class="number-btn">5</button>
<button id="btn-6" class="number-btn">6</button>
<button id="btn-1" class="number-btn">1</button>
<button id="btn-2" class="number-btn">2</button>
<button id="btn-3" class="number-btn">3</button>
<button id="btn-period" class="number-btn">.</button>
<button id="btn-O" class="number-btn">0</button>
<button id="btn-clear" class="number-btn">AC</button>
<button id="btn-equals" class="operator-btn">=</button>
</div>
</div>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
hope someone can help
use button.innerHTML not buttons.innerHTML
The array is called buttons - each item you're pulling out is being initialized as button. You want to set the div equal to that item's innerHTML, not the array buttons - which, as it is an array to begin with, does not have an innerHTML property. Furthermore, it wasn't clear in your question, but if you would like to keep adding digits to the calculator box, be sure to use the += operator instead of the =, like so document.querySelector('#nums').textContent += button.innerHTML That way it will keep adding to each box on button press.
If you would like the buttons to just replace the previous item in the calculator window, this will work:
buttons.forEach(button => {
button.addEventListener('click', function(){
document.querySelector('#nums').textContent = button.innerHTML
})
})
EDIT: As a matter of fact, since you just want the text node within your HTML, it would be better for performance to simply use button.textContent or as #Barmar pointed out, this.textContent ( this also references button )
textContent is faster because when you utilize innerHTML the Browser Engine has to reprocess and parse everything while it copies it over. textContent specifically only deals with a text node and the content therein.
buttons.forEach(button => {
button.addEventListener('click', function(){
document.querySelector('#nums').textContent = button.textContent;
})
})
It should be button and not buttons :)
document.querySelector('#nums').textContent = button.innerHTML
document.querySelector('#nums').textContent += button.innerHTML;

jQuery hover is too slow

Please run the snippet and drag you mouse over the bar to make it red.
If you drag the mouse very slowly, you will fill it red, but if you move it fast, there will be white holes in it.
How to fix it? (the white holes)
I want to make a bar divided into 500 parts and if you hover it, it becomes red and being able to drag fast and fill it without holes.
Any help appreciated :)
$(function() {
var line = $("#line");
for ( var i = 0; i < 500; i++) {
line.append('<div class="tile" id="t'+(i+1)+'"></div>');
}
var tile = $(".tile");
tile.hover (
function() { //hover-in
$(this).css("background-color","red");
},
function() { //hover-out
}
);
});
#line{
height: 50px;
background-color: #000;
width: 500px;
}
.tile {
height: 50px;
float: left;
background-color: #ddd;
width: 1px;
}
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<div id="line"></div>
With your design one way would be to iterate over the first to your current hovered element and fill it, which would lead no spaces. That said you may want to consider using the HTML5 Canvas and drawing a rectangle from 0 to your mouse position, which will perform significantly faster.
$(function() {
var line = $("#line");
for ( var i = 0; i < 500; i++) {
line.append('<div class="tile" id="t'+(i+1)+'"></div>');
}
var tile = $(".tile");
tile.hover (
function() { //hover-in
var self = this;
$("#line").children().each(function(){
$(this).css("background-color","red");
if(this == self) return false;
});
},
function() { //hover-out
}
);
});
#line{
height: 50px;
background-color: #000;
width: 500px;
}
.tile {
height: 50px;
float: left;
background-color: #ddd;
width: 1px;
}
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<div id="line"></div>
Edit
Below is an example doing the same task but using the HTML 5 Canvas:
$("#line").mousemove(function(e){
var canvas = $(this)[0];
var ctx = canvas.getContext("2d");
var rect = canvas.getBoundingClientRect()
var x = e.clientX - rect.left;
ctx.fillStyle="red";
ctx.fillRect(0, 0, x, canvas.height);
});
#line{ background-color: #ddd; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="line" width=500 height=50 ></canvas>
This is another approach with nextUntil to select siblings..
$(function() {
var line = $("#line");
for ( var i = 0; i < 500; i++) {
line.append('<div class="tile" id="t'+(i+1)+'"></div>');
}
var tile = $(".tile");
line.on( 'mouseover', function(ev){
$('.tile').first().nextUntil( $('.tile').eq(ev.pageX) ).css("background-color","red");
});
line.on( 'mouseleave', function(ev){
$('.tile').css("background-color","#ddd");
});
});
#line{
height: 50px;
background-color: #000;
width: 500px;
}
.tile {
height: 50px;
float: left;
background-color: #ddd;
width: 1px;
}
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<div id="line"></div>
Another solution makes use of jQuery's mousemove method. This allows the bar to go both forward and backwards, simply following the cursors position.
This detects movement inside of the div, then I calculate the position of the cursor within the div as a percentage and apply it as the width of the red bar.
$( ".bar" ).mousemove(function( event ) {
var xCord = event.pageX;
xPercent = (xCord + $('.pct').width()) / $( document ).width() * 100;
$('.pct').width(xPercent+'%');
});
.bar{
background:'#999999';
width:50%;
height:50px;
}
.pct{
height:100%;
background:red;
width:0%;
}
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js">
</script>
<div class="bar" style="background:#999999">
<div class="pct"></div>
</div>

Making a dragbar to resize divs inside CSS grids

I have 2 boxes and a vertical div line in one unique container div (code and fiddle below).
I'm using CSS grids to position my elements inside the container
What I'd like to accomplish is to use the vertical line to resize horizontally the two boxes based on the position of the vertical line.
I apologize if the question is noobish, I am new to web development, only used Python before, already tried to google and stackoverflow search but all solutions seem overly complicated and generally require additional libraries, I was looking for something simpler and JS only.
HTML:
<div class="wrapper">
<div class="box a">A</div>
<div class="handler"></div>
<div class="box b">B</div>
</div>
CSS:
body {
margin: 40px;
}
.wrapper {
display: grid;
grid-template-columns: 200px 8px 200px;
grid-gap: 10px;
background-color: #fff;
color: #444;
}
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
resize: both;
}
.handler{
width: 3px;
height: 100%;
padding: 0px 0;
top: 0;
background: red;
draggable: true;
}
https://jsfiddle.net/gv8Lwckh/6/
What you intend to do can be done using CSS flexbox—there is no need to use CSS grid. The bad news is that HTML + CSS is not so smart that declaring resize and draggable will make the layout flexible and adjustable by user interaction. For that, you will have to use JS. The good news is that this is actually not too complicated.
Here is a quick screen grab of output the code below:
However, for you to understand the code I will post below, you will have to familiarize yourself with:
Event binding using .addEventListener. In this case, we will use a combination of mousedown, mouseup and mousemove to determine whether the user is in the middle of dragging the element
CSS flexbox layout
Description of the solution
Initial layout using CSS
Firstly, you will want to layout your boxes using CSS flexbox. We simply declare display: flex on the parent, and then use flex: 1 1 auto (which translates to "let the element grow, let the element shrink, and have equal widths). This layout is only valid at the initial rendering of the page:
.wrapper {
/* Use flexbox */
display: flex;
}
.box {
/* Use box-sizing so that element's outerwidth will match width property */
box-sizing: border-box;
/* Allow box to grow and shrink, and ensure they are all equally sized */
flex: 1 1 auto;
}
Listen to drag interaction
You want to listen to mouse events that might have originated from your .handler element, and you want a global flag that remembers whether the user is dragging or not:
var handler = document.querySelector('.handler');
var isHandlerDragging = false;
Then you can use the following logic to check if the user is dragging or not:
document.addEventListener('mousedown', function(e) {
// If mousedown event is fired from .handler, toggle flag to true
if (e.target === handler) {
isHandlerDragging = true;
}
});
document.addEventListener('mousemove', function(e) {
// Don't do anything if dragging flag is false
if (!isHandlerDragging) {
return false;
}
// Set boxA width properly
// [...more logic here...]
});
document.addEventListener('mouseup', function(e) {
// Turn off dragging flag when user mouse is up
isHandlerDragging = false;
});
Computing the width of box A
All you are left with now is to compute the width of box A (to be inserted in the [...more logic here...] placeholder in the code above), so that it matches that of the movement of the mouse. Flexbox will ensure that box B will fill up the remaining space:
// Get offset
var containerOffsetLeft = wrapper.offsetLeft;
// Get x-coordinate of pointer relative to container
var pointerRelativeXpos = e.clientX - containerOffsetLeft;
// Resize box A
// * 8px is the left/right spacing between .handler and its inner pseudo-element
// * Set flex-grow to 0 to prevent it from growing
boxA.style.width = (pointerRelativeXpos - 8) + 'px';
boxA.style.flexGrow = 0;
Working example
var handler = document.querySelector('.handler');
var wrapper = handler.closest('.wrapper');
var boxA = wrapper.querySelector('.box');
var isHandlerDragging = false;
document.addEventListener('mousedown', function(e) {
// If mousedown event is fired from .handler, toggle flag to true
if (e.target === handler) {
isHandlerDragging = true;
}
});
document.addEventListener('mousemove', function(e) {
// Don't do anything if dragging flag is false
if (!isHandlerDragging) {
return false;
}
// Get offset
var containerOffsetLeft = wrapper.offsetLeft;
// Get x-coordinate of pointer relative to container
var pointerRelativeXpos = e.clientX - containerOffsetLeft;
// Arbitrary minimum width set on box A, otherwise its inner content will collapse to width of 0
var boxAminWidth = 60;
// Resize box A
// * 8px is the left/right spacing between .handler and its inner pseudo-element
// * Set flex-grow to 0 to prevent it from growing
boxA.style.width = (Math.max(boxAminWidth, pointerRelativeXpos - 8)) + 'px';
boxA.style.flexGrow = 0;
});
document.addEventListener('mouseup', function(e) {
// Turn off dragging flag when user mouse is up
isHandlerDragging = false;
});
body {
margin: 40px;
}
.wrapper {
background-color: #fff;
color: #444;
/* Use flexbox */
display: flex;
}
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
/* Use box-sizing so that element's outerwidth will match width property */
box-sizing: border-box;
/* Allow box to grow and shrink, and ensure they are all equally sized */
flex: 1 1 auto;
}
.handler {
width: 20px;
padding: 0;
cursor: ew-resize;
flex: 0 0 auto;
}
.handler::before {
content: '';
display: block;
width: 4px;
height: 100%;
background: red;
margin: 0 auto;
}
<div class="wrapper">
<div class="box">A</div>
<div class="handler"></div>
<div class="box">B</div>
</div>
Here's an example of the drag event handling, but using CSS Grids
The trick is to set the grid-template-columns (or rows) on the grid container rather than than the size of the grid items
let isLeftDragging = false;
let isRightDragging = false;
function ResetColumnSizes() {
// when page resizes return to default col sizes
let page = document.getElementById("pageFrame");
page.style.gridTemplateColumns = "2fr 6px 6fr 6px 2fr";
}
function SetCursor(cursor) {
let page = document.getElementById("page");
page.style.cursor = cursor;
}
function StartLeftDrag() {
// console.log("mouse down");
isLeftDragging = true;
SetCursor("ew-resize");
}
function StartRightDrag() {
// console.log("mouse down");
isRightDragging = true;
SetCursor("ew-resize");
}
function EndDrag() {
// console.log("mouse up");
isLeftDragging = false;
isRightDragging = false;
SetCursor("auto");
}
function OnDrag(event) {
if (isLeftDragging || isRightDragging) {
// console.log("Dragging");
//console.log(event);
let page = document.getElementById("page");
let leftcol = document.getElementById("leftcol");
let rightcol = document.getElementById("rightcol");
let leftColWidth = isLeftDragging ? event.clientX : leftcol.clientWidth;
let rightColWidth = isRightDragging ? page.clientWidth - event.clientX : rightcol.clientWidth;
let dragbarWidth = 6;
let cols = [
leftColWidth,
dragbarWidth,
page.clientWidth - (2 * dragbarWidth) - leftColWidth - rightColWidth,
dragbarWidth,
rightColWidth
];
let newColDefn = cols.map(c => c.toString() + "px").join(" ");
// console.log(newColDefn);
page.style.gridTemplateColumns = newColDefn;
event.preventDefault()
}
}
#page {
height: 100%;
background-color: pink;
display: grid;
grid-template-areas: 'header header header header header' 'leftcol leftdragbar tabs tabs tabs' 'leftcol leftdragbar tabpages rightdragbar rightcol' 'leftcol leftdragbar footer footer footer';
grid-template-rows: min-content 1fr 9fr 1fr;
grid-template-columns: 2fr 6px 6fr 6px 2fr;
}
/*****************************/
#header {
background-color: lightblue;
overflow: auto;
grid-area: header;
}
#leftcol {
background-color: #aaaaaa;
overflow: auto;
grid-area: leftcol;
}
#leftdragbar {
background-color: black;
grid-area: leftdragbar;
cursor: ew-resize;
}
#tabs {
background-color: #cccccc;
overflow: auto;
grid-area: tabs;
}
#tabpages {
background-color: #888888;
overflow: auto;
grid-area: tabpages;
}
#rightdragbar {
background-color: black;
grid-area: rightdragbar;
cursor: ew-resize;
}
#rightcol {
background-color: #aaaaaa;
overflow: auto;
grid-area: rightcol;
}
#footer {
background-color: lightblue;
overflow: auto;
grid-area: footer;
}
<body onresize="ResetColumnSizes()">
<div id="page" onmouseup="EndDrag()" onmousemove="OnDrag(event)">
<div id="header">
Header
</div>
<div id="leftcol">
Left Col
</div>
<div id="leftdragbar" onmousedown="StartLeftDrag()"></div>
<div id="tabs">
Tabs
</div>
<div id="tabpages">
Tab Pages
</div>
<div id="rightdragbar" onmousedown="StartRightDrag()"></div>
<div id="rightcol">
Rightcol
</div>
<div id="footer">
Footer
</div>
</div>
</body>
https://codepen.io/lukerazor/pen/GVBMZK
I changed, so you can add more Horizontal and Vertical slider.
test1.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="test1.css">
<script src= "test1.js" > </script>
</head>
<body>
<div id="page" onmouseup="EndDrag()" onmousemove="OnDrag(event)">
<div id="header">
Header asdlkj flkdfj sdflkksdjf sd;flsdjf sd;flkjsd;fljsd;flsdj;fjsd f;sdlfj;sdlfj
</div>
<div id="leftcol">
Left Col
</div>
<div id="leftdragbar" onmousedown="StartHDrag(1)"></div>
<div id="tabs">
Tabs
</div>
<div id="topdragbar" onmousedown="StartVDrag(2)"></div>
<div id="tabpages">
Tab Pages
</div>
<div id="rightdragbar" onmousedown="StartHDrag(3)"></div>
<div id="rightcol">
Rightcol
</div>
<div id="botdragbar" onmousedown="StartVDrag(4)"></div>
<div id="footer">
Footer
</div>
</div>
<div id= 'status'></div>
</body>
</html>
test1.css
body {
}
#page {
height: 100vh;
background-color: pink;
display: grid;
grid-template-areas:
'header header header header header'
'leftcol leftdragbar tabs tabs tabs'
'leftcol leftdragbar topdragbar topdragbar topdragbar'
'leftcol leftdragbar tabpages rightdragbar rightcol'
'botdragbar botdragbar botdragbar botdragbar botdragbar'
'footer footer footer footer footer';
grid-template-rows: min-content 1fr 6px 9fr 6px 1fr;
grid-template-columns: 2fr 6px 6fr 6px 2fr;
}
/*****************************/
#header {
background-color: lightblue;
overflow: auto;
grid-area: header;
}
#leftcol {
background-color: #aaaaaa;
overflow: auto;
grid-area: leftcol;
}
#leftdragbar {
background-color: black;
grid-area: leftdragbar;
cursor: ew-resize;
}
#topdragbar {
background-color: black;
grid-area: topdragbar;
cursor: ns-resize;
}
#botdragbar {
background-color: black;
grid-area: botdragbar;
cursor: ns-resize;
}
#tabs {
background-color: #cccccc;
overflow: auto;
grid-area: tabs;
}
#tabpages {
background-color: #888888;
overflow: auto;
grid-area: tabpages;
}
#rightdragbar {
background-color: black;
grid-area: rightdragbar;
cursor: ew-resize;
}
#rightcol {
background-color: #aaaaaa;
overflow: auto;
grid-area: rightcol;
}
#footer {
background-color: lightblue;
overflow: auto;
grid-area: footer;
}
test1.js
let isHDragging = false;
let isVDragging = false;
let cols = ['2fr','6px','6fr','6px','2fr']; //grid-template-columns: 2fr 6px 6fr 6px 2fr;
let colns = ['leftcol','','tabpages','','rightcol'];
let Tcols = [];
let rows = ['min-content','1fr','6px','9fr','6px','1fr']; //grid-template-rows: min-content 1fr 6px 9fr 1fr
let rowns = ['header','tabs','','tabpages','','footer'];
let Trows = []
let CLfactor ;
let CRfactor ;
let gWcol = -1;
let gWrow = -1;
function StartHDrag(pWcol) {
isHDragging = true;
SetCursor("ew-resize");
CLfactor = parseFloat(cols[pWcol-1]) / document.getElementById(colns[pWcol-1]).clientWidth;
CRfactor = parseFloat(cols[pWcol+1]) / document.getElementById(colns[pWcol+1]).clientWidth;
Tcols = cols.map(parseFloat);
gWcol = pWcol;
}
function StartVDrag(pRow) {
isVDragging = true;
SetCursor("ns-resize");
CLfactor = parseFloat(rows[pRow-1]) / document.getElementById(rowns[pRow-1]).clientHeight;
CRfactor = parseFloat(rows[pRow+1]) / document.getElementById(rowns[pRow+1]).clientHeight;
Trows = rows.map(parseFloat);
gWrow = pRow;
}
function SetCursor(cursor) {
let page = document.getElementById("page");
page.style.cursor = cursor;
}
function EndDrag() {
isHDragging = false;
isVDragging = false;
SetCursor("auto");
}
function OnDrag(event) {
if(isHDragging) {
Tcols[gWcol-1] += (CLfactor * event.movementX);
Tcols[gWcol+1] -= (CLfactor * event.movementX);
cols[gWcol-1] = Math.max(Tcols[gWcol-1],0.01) + "fr";
cols[gWcol+1] = Math.max(Tcols[gWcol+1],0.01) + "fr";
let newColDefn = cols.join(" ");
page.style.gridTemplateColumns = newColDefn;
} else if (isVDragging) {
Trows[gWrow-1] += (CLfactor * event.movementY);
Trows[gWrow+1] -= (CLfactor * event.movementY);
rows[gWrow-1] = Math.max(Trows[gWrow-1],0.01) + "fr";
rows[gWrow+1] = Math.max(Trows[gWrow+1],0.01) + "fr";
let newRowDefn = rows.join(" ");
page.style.gridTemplateRows = newRowDefn;
document.getElementById("footer").innerHTML = newRowDefn;
}
event.preventDefault()
}
To actually match the question! Making a dragbar to resize divs inside CSS grids.
Here is a possible way, the original OP layout is kept, as well as the CSS, using Grids.
The goal is to capture the original state of the Grid Template Columns, and convert it to floats.
The browser always compute in pixels, and the sum of those columns + the gap, represent the total width of the container element. That sum must always be the same, or the elements will jump!
NB: Calls to .getComputedStyle() are not very efficient, optimisation is likely possible here!
Notice, doing this way using grids and screenX avoid the common jumping bug on mouse down.
Comments are added, this will allow to apply the logic with any number of columns, or rows, good luck.
With the usage of pointer events, it does works from a touch device as well.
let target = document.querySelector("div") // Target container element
let md = false; // Will be true at mouse down
let xorigin; // Click origin X position
let gtcorigin = []; // Origin Grid Template Columns in pixels
const pointerdown = (e) => {
if (e.target.classList[0] === "handler"){ // Filter to target the wanted element
md = true; // Set mouse down
xorigin = e.screenX; // Store the origin X position
// Grid Template Columns, array of pixels as float
gtcorigin = window.getComputedStyle(target)["grid-template-columns"].split(" ").map((a) => +(a.slice(0, -2)));
document.body.style.cursor = "col-resize" // This makes things nice
document.body.style.userSelect = "none" // This makes things nice
}
}
const pointerup = (e) => {
md = false; // Reset bool at mouse up
document.body.style.cursor = "pointer"
document.body.style.userSelect = "unset"
}
const resizer = (e) => {
if (md){ // Mouse is down hover the handler element
let gtc = window.getComputedStyle(target)["grid-template-columns"].split(" ").map((a) => +(a.slice(0, -2))); // Grid Template Columns, array of pixels as float
let xdragdif = xorigin - e.screenX; // Move in pixels since the click
gtc[0] = gtcorigin[0] - xdragdif // First column, if negative, it will grow
gtc[2] = gtcorigin[2] + xdragdif // Third column
gtc = gtc.map((a) => a+"px") // Set back the values in string with "px"
document.querySelector("console").textContent = gtc.join(" ") // !!! This is only for the demo
target.style.gridTemplateColumns = gtc.join(" ") // Apply the new Grid Template Column as inline style.
}
}
// Attach all events on the largest container element. Here the body is used.
document.body.addEventListener("pointerdown", pointerdown, false)
document.body.addEventListener("pointerup", pointerup, false)
document.body.addEventListener("pointermove", resizer, false)
body {
margin: 40px;
overflow-x: hidden
}
.wrapper {
display: grid;
grid-template-columns: 200px 8px 200px;
grid-gap: 10px;
background-color: #fff;
color: #444;
}
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
}
.handler{
width: 3px;
height: 100%;
padding: 0px 0;
top: 0;
background: red;
cursor: col-resize
}
<div class="wrapper">
<div class="box">A</div>
<div class="handler"></div>
<div class="box">B</div>
</div>
<console></console>
No limits are applied here, this can be enhanced with CSS only, using min-width and other similar rules, and the float values can be retrieved to create range sliders and more, this way.

Categories