Two column flexbox overflowing two columns when considering error messages

Viewed 28

I have a form in which we want the layout to be two columns of inputs in which the first column is inputs 1, 2, 3, 4 and the second column is inputs 5, 6, 7, 8. We want it to be this way because when considering mobile displays, we want a single column of inputs 1,2,3,4,5,6,7,8.

Now, as you can see from the example below, I've put a max-height on the ol tag in order to constrain the inputs to two columns. Everything works nicely when moving from desktop to mobile displays.

The problem comes when form validation kicks in and an error message is displayed. The error message needs to appear below the input and when it does, it increases the height of each flex item. Now, becomes I am constrained by the max-height property, the resulting layout is no longer two columns.

I can't easily play the max-height property to accommodate two columns with and without the error messages showing. Without the error messages, if the max height is two large, then it is no longer 4 inputs per column.

Is there a way in CSS to make it so that I can keep 4 inputs to a column even with the added height due to the error message?

Jsfiddle: https://jsfiddle.net/vz95fh6x/

const submitButton = document.getElementById("button");
submitButton.onclick = validateForm;

/* Checks to see if the form inputs are empty. If so, display error message */
function validateForm() {
    const inputItems = document.getElementsByClassName('input-item')
    
    for (const item of inputItems) {
        const input = item.children[1]
        if (input.value === '') {
            item.children[2].innerHTML = 'Enter an input'
        }
    }
}
ol {
    background-color: lightblue;
    list-style-type: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    max-height: 100px;
}

@media (max-width: 500px) {
    ol {
        display: block;
        max-height: none;
    }
}

ol li {
    flex: 1 auto;
}

.error {
    color: firebrick;
}
<div class="wrapper">
    <ol>
        <li>
            <div class="input-item">
                <label for="item1">Item 1</label>
                <input type="text" id="item1" name="item1">
                <div class="error"></div>
            </div>
        </li>

        <li>
            <div class="input-item">
                <label for="item2">Item 2</label>
                <input type="text" id="item2" name="item2">
                <div class="error"></div>
            </div>
        </li>

        <li>
            <div class="input-item">
                <label for="item3">Item 3</label>
                <input type="text" id="item3" name="item3">
                <div class="error"></div>
            </div>
        </li>

        <li>
            <div class="input-item">
                <label for="item4">Item 4</label>
                <input type="text" id="item4" name="item4">
                <div class="error"></div>
            </div>
        </li>

        <li>
            <div class="input-item">
                <label for="item5">Item 5</label>
                <input type="text" id="item5" name="item5">
                <div class="error"></div>
            </div>
        </li>

        <li>
            <div class="input-item">
                <label for="item6">Item 6</label>
                <input type="text" id="item6" name="item6">
                <div class="error"></div>
            </div>
        </li>

        <li>
            <div class="input-item">
                <label for="item7">Item 7</label>
                <input type="text" id="item7" name="item7">
                <div class="error"></div>
            </div>
        </li>

        <li>
            <div class="input-item">
                <label for="item8">Item 8</label>
                <input type="text" id="item8" name="item8">
                <div class="error"></div>
            </div>
        </li>
    </ol>

    <br />

    <button id="button" onclick={validateForm()}>
        Submit
    </button>
</div>

1 Answers

I suggest to use flexbox to make this go well responsive wise. You don't need the max-width element, just use a @media selector to make the flex direction go from row to column when the screen width is 787px (Mobile resolution)

I also inserted two <div> elements within the <ol> so that flexbox can use those two <div> elements as two columns on PC and iPad.

PS: Make sure to click "Expand Snippet" to see the responsive query go to work because if you run the snippet within my post the media query will be used. OR check the JSFiddle

const submitButton = document.getElementById("button");
submitButton.onclick = validateForm;

/* Checks to see if the form inputs are empty. If so, display error message */
function validateForm() {
  const inputItems = document.getElementsByClassName('input-item')

  for (const item of inputItems) {
    const input = item.children[1]
    if (input.value === '') {
      item.children[2].innerHTML = 'Enter an input'
    }
  }
}
ol {
  background-color: lightblue;
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

ol div {
  padding: 0 5px;
}

ol li {
  flex: 1 auto;
}

.error {
  color: firebrick;
}

@media only screen and (max-width: 787px /* mobile */) {
  ol {
    flex-direction: column!important;
  }
}
<div class="wrapper">
  <ol>
    <div>
      <li>
        <div class="input-item">
          <label for="item1">Item 1</label>
          <input type="text" id="item1" name="item1">
          <div class="error"></div>
        </div>
      </li>

      <li>
        <div class="input-item">
          <label for="item2">Item 2</label>
          <input type="text" id="item2" name="item2">
          <div class="error"></div>
        </div>
      </li>

      <li>
        <div class="input-item">
          <label for="item3">Item 3</label>
          <input type="text" id="item3" name="item3">
          <div class="error"></div>
        </div>
      </li>

      <li>
        <div class="input-item">
          <label for="item4">Item 4</label>
          <input type="text" id="item4" name="item4">
          <div class="error"></div>
        </div>
      </li>
    </div>
    <div>
      <li>
        <div class="input-item">
          <label for="item5">Item 5</label>
          <input type="text" id="item5" name="item5">
          <div class="error"></div>
        </div>
      </li>

      <li>
        <div class="input-item">
          <label for="item6">Item 6</label>
          <input type="text" id="item6" name="item6">
          <div class="error"></div>
        </div>
      </li>

      <li>
        <div class="input-item">
          <label for="item7">Item 7</label>
          <input type="text" id="item7" name="item7">
          <div class="error"></div>
        </div>
      </li>

      <li>
        <div class="input-item">
          <label for="item8">Item 8</label>
          <input type="text" id="item8" name="item8">
          <div class="error"></div>
        </div>
      </li>
    </div>
  </ol>
  <br />

  <button id="button" onclick={validateForm()}>
        Submit
    </button>
</div>

After reading your comment regarding the flexbox solution not being kosher, I looked around for a little bit and found that this is also an option. You can simply define how many columns a <ol> or <ul> has by using the css element column-count.

For more information about column-count click here.

const submitButton = document.getElementById("button");
submitButton.onclick = validateForm;

/* Checks to see if the form inputs are empty. If so, display error message */
function validateForm() {
  const inputItems = document.getElementsByClassName('input-item')

  for (const item of inputItems) {
    const input = item.children[1]
    if (input.value === '') {
      item.children[2].innerHTML = 'Enter an input'
    }
  }
}
ol {
  background-color: lightblue;
  list-style-type: none;
  padding: 0;
  margin: 0;
  column-count: 2;
}

.error {
  color: firebrick;
}

@media only screen and (max-width: 787px /* mobile */) {
  ol {
    column-count: 1;
  }
}
<div class="wrapper">
  <ol>
    <li>
      <div class="input-item">
        <label for="item1">Item 1</label>
        <input type="text" id="item1" name="item1">
        <div class="error"></div>
      </div>
    </li>

    <li>
      <div class="input-item">
        <label for="item2">Item 2</label>
        <input type="text" id="item2" name="item2">
        <div class="error"></div>
      </div>
    </li>

    <li>
      <div class="input-item">
        <label for="item3">Item 3</label>
        <input type="text" id="item3" name="item3">
        <div class="error"></div>
      </div>
    </li>

    <li>
      <div class="input-item">
        <label for="item4">Item 4</label>
        <input type="text" id="item4" name="item4">
        <div class="error"></div>
      </div>
    </li>
    <li>
      <div class="input-item">
        <label for="item5">Item 5</label>
        <input type="text" id="item5" name="item5">
        <div class="error"></div>
      </div>
    </li>

    <li>
      <div class="input-item">
        <label for="item6">Item 6</label>
        <input type="text" id="item6" name="item6">
        <div class="error"></div>
      </div>
    </li>

    <li>
      <div class="input-item">
        <label for="item7">Item 7</label>
        <input type="text" id="item7" name="item7">
        <div class="error"></div>
      </div>
    </li>

    <li>
      <div class="input-item">
        <label for="item8">Item 8</label>
        <input type="text" id="item8" name="item8">
        <div class="error"></div>
      </div>
    </li>
  </ol>
  <br />

  <button id="button" onclick={validateForm()}>
        Submit
    </button>
</div>

So there you have it, two solutions to responsively style a <ol>

Related