How do i center two divs vertically in Javascript? - javascript

Here is the code snippet:
var wrapper = document.createElement('DIV');
wrapper.setAttribute("width", x * rows);
wrapper.setAttribute("height", y * columns);
wrapper.align = "center";
var buttonWrap = document.createElement('DIV');
buttonWrap.setAttribute("style", "clear:float");
As you can see in my code snippet, I have tried to center my div. But this code doesn't work. What works is making both divs fixed. But at the end of the day, the second div will then be upon the first div.
Please help.

If you can use only CSS I would do it this way:
.container {
width: 500px;
height: 150px;
border: solid black 1px;
/* Align center */
display: flex;
justify-content: center;
align-items: center;
}
.small {
background-color: red;
width: 100px;
height: 30px;
}
.big {
background-color: green;
width: 100px;
height: 100px;
}
<div class="container">
<div class="small"></div>
<div class="big"></div>
</div>
If you want to do it in javascript, apply the style written above in CSS this way:
var container = document.getElementsByClassName('container')[0];
container.style.display = "flex";
and so on...

Related

overflow-anchor doesn't work for horizontal scrolling

I would like to build an infinite horizontal scroll that scrolls in both directions - left and right. As user scrolls to the left, new content is prepended to the scrollable element (think scrolling through a schedule history, for example). As they scroll to the right, content is appended.
I have learned that browsers anchor content when scrolling up and down which is fantastic, exactly what I'd expect. The effect of that is that prepending content to the scrolled element anchors user to their current, logical position and the content doesn't "jump".
But the anchoring doesn't seem to work when scrolling left or right. The behaviour is as if I set overflow-anchor: none. What can I do to make it work as well as when scrolling up?
let topCounter = 0;
document.querySelector('.scrollable-top').scrollTo({ top: 100 });
document.querySelector('.scrollable-top').onscroll = (event) => {
if (event.target.scrollTop < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${topCounter--}`;
document.querySelector('.scrollable-top').prepend(box);
}
};
let leftCounter = 0;
document.querySelector('.scrollable-left').scrollTo({ left: 100 });
document.querySelector('.scrollable-left').onscroll = (event) => {
if (event.target.scrollLeft < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${leftCounter--}`;
document.querySelector('.scrollable-left').prepend(box);
}
};
.scrollable-top {
width: 200px;
height: 250px;
overflow-y: auto;
border: solid 1px black;
}
.scrollable-left {
display: flex;
width: 250px;
overflow-x: auto;
border: solid 1px black;
}
.content-box {
flex: 1 0 auto;
height: 150px;
width: 150px;
border: solid 1px red;
display: flex;
justify-content: center;
align-items: center;
}
<div class="scrollable-top">
<div class="content-box">1</div>
<div class="content-box">2</div>
<div class="content-box">3</div>
</div>
<div class="scrollable-left">
<div class="content-box">1</div>
<div class="content-box">2</div>
<div class="content-box">3</div>
</div>
Scroll the horizontal container to the right by 150 using scrollBy(150, 0):
let topCounter = 0;
document.querySelector('.scrollable-top').scrollTo({ top: 100 });
document.querySelector('.scrollable-top').onscroll = (event) => {
if (event.target.scrollTop < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${topCounter--}`;
document.querySelector('.scrollable-top').prepend(box);
}
};
let leftCounter = 0;
document.querySelector('.scrollable-left').scrollTo({ left: 100 });
document.querySelector('.scrollable-left').onscroll = (event) => {
if (event.target.scrollLeft < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${leftCounter--}`;
document.querySelector('.scrollable-left').prepend(box);
// solution ------------------
event.target.scrollBy(150, 0);
}
};
.scrollable-top {
width: 200px;
height: 250px;
overflow-y: auto;
border: solid 1px black;
display: inline-block;
float:left;
}
.scrollable-left {
display: flex;
width: 250px;
overflow-x: auto;
border: solid 1px black;
float:right;
}
.content-box {
flex: 1 0 auto;
height: 150px;
width: 150px;
border: solid 1px red;
display: flex;
justify-content: center;
align-items: center;
}
<div class="scrollable-top">
<div class="content-box">1</div>
<div class="content-box">2</div>
<div class="content-box">3</div>
</div>
<div class="scrollable-left">
<div class="content-box">1</div>
<div class="content-box">2</div>
<div class="content-box">3</div>
</div>
As per the specs, following is the intent behind such anchoring:
Changes in DOM elements above the visible region of a scrolling box can result in the page moving while the user is in the middle of consuming the content.
This spec proposes a mechanism to mitigate this jarring user experience
by keeping track of the position of an anchor node and adjusting the scroll offset accordingly.
This spec also proposes an API for web developers to opt-out of this behavior.
Since no page loads horizontally, I think they didn't implement this for horizontal scrollbars. Also, apart from above use case it makes no sense to implement this behavior.
Note: Safari doesn't implement the overflow-anchor behavior. So, your code for vertical scroll fails in Safari.
I've tried my code, for horizontal scrolling, on Safari and it works. So incase you want to implement infinite vertical scroll, and want to support all the browsers, then you'll have to optout of overflow-anchor behavior and use scrollBy(x,y) to do it manually. :(
I tried to fix your code and got this option
let leftCounter = -2;
let rightCounter = 2;
document.querySelector('.scrollable-left').scrollTo({ left: 100 });
document.querySelector('.scrollable-left').onscroll = (event) => {
if (event.target.scrollLeft < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${leftCounter--}`;
document.querySelector('.scrollable-left').prepend(box);
event.target.scrollLeft += 250
}
if ((event.target.scrollWidth - event.target.scrollLeft - 250) < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${rightCounter++}`;
document.querySelector('.scrollable-left').append(box);
}
};
.scrollable-top {
width: 200px;
height: 250px;
overflow-y: auto;
border: solid 1px black;
}
.scrollable-left {
display: flex;
width: 250px;
overflow-x: auto;
border: solid 1px black;
}
.content-box {
flex: 1 0 auto;
height: 150px;
width: 150px;
border: solid 1px red;
display: flex;
justify-content: center;
align-items: center;
}
<div class="scrollable-left">
<div class="content-box">-1</div>
<div class="content-box">0</div>
<div class="content-box">1</div>
</div>

Make both overlapping divs clickable?

Is it possible to make two overlapping divs, both clickable?
I've appended divs to two containers, #container and #container2. Their styles are exactly the same only except one is flex-direction: column; and one is flex-direction: column;. Both position:absolute with #container2 on top. I made each of the appended child clickable to fill its background color. Only the div on top is clickable so far, is there a way to make both clickable? or is there another way to have the bottom div react to my clicks?
window.addEventListener('load', init);
function init() {
calculateGrid();
//calculate grid
function calculateGrid() {
var w = window.innerWidth;
var h = window.innerHeight;
var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);
function randomInRange(from, to) {
let x = Math.random() * (to - from);
return x + from;
};
for (var i = 0; i < totalNum; i++) {
var div = document.createElement('div');
div.setAttribute('class', 'grid');
div.style.width = randomInRange(3, 10) + 'vw';
div.style.height = randomInRange(5, 10) + 'vh';
document.getElementById('container').appendChild(div);
document.getElementById('container2').appendChild(div.cloneNode(true));
}
};
$(".grid").click(function() {
$(this).toggleClass('selected');
});
};
#container {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: column;
overflow: hidden;
}
#container .grid {
border: 1px solid blue;
}
#container2 {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: row;
overflow: hidden;
}
#container2 .grid {
border: 1px solid red;
}
.grid {
font-size: 10px;
color: white;
}
#container .selected {
background-color: blue;
}
#container2 .selected {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="wrapper">
<div id="container"></div>
<div id="container2"></div>
</div>
View on CodePen
One method is to use Document.elementsFromPoint() to return "an array of all elements at the specified coordinates". Iterate through that array, adding the "selected" class to "grid" elements.
window.addEventListener('load', init);
function init() {
// build grid
function calculateGrid() {
var w = window.innerWidth;
var h = window.innerHeight;
var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);
function randomInRange(from, to) {
let x = Math.random() * (to - from);
return x + from;
};
for (var i = 0; i < totalNum; i++) {
var div = document.createElement('div');
div.setAttribute('class', 'grid');
div.style.width = randomInRange(3, 10) + 'vw';
div.style.height = randomInRange(5, 10) + 'vh';
document.getElementById('container1').appendChild(div);
document.getElementById('container2').appendChild(div.cloneNode(true));
}
};
// handle grid clicks
function handleGridClick(e) {
let elms = document.elementsFromPoint(e.clientX, e.clientY);
Array.from(elms).forEach(elm => {
if (elm.classList.contains('grid'))
elm.classList.add('selected');
});
}
// initialize grid and click handler
calculateGrid();
document.addEventListener('click', handleGridClick);
};
.container {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
overflow: hidden;
}
#container1 {
flex-direction: column;
}
#container1 .grid {
border: 1px solid blue;
}
#container1 .grid.selected {
background-color: blue;
}
#container2 .grid {
border: 1px solid red;
}
#container2 .grid.selected {
background-color: red;
}
<div id="wrapper">
<div id="container1" class="container"></div>
<div id="container2" class="container"></div>
</div>
You can't actually hover two items at the same time in plain 'ol HTML/CSS - for that you will need JavaScript as explained in the accepted solution. However, there's a CSS-only solution to allow hovering over the different layers, which was fun to figure out at the very least.
So the idea is that you have these invisible boxes on top of the visible ones. The invisible boxes only have borders such that any time your mouse hits a border, some clever z-index swapping takes place to make the visible containers change their stacking order.
For every .grid item you need to create a corresponding .box item: https://jsfiddle.net/ryanwheale/01v5yz86/93/

How to extend single div to the next row

We have a container (red), and a child div (blue). On increasing the child div's width, I want the child div to come to the next row instead of continuing on the same row.This is what is happening now :
This is what is needed :
.container{
width:100px;
height:50px;
background:red;
position:absolute;
}
.child{
width:120px;
height:20px;
background:blue;
margin:5px 5px 0px 5px;
position: relative;
}
<div class="container">
<div class="child"></div>
</div>
Any solution to the above stated issue will be very helpful :)
Thanks in advance!
You will not be able to do that with a single rectangular div but you can do something like this.
Hope this helps.
This is another solution not sure if it fits in your case.
.parent {
width: 500px;
display: grid;
grid-template-columns: repeat(auto-fill, 186px);
background-color: red;
}
.parent>* {
background-color: blue;
height: 50px;
border: 1px solid white;
color: white;
}
<div class="parent">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
</div>
I have no idea to the bigger picture of your problem. However here is a hackish way to do it using HTML, CSS and Javascript.
What the javscript does is:
Get the width of the container and the child
Check if the child has a higher width than the container
If the above is true, get the offset (childWidth - containerWidth)
Reduce the child's width and make it equal to the container
Create a div and give it a width equal to the offset and give it a height equal to the child.
Append the div to the container
var container = document.querySelector('.container');
var containerWidth = container.offsetWidth;
var child = document.querySelector('.child');
var childWidth = child.offsetWidth;
(function() {
extendDivToNextRow();
})();
function extendDivToNextRow() {
let offset;
let secondRow;
if (childWidth > containerWidth) {
sliceChildDiv();
extendToSecondRow();
}
}
function getChildOffset() {
return childWidth - containerWidth;
}
function sliceChildDiv() {
const newWidth = childWidth - getChildOffset();
child.setAttribute("style", `width:${newWidth}px`);
}
function extendToSecondRow() {
const secondRow = createSecondRow();
container.appendChild(secondRow);
}
function createSecondRow() {
const row = document.createElement('div');
row.setAttribute("style", `width: ${getChildOffset()}px; height: 20px; background: blue`);
return row;
}
* {
margin: 0px;
}
.container {
width: 100px;
height: 50px;
background: red;
position: absolute;
}
.child {
width: 120px;
height: 20px;
background: blue;
position: relative;
}
<div class="container">
<div class="child"></div>
</div>

Can't figure out where this #text is coming from when using node.firstChild()

I am trying to get the node.firstChild of the #root element, after generating content within the container. I expect it to be the first div, because when I look at the elements in the dev console, that's the first child that I see. I am not sure where this #text is coming from, or what it means even.
Please help me understand:
What #text is (obviously it's some type of text, but I don't see it)
Why it's showing up instead of the firstChild of my container which should actually be div.each-result
It should be noted that I am running this code in CodePen
I am also aware I can also use Node.firstElementChild, but I want to understand what's going wrong currently.
const leftArrow = document.querySelector('#left-arrow');
const rightArrow = document.querySelector('#right-arrow');
const rootDiv = document.querySelector('#root');
const generateButton = document.querySelector("#button-generate");
//This code basically generates the content within the div
generateButton.addEventListener("click", () => {
for (let i = 0; i < 10; i++) {
const newDiv = document.createElement("div");
newDiv.classList.add("each-result");
newDiv.appendChild(addImg("https://uk.usembassy.gov/wp-content/uploads/sites/16/please_read_icon_150x150.jpg"));
rootDiv.appendChild(newDiv);
}
console.log(rootDiv.firstChild);
});
//These enable the arrow to scroll through the dynamically generated content
leftArrow.addEventListener('click', () => {
//use
});
rightArrow.addEventListener('click', () => {
alert("right arrow works");
});
//Simple function to create and image element with the src attribute set in one line
function addImg(url) {
const newImg = document.createElement("img");
newImg.setAttribute("src", url);
return newImg;
}
html, body {
height: 100%;
}
button {
position: relative;
z-index: 1
width: auto;
height: 50px;
}
.container {
display: flex;
justify-content: center;
position: relative;
top: 15%;
z-index: 0
}
.result-container {
display: flex;
align-items: center;
border: 1px solid black;
width: 80%;
height: 200px;
position: relative;
flex-flow: row no-wrap;
overflow: hidden;
}
.each-result {
height: 150px;
width: 150px;
border: 3px dotted red;
margin: 1%;
}
img {
height: 100%;
width: auto;
}
.nav-arrows {
display: flex;
justify-content: space-between;
width: 100%;
height: auto;
position: absolute;
background: clear;
pointer-events: none;
}
#left-arrow, #right-arrow {
pointer-events: auto;
}
<script src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
<div class="container">
<div class="nav-arrows">
<button id="left-arrow"><i class="fas fa-arrow-alt-circle-left"></i>
</button>
<button id="right-arrow"> <i class="fas fa-arrow-alt-circle-right"></i>
</button>
</div>
<div id="root" class="result-container">
</div>
</div>
<button id="button-generate">Generate Content</button>
Look at the first example here: Node.firstChild
In the above, the console will show '#text' because a text node is
inserted to maintain the whitespace between the end of the opening <p>
and <span> tags. Any whitespace will create a #text node, from a
single space to multiple spaces, returns, tabs, and so on.
Another #text node is inserted between the closing </span> and
</p> tags.
If this whitespace is removed from the source, the #text nodes are not
inserted and the span element becomes the paragraph's first child.
As you suggested yourself, ParentNode.firstElementChild is the best way to go in this case.

How create a grid in which a variable number of squares automatically resize to fit a space?

I'm working on this problem: http://www.theodinproject.com/web-development-101/javascript-and-jquery?ref=lnav.
Basically, I need to create a game in which the user clicks a button, is asked how many squares they want in their grid, and the grid appears and they can draw on the grid by mousing over the squares which changes their color. So far, I figured out everything Except how to automatically resize the squares to take up the full space of the div container they are in (I restricted the shape of the 16x16 grid to a square by placing it in a div with the id container that had a set height and width).
I tried to play around with the css, changing the height and width to 100%. When I do that though, they just appear in a column and not a grid.
If you open this up in jsfiddle, and request a grid of 16 squares per side, you will see what I WANT to have happen. 16 squares per side is the max, and I would like any lower number of squares to fill up that space and stay in a square grid format, not form a column. Im unsure of whether the solution involves CSS or jQuery. Thank you for any help!
Here is the html:
<body>
<div id ="button">
<p>Play the game</p>
</div>
<div id ="container"></div>
</div>
</body>
Here is the CSS:
.squares {
background-color: #c266ff;
display: block;
height: 100%;
width: 100%;
display: inline-block;
margin-left: 3px;
}
#container {
height: 200px;
width: 470px;
margin: auto;
margin-top: 50px;
}
#button {
height: 50px;
width: 100px;
background-color: #ddcee6;
border: 2px solid #ddcee6;
border-radius: 10px;
text-align: center;
color: #19171a;
margin: auto;
margin-top: 50px;
}
Here is the jquery/javascript:
$(document).ready(function() {
$("#button").click(function() {
var x = prompt("How many squares do you want on each side of the grid? Pick between 1 and 16.");
for(var i = 0; i < (x*x); i++) {
$("<div class='squares'></div>").appendTo("#container");
}
$(".squares").one("mouseover", function() {
$(this).css("background-color","#6b00b3");
});
});
});
Here is a way that you can calculate the height and width, and apply it, using jQuery.
//Calculate squares height and width
var containerHeight = $('#container').innerHeight();
var containerWidth = $('#container').innerWidth();
var squareMargins = ($('.squares').outerWidth(true) - $('.squares').outerWidth()) * x; //Margin * x to take margin space into account, otherwise calculation will be off
var squareHeight = (containerHeight - squareMargins) / x;
var squareWidth = (containerWidth - squareMargins) / x;
$('.squares').height(squareHeight);
$('.squares').width(squareWidth);
Demo Here
Here is a start.
$(document).ready(function() {
$("#button").click(function() {
var x = prompt("How many squares do you want on each side of the grid? Pick between 1 and 16.");
var s = (100 / x);
for(var i = 0; i < (x*x); i++) {
$("<div class='squares' style='width:" + s + "%; height:" + s + "%'></div>").appendTo("#container");
}
$(".squares").one("mouseover", function() {
$(this).css("background-color","#6b00b3");
});
console.log(document.body.innerHTML);
});
});
.squares {
background-color: #c266ff;
float: left;
border: 1px solid black;
box-sizing: border-box;
}
#container {
height: 200px;
width: 470px;
margin: auto;
margin-top: 50px;
}
#button {
height: 50px;
width: 100px;
background-color: #ddcee6;
border: 2px solid #ddcee6;
border-radius: 10px;
text-align: center;
color: #19171a;
margin: auto;
margin-top: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id ="button">
<p>Play the game</p>
</div>
<div id ="container">
</div>

Categories