Tom
Tom

Reputation: 8681

Fix column headers while scrolling horizontally

I am trying to fix the column headers so that I can scroll horizontally and yet see the headers. I have tried the following but doesn't seem to work. If you see in the screenshots If I move the screen up or down, the headers dont seem to stay in one place and there is improper overlap. The first td is always hidden behind the th.

screenshot 1enter image description here

screenshot 2 enter image description here

   th:first-child {
        position: fixed;

    } 

        th {
            position: fixed;
        }
.fixed-side {
    border:1px solid #000;
    background:#eee;
    visibility:visible;
}

CSS

<style>
    th,
    td {
        padding: 7px;
        min-width: 300px;
        max-width: 300px;
    }

    /* th:first-child {
    position: fixed;

} */


    th {
        position: fixed;
    }

    .fundClassesTable {

        table-layout: fixed;
    }


    .cellbgcolor {
        color: transparent;
    }

    .btn {}

    .tableItem {
        text-align: left;
        border-left: solid 1px lightgrey;
        border-top: solid 1px lightgrey;
        border-right: solid 1px lightgrey;
        border-bottom: solid 1px lightgrey;

    }

    .rowItem:hover {
        background-color: #f5f7f7;
    }


    label {
        margin-left: 0.5rem;
        vertical-align: middle
    }


    .panel-heading {
        color: black;
        border-color: #ddd;
        overflow: hidden;
        padding-top: 5px !important;
        padding-bottom: 5px !important;
    }

    .panel-heading .left-label {
        display: inline-block;
        padding-top: 5px !important;

    }

    .scrollClass {
        overflow-x: scroll;
        display: grid;

    }

    .panel-heading label {
        margin-bottom: 0px !important;
    }

    #FundClass tr:hover {
        background-color: #ECF0F1;
    }

    .headcol {
        position: absolute;
        min-width: 300px;
        max-width: 300px;
        width: 5em;
        left: 0;
        top: auto;
        border-top-width: 1px;
        /*only relevant for first row*/
        margin-top: -1px;
        /*compensate for top border*/
    }

    .headcol:before {
        content: 'Row ';
    }

    .collapsed {
    color: #d6630a;
    font-size: 22px;
    text-decoration: none;
    font-weight: bold;
}

.expanded {
    color: #d6630a;
    font-size: 22px;
    text-decoration: none;
    font-weight: bold;

}

.fixed-side {
    border:1px solid #000;
    background:#eee;
    visibility:visible;
}

</style>

HTML

<div class="card">
    <div class="card-header panel-heading">
        <span class="left-label" style="font-size: 18px; font-weight: bold; ">Fund Classes</span>

        <a class="pull-right" [ngClass]="{'collapsed': !isExpanded, 'expanded': isExpanded }" data-toggle="collapse"
            href="javascript:void(0);" (click)="expand()" role="button" [attr.aria-expanded]="isExpanded"
            aria-controls="fundClass"> {{ isExpanded ? '-' : '+' }}
        </a>

        <div *ngIf="CanEdit" class="pull-right"
            style="padding-right:10px; display: inline-block; vertical-align:middle">
            <button style="text-align: center; vertical-align:middle" class="btn btn-default pull-right"
                (click)="openFundClassModal()"> <i data-bind="visible: true" class="fa fa-plus-square"></i> Add
                Class</button>
        </div>
        <div class="pull-right" style="padding-right:10px; display: inline-block; vertical-align:middle">
            <label style="text-align: center; vertical-align:middle" class="btn btn-primary"
                [ngClass]="{'btn-primary': InvestedOnly, 'btn-default': !InvestedOnly }"><input type="checkbox"
                    (click)="isInvestedSelected($event)" checked="checked" [(ngModel)]="InvestedOnly" class="hidden"
                    for="chkInvested" />Invested Only</label>
        </div>
    </div>


    <div *ngIf="!FundClasses || !FundClasses.FundDetailsViewModel" style="padding-top:10px">
        <div class="alert alert-warning" style="text-align:center" role="alert">
            Loading... Please Wait
        </div>
    </div>
    <div [ngClass]="{'show': isExpanded}" id="fundClass" class="collapse" role="tabpanel" aria-labelledby="fundClass_heading"
        data-parent="#fundClass" [attr.aria-expanded]="isExpanded">
        <div class="card-body scrollClass" *ngIf="FundClasses && FundClasses.FundDetailsViewModel">

            <table id="FundClass" class="fundClassesTable table-striped">
                <!-- *ngIf="c != 'Buttons1'  && !CanEdit" -->
                <tr *ngFor="let c of FundClasses.FundClassColumnNames">
                    <th class="fixed-side" scope="col" [ngClass]="c != 'Buttons1'? 'tableItem bold' : 'tableItem cellbgcolor'"> {{ c }}</th>

                    <ng-container *ngFor="let fundClass of FundClasses.FundDetailsViewModel let i=index">
                        <ng-container *ngFor="let f of fundClass['FundClassDetailsViewModel'] | keyvalue">

                            <td class="tableItem" style="font-weight: bold" *ngIf="c == 'Fund Name'">
                                {{ f.value.FundName}}
                            </td>
                            <td [attr.id]="'f.value.Id'" class="tableItem"
                                *ngIf="!EditMode[f.value.Id] && c == 'Accounting Class Name'">{{ f.value.Description}}
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="EditMode[f.value.Id] && c == 'Accounting Class Name'"
                                class="tableItem">
                                <input kendoTextBox [(ngModel)]="f.value.Description"
                                    style="width: 284px; height: 29.5px;" />
                            </td>
                            <td class="tableItem" *ngIf="c == 'Class ID'">{{f.value.Id}}</td>
                            <td *ngIf="EditMode[f.value.Id] && c == 'Legal Fund Class'" class="tableItem">
                                <kendo-dropdownlist [(ngModel)]="f.value.LegalFundClassId"
                                    class="form-control  form-control-sm " [data]="fundClass.PrimaryLegalFundClasses"
                                    [filterable]="false" textField="Description" [valuePrimitive]="true"
                                    valueField="Id">
                                </kendo-dropdownlist>
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="!EditMode[f.value.Id] && c == 'Legal Fund Class'"
                                class="tableItem">
                                {{ f.value.LegalFundClassName}}
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="EditMode[f.value.Id] && c == 'Inception Date'"
                                class="tableItem">
                                <kendo-datepicker [format]="'MMM dd, yyyy'" [(ngModel)]="f.value.InceptionDate"
                                    class="form-control  form-control-sm">
                                </kendo-datepicker>
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="!EditMode[f.value.Id] && c == 'Inception Date'"
                                class="tableItem">
                                {{ f.value.InceptionDate | date:"'MMM dd, yyyy"}}
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="EditMode[f.value.Id] && c == 'Invested Amount'"
                                class="tableItem">
                                <input kendoTextBox [(ngModel)]="f.value.InvestedAmount"
                                    style="width: 284px; height: 29.5px;" />
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="!EditMode[f.value.Id] && c == 'Invested Amount'"
                                class="tableItem">
                                {{ f.value.InvestedAmount | number : '.2-2'}}
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="EditMode[f.value.Id] && c == 'Vehicle Type'"
                                class="tableItem">
                                <kendo-dropdownlist [(ngModel)]="f.value.VehicleTypeId"
                                    class="form-control  form-control-sm " [data]="FundClasses.VehicleTypes"
                                    [filterable]="false" textField="Name" [valuePrimitive]="true" valueField="Id">
                                </kendo-dropdownlist>
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="!EditMode[f.value.Id] && c == 'Vehicle Type'"
                                class="tableItem">
                                {{ f.value.VehicleTypeName}}
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="EditMode[f.value.Id] && c == 'Closure Status'"
                                class="tableItem">
                                <kendo-dropdownlist [(ngModel)]="f.value.ClosureStatusId"
                                    class="form-control  form-control-sm" [data]="FundClasses.ClosureStatuses"
                                    [filterable]="false" textField="Name" [valuePrimitive]="true" valueField="Id">
                                </kendo-dropdownlist>
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="!EditMode[f.value.Id] && c == 'Closure Status'"
                                class="tableItem">
                                {{ f.value.ClosureStatusName}}
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="EditMode[f.value.Id] && c == 'Is Side Pocket?'"
                                class="tableItem">
                                <input type="checkbox" value="{{f.value.IsSidePocket}}" id="chkSidePocket"
                                    [(ngModel)]="f.value.IsSidePocket" style="width: 13px; height: 13px;" />
                                <label for="chk">Yes</label>

                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="!EditMode[f.value.Id] && c == 'Is Side Pocket?'"
                                class="tableItem">
                                {{ f.value.IsSidePocket == true ? 'Yes' : 'No'}}
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="EditMode[f.value.Id] && c == 'Is Thematic?'"
                                class="tableItem">
                                <input type="checkbox" value="{{f.value.IsThematic}}" style="width: 13px; height: 13px;"
                                    [(ngModel)]="f.value.IsThematic" />
                                <label for="chkThematic">Yes</label>
                            </td>
                            <td [attr.id]="'f.value.Id'" *ngIf="!EditMode[f.value.Id] && c == 'Is Thematic?'"
                                class="tableItem">
                                {{ f.value.IsThematic == true ? 'Yes' : 'No'}}
                            </td>

                            <td class="tableItem" *ngIf="c == 'Buttons1' && CanEdit">

                                <button *ngIf="!EditMode[f.value.Id]" type="button"
                                    class="btn btn-primary btn mr-1 "
                                    (click)="buttonClicked(f.value.Id)">Edit</button>
                                <button *ngIf="EditMode[f.value.Id]" type="button"
                                    class="btn btn-primary btn mr-1"
                                    (click)="Update(f.value.Id)">Save</button>
                                <button *ngIf="EditMode[f.value.Id]" type="button"
                                    class="btn btn-primary btn mr-1"
                                    (click)="Delete(f.value.Id)">Delete</button>
                                <button *ngIf="EditMode[f.value.Id]" type="button"
                                    class="btn btn-primary btn mr-1"
                                    (click)="buttonClicked(f.value.Id)">Cancel</button>

                            </td>

                        </ng-container>
                    </ng-container>

                </tr>
            </table>
        </div>
    </div>
</div>

Update 1

After trying darthnach's solution, have a small issue while scrolling. I can see the contents in the margin

css

 th {
        padding: 7px;
        position: sticky;
        left:0;
        min-width: 300px;
        background-color:#f5f7f7;

        }

Screenshot 1 enter image description here

Screenshot 2 enter image description here

Here is the stackblitz

stackblitz.com/edit/angular-d1mzew

Update 2

Fix for addressing the border issue while scrolling but text visibility persist while scrolling

enter image description here

Upvotes: 8

Views: 1598

Answers (5)

Furqan Rahamath
Furqan Rahamath

Reputation: 2076

Building upon @Sethuraman 's answer, if you replace padding with margin and then put a border on the div wrapping table, it might solve your problem of border (not sure if this is what you're trying to address)

You can try below change:

.card-body {
    flex: 1 1 auto;
    padding: 0;
    margin: 10px 0;
    /* border: 1px solid lightgray; */ <-- You can add or remove this based on your need
}

This is before and after modifying the above class.

enter image description here

enter image description here

Furthermore, you can remove the border spacing to avoid 2px scroll gap on the th elements while scrolling the content towards left.

.fundClassesTable[_ngcontent-xcq-c0] {
    table-layout: fixed;
    border-spacing: 0;
}

Please let me know if this helps or you are looking for something else.

Upvotes: 5

Sai Manoj
Sai Manoj

Reputation: 3849

Try this by setting position:sticky and left:-10px.Here is the stackbliz for the same. This will meet your requirement I guess

th{
    position: sticky;
    left: -10px;

}

Upvotes: 2

Sethuraman
Sethuraman

Reputation: 651

In .card-body instead of padding try margin it may solve your problem, i have wored on stackblitz -- https://stackblitz.com/edit/angular-d1mzew

.card-body {
    flex: 1 1 auto;
    /* padding: 10px; */
    margin: 10px;
}

Upvotes: 2

darthnach
darthnach

Reputation: 109

Simply remove position fixed from th and add

th {
  position: sticky;
  left:0;
}

Upvotes: 0

Mak0619
Mak0619

Reputation: 660

You can try this code for the first column.

div {
  overflow-x:scroll;  
  margin-left:6em;
  width: 300px;
    }
table th, td{
  border: 1px solid #000;
}
.first-col {
  position:absolute;
	width:6em;
	left:0;
  text-align: center;
  background-color: #ccc;
    }
<div>
  <table>
    <thead>
      <tr>
        <th class="first-col">text</th>
        <th>value</th>
        <th>value2
        </th>
        <th>value2
        </th>
        <th>value3
        </th>
        <th>value4
        </th>
        <th>value5
        </th>
        <th>value6
        </th>
        <th>value7
        </th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="first-col">lorem ipsum</td>
        <td><strong>0</strong>
        </td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr>
        <td class="first-col">lorem ipsum</td>
        <td><strong>0</strong>
        </td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr>
        <td class="first-col">lorem ipsum</td>
        <td><strong>0</strong>
        </td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr>
        <td class="first-col">lorem ipsum</td>
        <td><strong>0</strong>
        </td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr>
        <td class="first-col">lorem ipsum</td>
        <td><strong>0</strong>
        </td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr>
        <td class="first-col">lorem ipsum</td>
        <td><strong>0</strong>
        </td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr>
        <td class="first-col">lorem ipsum</td>
        <td><strong>0</strong>
        </td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
      <tr>
        <td class="first-col">lorem ipsum</td>
        <td><strong>0</strong>
        </td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
        <td>7</td>
      </tr>
    </tbody>

  </table>

</div>

Upvotes: 1

Related Questions