2

I am new to html and css. I'd like to create an function that given a number k between 1 to 9, there will be k divs displayed inside the container div. However, the arrangement of blocks varies as k differs. For example, for k=4, the arrangement will be 2x2, while for k=6, the arrangement will be 3x2. All arrangement can be seen in the below picture. picture Is there any way I can adjust the arrangement based on the number of divs? I guess I should use flexbox for css but I somehow could not find the correct configuration of the container.

Adi_Hsiao_0410
  • 111
  • 2
  • 9

3 Answers3

1

Here is a pure CSS solution using display: grid; and the :has() CSS pseudo-class. Using :has() we can apply styles to an element based on its children count:

#container:has(div:nth-child(3)) {
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

The above style will apply to the element with id="container" only when it contains at least 3 child elements.

JS Fiddle

(JavaScript only used to add divs to container for demonstration)

Sources:

Article explaining how to style elements based on their children count: https://www.matuzo.at/blog/2022/counting-children/

:has() CSS pseudo-class: https://developer.mozilla.org/en-US/docs/Web/CSS/:has

Dimitris Maragkos
  • 8,932
  • 2
  • 8
  • 26
1

There is a pure CSS solution that has better browser support than has (https://caniuse.com/?search=has).

The need for has arises when a parent's styles need to be dependent on what children it has. In this case, you can style the children themselves based on the number of siblings they have. You could do this with flexbox, or even with something older like floats (I suspect you could do it with grid too but I haven't sat down to figure out how exactly).

What you'd do is plan for three columns ordinarily by forcing the items to have a width of 1/3 (depending on your design you might need some jiggery pokery to allow for margins), then override that in cases with fewer than five items. Here's a basic suggestion using flex:

.parent {
  display: flex;
  flex-wrap: wrap;
}

.child {
  width: calc(100% / 3);
}

/* for one item */
.child:only-child {
  width: 100%;
}

/* for two items */
.child:nth-child(1):nth-last-child(2),
.child:nth-child(2):nth-last-child(1) {
  width: 50%;
}

/* for three items */
.child:nth-child(1):nth-last-child(3),
.child:nth-child(2):nth-last-child(2),
.child:nth-child(3):nth-last-child(1) {
  width: 50%;
}

/* for four items */
.child:nth-child(1):nth-last-child(4),
.child:nth-child(2):nth-last-child(3),
.child:nth-child(3):nth-last-child(2),
.child:nth-child(4):nth-last-child(1) {
  width: 50%;
}

Essentially, those nth-child selectors are saying "if the first item from the start is also the fourth item from the end, there must be four items". There's a full explanation of this technique in a link in the article that @Dimitris Maragkos shared (https://alistapart.com/article/quantity-queries-for-css/).

I've put all this in a pen at https://codepen.io/jonedge/pen/abapJEK if you wanted to test it. Note I've used a child class here for readability but when working with flex and grid I'd usually recommend using .parent > * instead to select all of the flex/grid container's direct children.

Jon
  • 129
  • 1
  • 3
  • Here's an improvement on the selectors taken from the article I linked to: `.child:nth-last-child(-n+4):first-child, .child:nth-last-child(-n+4):first-child ~ * {width: 50%;} .child:only-child:only-child {width: 100%;}` – Jon Feb 28 '23 at 15:31
0

HTML:Creating Input for K value with submit & reset Buttons,creating a div container .wrapper.

CSS:giving my container 400x400 Size. declaring a :root variable --width:50%; which is being used to set width of a single box which will be generated through JS.

JS:adding EventListener on submit button. first thing i am doing is getting the value of input(K). putting an if condition to check if user has already generated any divs,if has, i remove every thing inside the wrapper with innerHTML = ""

putting another if to check if the number is greater than 4 then making the size of each box 33% to fill our 3x3 requirment by changing the value of the variable in the :root to --width:33%`.

looping inside for loop to create our box.Boxes will generate (k)Times. with html inside createBox and adding it inside wrapper. i hope you understood.if you have any questions please ask.

let wrapper = document.querySelector(".wrapper");
let btn = document.querySelector(".btn");
let resetBtn = document.querySelector(".r-btn");

btn.addEventListener("click", () => {
  let input = document.querySelector("#input").value;
  if (wrapper.innerHTML !== "") {
    wrapper.innerHTML = "";
  }
  if (input > 4) {
    document.querySelector(":root").style.setProperty("--width", "33.3%");
  } else {
    document.querySelector(":root").style.setProperty("--width", "50%");
  }
  for (let i = 0; i < input; i++) {
    let createBox = `<div class="box">${i + 1}</div>`;
    wrapper.innerHTML += createBox;
  }

  document.querySelector("#input").value = "";
});
resetBtn.addEventListener("click", () => {
  wrapper.innerHTML = "";
});
:root{
  --width:50%;
}
#input {
  width: 100px;
  margin-bottom: 10px;
}
.box {
  background-color: burlywood;
  text-align: center;
  width: var(--width);
  outline: 1px solid green;
}
.wrapper {
  display: flex;
  flex-wrap: wrap;
  width: 400px;
  height: 400px;
  outline: 1px solid red;
}
<input min="2" max="9" type="number" placeholder="Enter K Value" id="input" />
<button class="btn">Enter</button>
<button class="r-btn">Reset</button>
<div class="wrapper"></div>
UmairFarooq
  • 1,269
  • 1
  • 3
  • 8