htmlcss

css sticky not working on horizontal scrolls


I have an html form that includes a wide table along with the expected label/input fields. The requirement is that the window/frame has the ability to scroll horizontally, scrolling the table as expected, but leaving the fields in place during that scroll.

Trying to place the table in a div that can scroll horizontally is not good enough, as this results in the need to scroll down to the bottom of the table to find the scrollbar - hence the h scrollbar needs to sit on the whole frame.

I have tried the following code - and it's close, but when (1) I'd prefer to have the red div's to be 100vw instead of 50vw and (2) it only seems to work when the screen is wider ... thinner screens seem to keep the inputs sticky for a while - and then they scroll off

Sample usage gif https://imgur.com/c0sc534

.form-container > div {
    position:sticky;
    left:10px;
    color:red;
    border:solid 1px red;
    width:50vw;
}

Demo - also on JSFiddle

function createRowCopies(tr, num) {
  if (!tr || num <= 0) return;

  // Ensure the `tr` element is valid
  if (!(tr instanceof HTMLTableRowElement)) {
    console.error("Invalid row element passed to createRowCopies.");
    return;
  }

  // Loop to create and append copies
  for (let i = 0; i < num; i++) {
    // Clone the row
    const clone = tr.cloneNode(true);

    // Append the cloned row right after the original
    tr.parentNode.insertBefore(clone, tr.nextSibling);

    // Update `tr` reference to the last inserted row
    tr = clone;
    tr.children[0].innerHTML = i + 2;
  }
}
createRowCopies(document.querySelector('tbody tr'), 30);
.form-container {
  padding: 10px;
  border: 1px solid #ccc;
  display: unset;
  max-width: 100%;
  /* Ensures it doesn't exceed the viewport width */
}

.form-container>div {
  position: sticky;
  left: 10px;
  color: red;
  border: solid 1px red;
  width: 50vw;
}

label {
  display: block;
  padding: 10px;
}

table {
  border-collapse: collapse;
}

th,
td {
  border: 1px solid #ccc;
  padding: 8px;
  white-space: nowrap;
}

thead th {
  background-color: #f8f8f8;
  position: sticky;
}
<div class="form-container">
  <div>
    <label>
      Name:
      <input type="text" name="name" />
    </label>
  </div>
  <div>
    <label>
      Email:
      <input type="email" name="email" />
    </label>
  </div>
  <table>
    <thead>
      <tr>
        <th>#</th>
        <th>Column 1</th>
        <th style="min-width: 200px">Column 2</th>
        <th style="min-width: 100px">Column 3</th>
        <th>Column 4</th>
        <th>Column 5</th>
        <th>Column 6</th>
        <th>Column 7</th>
        <th>Column 8</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>Data 1</td>
        <td>Data 2</td>
        <td>Data 3</td>
        <td>Data 4</td>
        <td>Data 5</td>
        <td>Data 6</td>
        <td>Data 7</td>
        <td>Data 8</td>
      </tr>
    </tbody>
  </table>

  <div>
    <label>
      Comments:
      <textarea name="comments" rows="4"></textarea>
    </label>
  </div>
  <div>
    <button type="submit">Submit</button>
  </div>
</div>

.... or if you want to play with the code (or change the screen width) then use https://jsfiddle.net/ntsf7ajp


Solution

  • sticky elements stop being sticky during the scrolling due to the fact that they reach the end of .form-container width, which happens to be much narrower than its content size. This occurs for several reasons:

    To achieve the desired behavior, you'll just have to set width: max-content to ensure the container adjusts to its content's width.

    .form-container {
      padding: 10px;
      border: 1px solid #ccc;
      width: max-content;
    }