Search code examples
csshtm

How to have a sticky element that stops before end of viewport


I have a headline and a thead that need to be sticky, but they shouldn't stay sticky when the last row comes up. Instead, they should stop being sticky so that the last row of the tbody is always visible and is the last element that scrolls away.

I have tried to separate the headline and the thead from the tbody. But i coudn't find a way to let the headline/thead div to stop before the last row.

Edit: I have found a way to maintain the spacing. With a margin-botom with the height of the last line, but I have to find a way to set the margin only when the table body is scrolled, so that there is no space between the header and the first line.

            $(".myTable").each(function () {
                var lastRowOuterHeight = $(this).find('.tableBody tr:last').outerHeight();

                if (true) {// todo how to detect if table body is scrolled?
                    var stickyTable =  $(this).find('.stickyHeader');
                    stickyTable.attr('style', 'margin-bottom: ' + lastRowOuterHeight + 'px !important;');
                }
         
            });
    body {
      height: 1000px;
    }

    .table tbody td {
      min-width: 6rem;
      max-width: 6rem;
    }

    .table th {
      min-width: 6rem;
      max-width: 6rem;
    }

    .stickyHeader {
      position: -webkit-sticky;
      position: sticky;
      top: 0;
      z-index: 2;
      background: #f5f5f5;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!DOCTYPE html>
    <html lang="en">
    <head>
      <title>Bootstrap Example</title>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
      <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.slim.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
    </head>
    <body>

    <div class="myTable pb-4 ">
        <div class=" stickyHeader mb-0" >
            <h4 class="pb-1 ">My headline</h4>
            <div class="table-responsive">
                <table class="tableHeader table table-sm table-bordered border-dark  mb-0">
                    <thead>
                        <tr>
                            
                                <th>Col</th>
                                <th>Col</th>
                                <th>Col</th>
                                <th>Col</th>
                                <th>Col</th>
                                <th>Col</th>
                                <th>Col</th>
                                <th>Col</th>
                                <th>Col</th>
                                <th>Col</th>

                        </tr>
                    </thead>
                </table>
            </div>
        </div>
        <div class="table-responsive">
            <table class="tableBody  table table-responsive table-sm table-bordered border-dark mb-0">
                <tbody>
                        <tr>
                       
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                        </tr>

                        <tr>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                        </tr>

                        <tr>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                            <td > Content </td>
                        </tr>

                  </tbody>
              </table>
          </div>
      </div>
    </body>


Solution

  • I've found a solution:

    get the outerHeiht of the last Row = lastRowHeight get the height of the scrollbar add a margin-bottom lastRowOuterHeight + scrollBarHeight on stickyHeader add a margin-top lastRowOuterHeight - scrollBarHeight on tableBodySection

        $(".myTable").each(function () {
           var lastRowHeight = $(this).find('.tableBody tr:last').outerHeight();
             var tableBody = $(this).find('.tableBody')[0];
           var stickyTableBottom = $(this).find('.stickyHeader');
           var stickyTableTop = $(this).find('.tableBodySection');
           
           var scrollBarHeight = tableBody.offsetHeight - tableBody.clientHeight;
           
           // .css() cannot use !important Attention: .attr() overwrites all other inLine styles  
           stickyTableBottom.attr('style', 'margin-bottom: ' + (lastRowHeight + scrollBarHeight) + 'px !important;');
           stickyTableTop.attr('style', 'margin-top: -' + (lastRowHeight + scrollBarHeight) + 'px !important;');
        });
        body {
          height: 1000px;
        }
    
        .table tbody td {
          min-width: 6rem;
          max-width: 6rem;
        }
    
        .table th {
          min-width: 6rem;
          max-width: 6rem;
        }
    
        .stickyHeader {
          position: -webkit-sticky;
          position: sticky;
          top: 0;
          z-index: 2;
          background: #f5f5f5;
        }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <!DOCTYPE html>
        <html lang="en">
        <head>
          <title>Bootstrap Example</title>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
          <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.slim.min.js"></script>
          <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"></script>
          <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
        </head>
        <body>
    
        <div class="myTable pb-4 ">
            <div class=" stickyHeader mb-0" >
                <h4 class="pb-1 ">My headline</h4>
                <div class="table-responsive">
                    <table class="tableHeader table table-sm table-bordered border-dark  mb-0">
                        <thead>
                            <tr>
                                    <th>Col</th>
                                    <th>Col</th>
                                    <th>Col</th>
                                    <th>Col</th>
                                    <th>Col</th>
                                    <th>Col</th>
                                    <th>Col</th>
                                    <th>Col</th>
                                    <th>Col</th>
                                    <th>Col</th>
                            </tr>
                        </thead>
                    </table>
                </div>
            </div>
            <div class="tableBodySection table-responsive">
                <table class="tableBody  table table-responsive table-sm table-bordered border-dark mb-0">
                    <tbody>
                            <tr>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                            </tr>
    
                            <tr>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                            </tr>
    
                            <tr>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                                <td > Content </td>
                            </tr>
                      </tbody>
                  </table>
              </div>
          </div>
        </body>