Creating a Pyramid using CSS and JS - javascript

I have a wrapper div and many content blocks. The content block can be of any number.
<div class="wrapper">
<div class="content-block">Something goes here</div>
<div class="content-block">Something goes here</div>
.
.
.
<div class="content-block">Something goes here</div>
</div>
I wish to form a pyramid structure using these content-blocks as it appears below:
Is it possible to achieve pyramid like this? The above image is just an example, there can be more than 10 content-blocks or even less.

Check out this very simple JavaScript/CSS solution:
var objContainer = document.getElementById("container"),
intLevels = 10,
strBlocksHTML = '';
// Using innerHTML is faster than DOM appendChild
for (var i = 0; i < intLevels; i++) {
for (var n = 0; n < i + 1; n++) {
strBlocksHTML += '<div class="buildingBlock"></div>';
}
strBlocksHTML += '<div></div>'; // Line break after each row
}
objContainer.innerHTML = strBlocksHTML;
.buildingBlock {
display: inline-block;
width: 20px;
height: 20px;
margin: 2px 5px;
background-color: #eee;
border: 2px solid #ccc;
}
#container {
text-align: center;
}
<div id="container"></div>

Yes, it is perfectly possible, but hard to write down without more precise requirements. Number of divs would obviously equal number of elements = 10. Length of bottom row = (10/2 - 1) with each next row to top taking one less element, etc. Either use absolute positioning in div style or treat table as matrix and draw with cells. Table solution will be progressively slower with more rows, because all the empty "pixels" and quadratically increasing overhead on recalculating cell sizes and positions in browser.

Hm, not a trivial task. I don't think it is possible to write (finite) CSS for any number of elements. It would need something like this:
#wrapper {
text-align: center;
}
.content-block {
display: inline-block;
width: 5em;
height: 4em;
margin: 0 2.5em;
}
.content-block:nth-child(n*(n+1)/2)::after {
display: block; /* linebreak */
}
Where the nth-child-selector would contain a triangular number, but it must have the form an+b.

Related

How to iterate through DOM elements changing id with Javascript

I have 36 divs with ids id="square-1", "square-2", etc., framed in a grid 6x6 container. I want them to be all square and be the size of each square of the grid.
I want to make a function to add css to each of them to determine their position in the grid. In my mind I can do something to iterate through them with a for loop and apply "gridColumn = "a/b"", where a and b are relative to the i in the for loop, so that I don't have to specify that 36 times in the css document.
Is this even possible? Does it make sense? Very beginner...
<div class="div-container">
<div id= "square-1"></div>
<div id= "square-2"></div>
<div id= "square-3"></div>
<div id= "square-4"></div>
<div id= "square-5"></div>
<div id= "square-6"></div>
<div id= "square-7"></div>
etc...
</div>
Keeping track of elements by a CSS property is fragile. If you are using JavaScript, then let it do the heavy lifting. For instance, instead of hard coding 36 <div> with ids, make a <div> on each iteration of a loop. In the example below, the container is a <main> and each sub-box is a <section>. Each <section> is given a CSS property of order and a corresponding number and an id: "sq"+ a corresponding number. The order property applies to the order in which a flex item appears within a flex container.
const box = document.querySelector('main');
for (let i = 0; i < 36; i++) {
const sec = document.createElement('section');
sec.style.order = i;
sec.id = `sq${i}`;
box.append(sec);
}
html {
font: 300 5vmin/1 Consolas
}
main {
display: flex;
flex-flow: row wrap;
width: 12rem;
height: 12rem;
border: 1px solid red
}
section {
width: 2rem;
height: 2rem;
outline: 1px dashed blue
}
<main></main>
You could grab the parent element and loop through the child elements:
var element = document.getElementById('parentDiv');
var children = element.children;
for(var i=0; i<children.length; i++){
var child = children[i];
//apply changes
}
So if you just want to iterate thru DOM elements you can do it by iterating thru every child that's div inside .div-container:
const squares = document.querySelectorAll('.div-container div')
for(let square of squares){
square.classList.add('YOUR_CLASS')
}
It seems like you can use a css grid here.
To use this, you simply need to add css to the parent item (here your div-container) and populate with child elements which will be arranged in grid.
If you're beginner in HTML and css, you can generate grid using websites like cssgrid-generator.
Small example
Small example here using a 2*2 grid
.parent {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px;
width: 210px
}
.child {
background: red;
width: 100px;
height: 100px;
}
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
Note : You can also generate the child elements using a for loop in javascript which will make your code lighter
If you use CSS Grid use a loop to create the markup (I've used template strings pushed into an array) and add that (joined array) as the innerHTML of a container. You can add the array index as the data id attribute, along with a row/column attribute if you want to pinpoint the box's position in the grid.
In this example I've added a function that changes the box background color (an ,active class) when it's clicked on.
function makeGrid(n) {
const boxes = [];
let row = 0;
let column = 1;
for (let i = 1; i < n * n; i++) {
column++;
if (i % 6 === 1) {
row++;
column = 1;
}
const div = `
<div
class="box"
data-id="${i}"
data-row="${row}"
data-column="${column}"
>${i}
</div>`;
boxes.push(div);
}
return boxes.join('');
}
const main = document.querySelector('main');
main.innerHTML = makeGrid(6);
main.addEventListener('click', handleClick);
function handleClick(e) {
if (e.target.matches('.box')) {
e.target.classList.add('active');
}
}
main { display: grid; grid-template-columns: repeat(6, 30px); gap: 0.5em; }
.box { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 30px; height: 30px; border: 1px solid #676767; }
.box:not(.active):hover { background-color: lightblue; cursor: pointer; }
.active { background-color: salmon };
<main></main>
Additional documentation
Event delegation
Template/string literals

How can I make divs dynamically fill up fixed-size container and shrink and/or expand depending on number of divs in the container?

Introduction
I am currently learning web development and I am trying to make some sort of etch-a-sketch thing and it should pop out a grid with up to 100 square cells per side BUT no matter what number of cells per side the grid itself should be using the same amount of space, so for example if the total space it could use was 1000px then the grid, no matter how many cells it has, will use 1000px, no more or no less. My problem is that I'm not sure what I should do to make the grid do this.
Problem context For starters, for my purposes I can only generate the grid itself via Javascript. In the Javascript I use a doubly-nested for loop where after each iteration in the first loop, a new "grid-row" div will be created and then the second for loop will populate that grid row with "square" divs, so if I set the number as 5 per-side, it will create a grid-row and populate that row with 5 square divs 5 times.
My problem The whole grid itself is wrapped in a "grid-container" div with a fixed size. I would like to make it so the grid will always fill up the size of the grid container but not exceed the container itself. In other words, I would like to find a way to make the square and/or grid-row divs be able to shrink or grow depending on the number of them inside the grid-container so as to not have the overall grid exceed the size of the grid-container itself but still fill up the available space in the container.
So far I have played around with various CSS properties suggestions that I found from Google searches such as setting height and width of either the "square" or "grid-row" divs (or both of them at the same time) to 100%, playing around with the max widths and heights, as well as their mins, and playing around with the "overflow" and object-fit properties. I'm not sure if there was a solution for my problem in Javascript and if there was I haven't found one in my myriad Google searches.
Here's my code snippet, I'd appreciate any help:
const container = document.querySelector(".grid-container");
for (let x = 0; x < 6; x++) {
/* Both the "x" and "y" variables are set to arbitrary values (up to 100). The "x" variable here represents how many total "rows" the grid will create. The following "y" variable represents how many square divs will populate each row, so both of these numbers should be the same to make a square grid.*/
let newRow = document.createElement('div');
newRow.classList = `grid-row`;
container.appendChild(newRow);
for (let y = 0; y < 6; y++) {
let square = document.createElement('div');
square.classList = `square`;
newRow.appendChild(square);
}
}
const gridCells = document.querySelectorAll(".square");
gridCells.forEach(cell => {
cell.addEventListener("mouseover", () => {
cell.classList.add("hov-square");
});
});
/*This is the current state of the CSS file that I left on prior to posting this question. I set the width and height of the "square" divs to 10px each to make the grid visible and to give an idea of what the grid may look like, but my goal is for the grid (the grid-rows and the square divs inside of it) to be able to fill up the "grid-container" div that wraps the grid itself and resize itself depending on how many square cells are in the grid but never exceed the size of the grid-container itself.*/
body {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
padding: 0;
background-color: black;
}
.grid-container {
margin-top: 10%;
display: flex;
width: 750px;
height: 750px;
justify-content: center;
align-items: center;
overflow: visible;
}
.grid-row {
max-width: 100%;
max-height: 100%;
}
.square {
width: 10px;
height: 10px;
border: 1px dashed white;
}
.hov-square {
background-color: grey;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="script.js" defer></script>
<link rel="stylesheet" href="styles.css">
<title>Etch-a-Sketch</title>
</head>
<body>
<div class="grid-container"></div>
</body>
</html>
I grabbed the width of the grid-container, then divided that by the number of columns to get the width/height of each cell.
Then I used javascript to set the width and height to that size.
I got rid of the grid-row CSS. Also, you don't need to loop through the cells to add hover class. Just use .square:hover in CSS and it will automatically apply it to the matching cells.
const container = document.querySelector(".grid-container");
let squares = 100;
let _parent_width = getComputedStyle(container).width;
let sq_size = Math.floor(_parent_width.replace("px","") / squares) - 2;
for (let x = 0; x < squares; x++) {
let newRow = document.createElement('div');
newRow.classList = `grid-row`;
container.appendChild(newRow);
for (let y = 0; y < squares; y++) {
let square = document.createElement('div');
square.classList.add("square");
square.style.width = sq_size + "px";
square.style.height = sq_size + "px";
newRow.appendChild(square);
}
}
const gridCells = document.querySelectorAll(".square");
gridCells.forEach(cell => {
cell.addEventListener("mouseover", () => {
cell.classList.add("hov-square");
});
});
/*This is the current state of the CSS file that I left on prior to posting this question. I set the width and height of the "square" divs to 10px each to make the grid visible and to give an idea of what the grid may look like, but my goal is for the grid (the grid-rows and the square divs inside of it) to be able to fill up the "grid-container" div that wraps the grid itself and resize itself depending on how many square cells are in the grid but never exceed the size of the grid-container itself.*/
body {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
padding: 0;
background-color: black;
}
.grid-container {
display: flex;
width: 1000px;
height: 1000px;
justify-content: center;
align-items: center;
overflow: visible;
}
.square {
border: 1px dashed white;
}
.hov-square{
background-color: grey;
}
<div class="grid-container">c</div>

Unwanted space after first element in a row

I am creating a list of profiles which will be displayed based on a given category. The setup makes it inconvenient to use a container element to wrap the list items, so I'm using display:inline-flex on each item instead of a flex container with the usual justify-this and align-that.
The issue is that the first element in the row appears to have a space to the right of it, and I'm not sure why.
I'd like to display all the elements evenly, in this case 4 to a row with identical spacing, without nesting them in a parent container if possible.
// simple function to repeat html elements
$(document).ready(function() {
let a = $('.a')[0];
const repeats = 11;
let count = 0;
while (count < repeats) {
$('body').append($(a).clone())
count++;
}
//$( 'body' ).append( html );
});
.a {
background-color: red;
box-sizing: border-box;
border: 1px solid green;
display: inline-flex;
height: 25px;
width: 25%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<div class="a"></div>
</body>
</html>
Actually now the first element (with space to the right) is one you declared in your html. Remove it from there and use instead:
// simple function to repeat html elements
$(document).ready(function() {
let a = $('<div class="a"></div>');
const repeats = 12;
let count = 0;
while (count < repeats) {
$('body').append($(a).clone())
count++;
}
//$( 'body' ).append( html );
});
.a {
background-color: red;
box-sizing: border-box;
border: 1px solid green;
display: inline-flex;
height: 25px;
width: 25%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
</body>
</html>
A look at the dev tools inspector reveals a bit of invisible code between the first and second items:
When I delete those lines in the inspector, the gap is removed and all boxes line up as intended.
So it's an issue with your script appending elements. I'm not sure how you want to handle that (e.g., is the script only for this demo? is the problem only in this IDE? is removing the first element an option?), so I won't get into solutions.

HTML/CSS/Javascript delete button

I have a list of items inside a div that is determined by the contents of two arrays.
product_codes=[code1, code2, code3];
quantities=[1, 34, 67,];
Every time a new code and quantity is added to its respective array, I have a javascript function that does this:
document.getElementById('cart_body').innerHTML='';
cart_text='';
elf='<br class="none"/>';
for(i=0; i<product_codes.length; i++){
cart_text+=(product_codes[i]+" (x"+quantities[i]+")"+elf);
}
document.getElementById('cart_body').innerHTML=cart_text;
and acts upon This HTML:
<div id='cart_body'></div>
with this CSS:
.none{margin-top: 0px;}
(the CSS simply overrides another styling I gave to ALL tags)
What I want to do, is at the end of each line added to cart_text (before the inserted line break), is to add a small circular button with an x in the center (I imagine that there's something like that in Bootstrap, but I am unable to use it or any other libraries) that when clicked, deletes the text next to it ON THAT LINE ONLY (the product code and quantity) from the div, AND deletes the two items(product code and quantity) from their respective arrays. Ideally, the aforementioned delete button would look something like the button that lets you delete a your comment that you've posted(here on Stack Overflow).
Please only Vanilla CSS and Javascript answers only. No libraries, please.
If it's not too much to ask, a working JsFiddle would be great too.
Thanks!
Edit
Attempt at the button: #1
#close_button{
border: 1px solid black;
padding-top: 0;
max-width: 15px;
max-height: 15px;
background-color: lightBlue;
border-radius: 90px;
font-size: 14px;
text-align: center;
}
<div id='close_button'>x</div>
This does not work because I cannot get a proper size with the x in the exact center of the circle. I tried padding, all that good stuff, but to no avail.
You can use the following code at https://jsfiddle.net/osha90/krrhvdmj/
<div id='cart_body'>
<p>
code1 x1 <span style="display:inline-block;width:30;height:30;background-color:#d2d2d2; border-radius: 50%;font-size:12px;line-height:18px;">X</span>
</p>
var product_codes=["code1", "code2", "code3"];
var quantities=[1, 34, 67,];
document.getElementById('cart_body').innerHTML='';
for(i=0; i<product_codes.length; i++){
var p = document.createElement("p");
p.appendChild(document.createTextNode(product_codes[i] +" X "+quantities[i]));
var span = document.createElement("span");
span.appendChild(document.createTextNode("X"));
span.classList.add("delete");
var a = document.createAttribute("data-productCode");
a.value = product_codes[i];
span.setAttributeNode(a);
p.appendChild(span);
span.addEventListener("click",removeElm);
document.getElementById('cart_body').appendChild(p);
}
function removeElm(){
var div = this.parentNode.parentNode;
for(i=0; i<product_codes.length; i++){
if(product_codes[i] == this.getAttribute("data-productCode"))
{product_codes.splice(i,1);
quantities.splice(i,1);
console.log(product_codes);
console.log(quantities);
break;
}
}
div.removeChild(this.parentNode);
}
css Code
.delete{
display: inline-block;
width: 19px;
height: 18px;
text-align: center;
background-color: #D2D2D2;
border-radius: 50%;
font-size: 12px;
line-height: 18px;
cursor: pointer;
}

How to set a max-width only to text nodes not in a tag?

I have mixed well and bad formatted text from a legacy wordpress database. Well formated is inside p tags and bad formatted is outside. So at the end the HTML is like that:
<div>
<p>Good text</p>
<blockquote>Good text</blockquote>
Problematic text <strong>like this</strong> one.
<p>Good text</p>
</div>
The p text has a max-width set and is centered:
p {
max-width: 300px;
margin: 0 auto;
padding: 10px;
}
The blockquote element or other divs are not width-limited.
As you can see in this fiddle example, my problem is that the non-p text is left-aligned. I don't know if it's possible to center using just CSS. Using javascript my approach was to do this:
jQuery("div").contents().filter(function() { return this.nodeType === 3; }).wrap('<p>');
This is ok in general, buy when you have strong or em tags in the middle it doesn't work (example).
So, is CSS able to do this? If not, how to do in Javascript? Of course, I prefer the CSS option, but JS is a better option than reformat the whole database :)
Clarification: The objective is to limit with max-width only the p-tags and the bad-fomatted text elements (which include text and some tags like strong or em). Other elements must have 100% width, it is, not limited by the 300px max-width (i.e. blockquote must use all the available screen size).
Here's a jQuery solution that will wrap the contents that aren't already in <p> or <blockquote>.
Can be easily adapted to include other acceptable tags
var $container = $('div'),
$contents = $container.contents();
var validTags = ['P', 'BLOCKQUOTE'];
var newP = null;
$contents.each(function (i) {
if (this.nodeType === 3 || $.inArray(this.tagName, validTags) == -1) {
if (!newP) { // start creating a new <p>
newP = $('<p style="color:red">')
}
newP.append(this); // append to the new <p>
} else {
if (newP) {
$(this).before(newP); //insert new <p> if there is one
newP = null; //reset
}
}
/* in case text is after all valid tags, add to end */
if (i == $contents.length - 1 && newP) {
$container.append(newP);
}
});
Note that <div> can't be appended to <p> (invalid child) so this approach would probably need some more refinement for situations like that. It does work on sample provided however
DEMO
To center all the content inside the div:
CSS:
div {
text-align: center;
}
To center some divs (example 1st and 3rd from 4), selecting them by id:
CSS:
div#sect1, div#sect3{
text-align: center;
}
HTML:
<div id="sect1>
<!-- contents -->
</div>
<div id="sect2>
<!-- contents -->
</div>
<div id="sect3>
<!-- contents -->
</div>
<div id="sect4>
<!-- contents -->
</div>
Try adding the same styles to the body and the strong tags:
strong {
font-weight: normal; margin: 0 auto;
padding: 10px;}
body {
max-width: 300px;
margin: 0 auto;
padding: 10px;
}
Why don't you apply the max-width on the container element and remove it from other descendent elements.
div.container {
max-width: 300px;
margin: 0 auto;
}
check this fiddle.
EDIT:
you can use the negative margins if you know your main container width. e.g. use -150px margin if your content area is 300px and it's container is 600px and you want bloquote to be 600px wide.
Fiddle
<style>
body{
text-align:center;
}
div:before{
text-align:center;
max-width:300px;
margin:0 auto;
}
p{
max-width: 300px;
margin: 0 auto;
padding: 10px;
}
blockquote{
font-size: 2em;
width:100%;
margin:0 auto;
}
</style>

Categories