How to display x items on left, y on middle, and z on right in a row without wrapping elements?
Asked Answered
F

5

8

I would like to do something similar to justify-content:space-around or justify-content:space-between, but with :

  • x items side by side on the left,
  • y items side by side on the middle,
  • z items side by side on the right.

enter image description here

It would be simple by wrap elements but I can't because these items (.left, .middle, .right) would be checkboxes influencing the styles of elements below (and there is no well-supported parent selector).

I found this answer to emulate first-of-class for the right side, but didn't find something similar to emulate last-of-class.

/* emulate first-of-class */
.container>.right {
  margin-left: auto;
}

.container>.right~.right {
  margin-left: unset;
}

Here is a snippet of my current attempt :

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

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
}

.container>.right {
  margin-left: auto;
}

.container>.right~.right {
  margin-left: unset;
}

#check-center {
  width: 100%;
  text-align: center;
}

[ lang ] {
  display: none;
}

[ name="language" ]:checked+label {
  background-color: pink;
}

[ value="en" ]:checked ~  [ lang="en" ],
[ value="en" ]:checked ~* [ lang="en" ],
[ value="fr" ]:checked ~  [ lang="fr" ],
[ value="fr" ]:checked ~* [ lang="fr" ],
[ value="es" ]:checked ~  [ lang="es" ],
[ value="es" ]:checked ~* [ lang="es" ] {
  display: block;
}

/*for codepen*/
html[ lang ] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <input type="radio" name="left-field" id="B" />
  <label class="left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="right" for="enLang">en</label>
  <input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label>
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label>

  <div id="check-center">|</div>

  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>

I could cheat by adding a specific class to the last left element to apply margin-right:auto; and the first right element to apply margin-left:auto; in a display:flex container but it's not the best and even then, the middle items wouldn't be centered if the left and right parts have not the same width.

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

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
}

.cheat.left {
  margin-right: auto;
}

.cheat.right {
  margin-left: auto;
}

#check-center {
  width: 100%;
  text-align: center;
}

[ lang ] {
  display: none;
}

[ name="language" ]:checked+label {
  background-color: pink;
}

[ value="en" ]:checked ~  [ lang="en" ],
[ value="en" ]:checked ~* [ lang="en" ],
[ value="fr" ]:checked ~  [ lang="fr" ],
[ value="fr" ]:checked ~* [ lang="fr" ],
[ value="es" ]:checked ~  [ lang="es" ],
[ value="es" ]:checked ~* [ lang="es" ] {
  display: block;
}

/*for codepen*/
html[ lang ] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <input type="radio" name="left-field" id="B" />
  <label class="cheat left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="cheat right" for="enLang">en</label>
  <input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label>
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label>

  <div id="check-center">|</div>

  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>

My current goal is to display elements depending on the selected language even if JavaScript is disabled, so I'm looking for a solution without JavaScript.

As HackerFrosch suggested, I tried to solve it by using a grid but I'm not used to it, the .middle items are not centered and I did not manage to make the elements below .right divs 100% width as by default.

.container {
  display: grid;
  grid-template-columns: auto 1fr auto auto auto 1fr auto auto;
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
  width: fit-content;
}

.cheat.left {
  margin-right: auto;
}

.cheat.right {
  margin-left: auto;
}

#check-center {
  width: 100%;
  text-align: center;
}

[ lang] {
  display: none;
}

[ name="language"]:checked+label {
  background-color: pink;
}

[ value="en"]:checked~[ lang="en"],
[ value="en"]:checked~* [ lang="en"],
[ value="fr"]:checked~[ lang="fr"],
[ value="fr"]:checked~* [ lang="fr"],
[ value="es"]:checked~[ lang="es"],
[ value="es"]:checked~* [ lang="es"] {
  display: block;
}


/*for codepen*/

html[ lang] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <input type="radio" name="left-field" id="B" />
  <label class="cheat left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="cheat right" for="enLang">en</label>
  <input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label>
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label>
  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>
<div id="check-center">|</div>

Is there a way to achieve this?

Fourfold answered 27/10, 2022 at 8:2 Comment(12)
Hello, can you explain what are you trying to achieve more? The snippet and explanation still didnt clear it out for me. A simple image will be more helpful. Do you want to show "radiobutton div" on the left, divider in the middle and "language" div on the right? Or do you want to even divide the "radiobutton div" into left middle and right?Provisional
@RaunaqPatel I added an imageEzarra
will you be able to wrap inputs into their individual divs? EG left inputs in .left div, center inputs in .center div and so on... Since in your code snippet its all under .Container?Provisional
No, that is the point of the questionEzarra
Sorry but I couldnt think about any good way to do this with that restriction. .container > .middle ~ .middle{ margin: unset !important; } .container > .left ~ .middle{ margin-left: auto; } remotely made things centered but then since left items and right items are not there, it couldnt do itProvisional
@Fourfold why can’t you add a container?Malacology
@Malacology I use some of the items to change properties of elements below only with css. If I add a container to those "nav items", I won't be able to reach next elements because you can access to siblings or children but not parents in css, or it's not well supportedEzarra
@Fourfold I know maybe you don’t want to, but just use JavaScript. There‘s no need to do something like this with css and it’s just complicated and unnecessary.Malacology
"complicated" is not a good reason to give up the idea, and "unecessary" is your opinion that I disagree with. It won't be usefull in most cases but sometimes it will be.Ezarra
@Fourfold Well I would say „complicated“ is a very good reason to not do something if you can do it way easier otherwise. In your case why is it useful to do it with css instead of js?Malacology
As your previous question, I quickly explained it in my post "My current goal is to display elements depending on the selected language even if JavaScript is disabled, so I'm looking for a solution without JavaScript.". This reason is a choice and a part of the constraints.Ezarra
possible guidance: https://mcmap.net/q/36656/-center-one-and-right-left-align-other-flexbox-element/3597276Preterite
M
3

A solution to your problem is the css grid layout. You can customize the positioning in this example if you want. I hope that helps.

.container {
  display: grid;
  grid-template-columns: 50px auto 50px 50px auto 50px 50px 50px;
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
  width: 20px;
}

#check-center {
  width: 100%;
  text-align: center;
}

[ lang ] {
  display: none;
}

[ name="language" ]:checked+label {
  background-color: pink;
}

[ value="en" ]:checked ~  [ lang="en" ],
[ value="en" ]:checked ~* [ lang="en" ],
[ value="fr" ]:checked ~  [ lang="fr" ],
[ value="fr" ]:checked ~* [ lang="fr" ],
[ value="es" ]:checked ~  [ lang="es" ],
[ value="es" ]:checked ~* [ lang="es" ] {
  display: block;
}

/*for codepen*/
html[ lang ] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <input type="radio" name="left-field" id="B" />
  <label class="left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="right" for="enLang">en</label>
  <input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label>
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label>

  <div id="check-center">|</div>

  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>
Malacology answered 1/11, 2022 at 11:29 Comment(1)
It's a good start, I changed the grid template for grid-template-columns: auto 1fr auto auto auto 1fr auto auto; but as in your snippet, .middle elements aren't centered, and I'm not sure how next elements (after .right els) could take 100% width as usual with these rules. Anyway, I would like if possible to find a more flexible solution so I don't have to change css if I want to add or delete a .left , .middle or .right element. Thanks for your contribution.Ezarra
I
1

You can try to solve this with flex and use the calc() function to manage the left or right margins. So, I created four classes to use one of them to center the middle classes.

/* adjust for left side, put to the first middle label */
.one-left.middle {
  margin-inline-start: calc((4 * var(--block-width) / 2));
}
.two-left.middle {
  margin-inline-start: calc((3 * var(--block-width) / 3));
}

/* adjust for right side, set to the last meddle label */
.one-right.middle {
  margin-inline-end: calc((4 * var(--block-width) / 2));
}
.two-right.middle {
  margin-inline-end: calc((3 * var(--block-width) / 3));
}

Usage .two-left.middle

*,
::after,
::before {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.container {
  --block-width: 50px;
  display: flex;
  justify-content: center;
}

.gap.left {
  margin-inline-end: auto;
}

.gap.right {
  margin-inline-start: auto;
}


/* adjust for left side, put to the first middle label */
.one-left.middle {
  margin-inline-start: calc((4 * var(--block-width) / 2));
}

.two-left.middle {
  margin-inline-start: calc((3 * var(--block-width) / 3));
}


/* adjust for right side, set to the last meddle label */
.one-right.middle {
  margin-inline-end: calc((4 * var(--block-width) / 2));
}

.two-right.middle {
  margin-inline-end: calc((3 * var(--block-width) / 3));
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
  width: var(--block-width);
}

#check-center {
  width: 100%;
  text-align: center;
}

[lang] {
  display: none;
}

[name='language']:checked+label {
  background-color: pink;
}

[value='en']:checked~[lang='en'],
[value='en']:checked~* [lang='en'],
[value='fr']:checked~[lang='fr'],
[value='fr']:checked~* [lang='fr'],
[value='es']:checked~[lang='es'],
[value='es']:checked~* [lang='es'] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <!-- <input type="radio" name="left-field" id="A1" />
      <label class="left" for="A1">A1</label> -->
  <input type="radio" name="left-field" id="B" />
  <label class="gap left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="two-left middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="gap right" for="enLang">en</label>
  <input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label>
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label>
  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>
<div id="check-center">|</div>

Usage .one-right.middle

*,
::after,
::before {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.container {
  --block-width: 50px;
  display: flex;
  justify-content: center;
}

.gap.left {
  margin-inline-end: auto;
}

.gap.right {
  margin-inline-start: auto;
}


/* adjust for left side, put to the first middle label */
.one-left.middle {
  margin-inline-start: calc((4 * var(--block-width) / 2));
}

.two-left.middle {
  margin-inline-start: calc((3 * var(--block-width) / 3));
}


/* adjust for right side, set to the last meddle label */
.one-right.middle {
  margin-inline-end: calc((4 * var(--block-width) / 2));
}

.two-right.middle {
  margin-inline-end: calc((3 * var(--block-width) / 3));
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
  width: var(--block-width);
}

#check-center {
  width: 100%;
  text-align: center;
}

[lang] {
  display: none;
}

[name='language']:checked+label {
  background-color: pink;
}

[value='en']:checked~[lang='en'],
[value='en']:checked~* [lang='en'],
[value='fr']:checked~[lang='fr'],
[value='fr']:checked~* [lang='fr'],
[value='es']:checked~[lang='es'],
[value='es']:checked~* [lang='es'] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <input type="radio" name="left-field" id="A1" />
  <label class="left" for="A1">A1</label> 
  <input type="radio" name="left-field" id="B" />
  <label class="gap left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="one-right middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="gap right" for="enLang">en</label>
  <!--<input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label> 
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label> -->
  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>
<div id="check-center">|</div>
Indican answered 9/11, 2022 at 17:18 Comment(0)
T
0

Something like this - using left margin auto on the first center and right classes:

/* flex container */
.container { display: flex; }

/* left margin auto on all center/right classes */
.center, .right { margin-left: auto; }

/* remove auto margin on non firsts */
.center~.center, .right~.right { margin-left: initial; }



/* just a bit of styling */
div { padding: .5rem; }
.left { background: tomato; }
.center { background: orange; }
.right { background: yellowgreen; }
<div class="container">
  <div class="left">Left 1</div>
  <div class="left">Left 2</div>
  <div class="left">Left 3</div>

  <div class="center">Center 1</div>
  <div class="center">Center 2</div>
  <div class="center">Center 3</div>

  <div class="right">Right 1</div>
  <div class="right">Right 2</div>
  <div class="right">Right 3</div>
</div>
Touchmenot answered 11/11, 2022 at 15:33 Comment(1)
Unfortunately .center elements are not centeredEzarra
T
0

This does well to maintain the center while still allowing css selectors. It will maintain center adding more elements to the left, middle, or right containers. The middle container can have odd or even elements and it will still maintain center, provided there is adequate room.

The limitations of this approach is that your header height must be fixed, and the absolutely positioned #dynamic_children div top property must reflect that value. Also, due to IE 11's poor flex implementation, this will not work properly and obviously they will never fix that since IE 11 is no longer supported. I am unsure if that matters for you.

body {
  padding: 0;
  margin: 0;
}

.container {
  display: flex;
  flex-wrap: nowrap;
  height: 30px;
  max-height: 30px;
}

#left_container {
  display: flex;
  justify-content: left;
  flex: 1;
  flex-wrap: nowrap;
  align-items: center;
}

#middle_container {
  display: flex;
  justify-content: center;
  flex: 0;
  flex-wrap: nowrap;
  align-items: center;
}

#right_container {
  display: flex;
  justify-content: right;
  flex: 1;
  flex-wrap: wrap;
  align-items: center;
  margin-left: auto;
}

#check-center {
  display: block;
  width: 100%;
  height: 20px;
  text-align: center;
  background-color: rgb(255, 255, 0);
}

#dynamic_children {
  width: 100%;
  display: block;
  position: absolute;
  top: 30px;
  left: 0;
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
}

[ lang] {
  display: none;
}

[ name="language"]:checked+label {
  background-color: pink;
}

[ value="en"]:checked~[ lang="en"],
[ value="en"]:checked~* [ lang="en"],
[ value="fr"]:checked~[ lang="fr"],
[ value="fr"]:checked~* [ lang="fr"],
[ value="es"]:checked~[ lang="es"],
[ value="es"]:checked~* [ lang="es"] {
  display: block;
}
<body>
  <div class="container">
    <div id="left_container">
      <input type="radio" name="left-field" id="A" />
      <label class="left" for="A">A</label>
      <input type="radio" name="left-field" id="B" />
      <label class="left" for="B">B</label>
    </div>
    <div id="middle_container">
      <input type="radio" name="middle-field" id="C" />
      <label class="middle" for="C">C</label>
      <input type="radio" name="middle-field" id="D" />
      <label class="middle" for="D">D</label>
      <input type="radio" name="middle-field" id="E" />
      <label class="middle" for="E">E</label>
    </div>
    <div id="right_container">
      <input type="radio" name="language" id="enLang" value="en" />
      <label class="right" for="enLang">en</label>
      <input type="radio" name="language" id="frLang" value="fr" />
      <label class="right" for="frLang">fr</label>
      <input type="radio" name="language" id="esLang" value="es" />
      <label class="right" for="esLang">es</label>
      <div id="dynamic_children">
        <div id="check-center">-|-</div>
        <div>
          <div lang="en">EN selected div 1</div>
          <div lang="fr">FR selected div 1</div>
          <div lang="es">ES selected div 1</div>

          <div lang="en">EN selected div 2</div>
          <div lang="fr">FR selected div 2</div>
          <div lang="es">ES selected div 2</div>

          <div lang="en">EN selected div 3</div>
          <div lang="fr">FR selected div 3</div>
          <div lang="es">ES selected div 3</div>
        </div>
      </div>
    </div>
  </div>
</body>
Tobytobye answered 15/11, 2022 at 23:33 Comment(2)
Thanks for you answer, maybe it lacks of clarity for this point, I need to access to all .left, .middle and .right elements to affect next elementsEzarra
Yes, it would probably help to add left and middle controlled divs to your sample code, even if it has no layout as well as what you just said in this comment.Tobytobye
G
-1

As per your requirements, you need to use grid property.

<!DOCTYPE html>
<html>
   <style>
      .container {
      display: grid;
      grid-template-columns: 50px auto 50px 50px auto 50px 50px 50px;
      }
      input {
      display: none;
      }
      label {
      padding: 0 10px;
      background-color: orange;
      width: 12px;
      }
      #check-center {
      width: 100%;
      text-align: center;
      }
      [ lang ] {
      display: none;
      }
      [ name="language" ]:checked+label {
      background-color: pink;
      }
   </style>
   <body>
      <div class="container">
         <input type="radio" name="left-field" id="A" />
         <label class="left" for="A">A</label>
         <input type="radio" name="left-field" id="B" />
         <label class="left" for="B">B</label>
         <input type="radio" name="middle-field" id="C" />
         <label class="middle" for="C">C</label>
         <input type="radio" name="middle-field" id="D" />
         <label class="middle" for="D">D</label>
         <input type="radio" name="middle-field" id="E" />
         <label class="middle" for="E">E</label>
         <input type="radio" name="language" id="enLang" value="en" />
         <label class="right" for="enLang">en</label>
         <input type="radio" name="language" id="frLang" value="fr" />
         <label class="right" for="frLang">fr</label>
         <input type="radio" name="language" id="esLang" value="es" />
         <label class="right" for="esLang">es</label>
         <div lang="en">EN selected</div>
         <div lang="fr">FR selected</div>
         <div lang="es">ES selected</div>
         <div>
            <div lang="en">EN selected</div>
            <div lang="fr">FR selected</div>
            <div lang="es">ES selected</div>
         </div>
      </div>
   </body>
</html>
Gardal answered 10/11, 2022 at 4:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.