Design a site like this with WordPress.com
Get started

Custom datatable in LWC (Fixed-header,Resizable,Scroll etc)

Here you will learn to build custom data table with:

  1. Fixed header
  2. Re-sizable columns
  3. Horizontal scroll
  4. Double-click anywhere on table to resize to initial widths

HTML

<template>

	<div id="containerDiv" onmousemove={handlemousemove} onmouseup={handlemouseup}
		 ondblclick={handledblclickresizable}
		 class="slds-table_header-fixed_container slds-border_right slds-border_left tableScroll"
		 onscroll={tableOuterDivScrolled}>
		<div id="tableViewInnerDiv" onscroll={tableScrolled} class="slds-scrollable_y tableViewInnerDiv">

			<table
				   class="slds-table slds-table_bordered slds-table_header-fixed slds-table_resizable-cols slds-table_fixed-layout">
				<thead>
					<tr>
						<th class="slds-is-resizable dv-dynamic-width" scope="col" style={fixedWidth} title="Column 1">
							<div class="slds-cell-fixed" style={fixedWidth}>
								<a class="slds-th__action slds-text-link--reset ">
									<span class="slds-truncate">Column 1</span>
								</a>
								<div class="slds-resizable">
									<span class="slds-resizable__handle" onmousedown={handlemousedown}>
										<span class="slds-resizable__divider"></span>
									</span>
								</div>
							</div>
						</th>
						<th class="slds-is-resizable dv-dynamic-width" scope="col" style={fixedWidth} title="Column 2">
							<div class="slds-cell-fixed" style={fixedWidth}>
								<a class="slds-th__action slds-text-link--reset ">
									<span class="slds-truncate">Column 2</span>
								</a>
								<div class="slds-resizable">
									<span class="slds-resizable__handle" onmousedown={handlemousedown}>
										<span class="slds-resizable__divider"></span>
									</span>
								</div>
							</div>
						</th>
						<th class="slds-is-resizable dv-dynamic-width" scope="col" style={fixedWidth} title="Column 3">
							<div class="slds-cell-fixed" style={fixedWidth}>
								<a class="slds-th__action slds-text-link--reset ">
									<span class="slds-truncate">Column 3</span>
								</a>
								<div class="slds-resizable">
									<span class="slds-resizable__handle" onmousedown={handlemousedown}>
										<span class="slds-resizable__divider"></span>
									</span>
								</div>
							</div>
						</th>
						<th scope="col">
							<div class="slds-cell-fixed">
								 
							</div>
						</th>
					</tr>
				</thead>
				<tbody>
					<tr>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Made neat an on be gave show
								snug tore neat an on be gave
							</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Piqued favour</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>behaviour</div>
						</td>
						<td></td>
					</tr>
					<tr>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Made neat an on be gave show
								snug tore neat an on be gave
							</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Piqued favour</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>behaviour</div>
						</td>
						<td></td>
					</tr>
					<tr>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Made neat an on be gave show
								snug tore neat an on be gave
							</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Piqued favour</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>behaviour</div>
						</td>
						<td></td>
					</tr>
					<tr>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Made neat an on be gave show
								snug tore neat an on be gave
							</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Piqued favour</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>behaviour</div>
						</td>
						<td></td>
					</tr>
					<tr>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Made neat an on be gave show
								snug tore neat an on be gave
							</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Piqued favour</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>behaviour</div>
						</td>
						<td></td>
					</tr>
					<tr>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Made neat an on be gave show
								snug tore neat an on be gave
							</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Piqued favour</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>behaviour</div>
						</td>
						<td></td>
					</tr>
					<tr>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Made neat an on be gave show
								snug tore neat an on be gave
							</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Piqued favour</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>behaviour</div>
						</td>
						<td></td>
					</tr>
					<tr>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Made neat an on be gave show
								snug tore neat an on be gave
							</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Piqued favour</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>behaviour</div>
						</td>
						<td></td>
					</tr>
					<tr>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Made neat an on be gave show
								snug tore neat an on be gave
							</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Piqued favour</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>behaviour</div>
						</td>
						<td></td>
					</tr>
					<tr>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Made neat an on be gave show
								snug tore neat an on be gave
							</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>Piqued favour</div>
						</td>
						<td>
							<div class="slds-truncate dv-dynamic-width" style={fixedWidth}>behaviour</div>
						</td>
						<td></td>
					</tr>
				</tbody>
			</table>
		</div>
	</div>
	
</template>

JS

import {
    LightningElement, track
} from 'lwc';

export default class DemoDataViewFieldsConfig extends LightningElement {

    fixedWidth = "width:15rem;";

    //FOR HANDLING THE HORIZONTAL SCROLL OF TABLE MANUALLY
    tableOuterDivScrolled(event) {
        this._tableViewInnerDiv = this.template.querySelector(".tableViewInnerDiv");
        if (this._tableViewInnerDiv) {
            if (!this._tableViewInnerDivOffsetWidth || this._tableViewInnerDivOffsetWidth === 0) {
                this._tableViewInnerDivOffsetWidth = this._tableViewInnerDiv.offsetWidth;
            }
            this._tableViewInnerDiv.style = 'width:' + (event.currentTarget.scrollLeft + this._tableViewInnerDivOffsetWidth) + "px;" + this.tableBodyStyle;
        }
        this.tableScrolled(event);
    }

    tableScrolled(event) {
        if (this.enableInfiniteScrolling) {
            if ((event.target.scrollTop + event.target.offsetHeight) >= event.target.scrollHeight) {
                this.dispatchEvent(new CustomEvent('showmorerecords', {
                    bubbles: true
                }));
            }
        }
        if (this.enableBatchLoading) {
            if ((event.target.scrollTop + event.target.offsetHeight) >= event.target.scrollHeight) {
                this.dispatchEvent(new CustomEvent('shownextbatch', {
                    bubbles: true
                }));
            }
        }
    }

    //#region ***************** RESIZABLE COLUMNS *************************************/
    handlemouseup(e) {
        this._tableThColumn = undefined;
        this._tableThInnerDiv = undefined;
        this._pageX = undefined;
        this._tableThWidth = undefined;
    }

    handlemousedown(e) {
        if (!this._initWidths) {
            this._initWidths = [];
            let tableThs = this.template.querySelectorAll("table thead .dv-dynamic-width");
            tableThs.forEach(th => {
                this._initWidths.push(th.style.width);
            });
        }

        this._tableThColumn = e.target.parentElement;
        this._tableThInnerDiv = e.target.parentElement;
        while (this._tableThColumn.tagName !== "TH") {
            this._tableThColumn = this._tableThColumn.parentNode;
        }
        while (!this._tableThInnerDiv.className.includes("slds-cell-fixed")) {
            this._tableThInnerDiv = this._tableThInnerDiv.parentNode;
        }
        console.log("handlemousedown this._tableThColumn.tagName => ", this._tableThColumn.tagName);
        this._pageX = e.pageX;

        this._padding = this.paddingDiff(this._tableThColumn);

        this._tableThWidth = this._tableThColumn.offsetWidth - this._padding;
        console.log("handlemousedown this._tableThColumn.tagName => ", this._tableThColumn.tagName);
    }

    handlemousemove(e) {
        console.log("mousemove this._tableThColumn => ", this._tableThColumn);
        if (this._tableThColumn && this._tableThColumn.tagName === "TH") {
            this._diffX = e.pageX - this._pageX;

            this.template.querySelector("table").style.width = (this.template.querySelector("table") - (this._diffX)) + 'px';

            this._tableThColumn.style.width = (this._tableThWidth + this._diffX) + 'px';
            this._tableThInnerDiv.style.width = this._tableThColumn.style.width;

            let tableThs = this.template.querySelectorAll("table thead .dv-dynamic-width");
            let tableBodyRows = this.template.querySelectorAll("table tbody tr");
            let tableBodyTds = this.template.querySelectorAll("table tbody .dv-dynamic-width");
            tableBodyRows.forEach(row => {
                let rowTds = row.querySelectorAll(".dv-dynamic-width");
                rowTds.forEach((td, ind) => {
                    rowTds[ind].style.width = tableThs[ind].style.width;
                });
            });
        }
    }

    handledblclickresizable() {
        let tableThs = this.template.querySelectorAll("table thead .dv-dynamic-width");
        let tableBodyRows = this.template.querySelectorAll("table tbody tr");
        tableThs.forEach((th, ind) => {
            th.style.width = this._initWidths[ind];
            th.querySelector(".slds-cell-fixed").style.width = this._initWidths[ind];
        });
        tableBodyRows.forEach(row => {
            let rowTds = row.querySelectorAll(".dv-dynamic-width");
            rowTds.forEach((td, ind) => {
                rowTds[ind].style.width = this._initWidths[ind];
            });
        });
    }

    paddingDiff(col) {

        if (this.getStyleVal(col, 'box-sizing') === 'border-box') {
            return 0;
        }

        this._padLeft = this.getStyleVal(col, 'padding-left');
        this._padRight = this.getStyleVal(col, 'padding-right');
        return (parseInt(this._padLeft, 10) + parseInt(this._padRight, 10));

    }

    getStyleVal(elm, css) {
        return (window.getComputedStyle(elm, null).getPropertyValue(css))
    }

}

CSS

.tableScroll {
    overflow: auto;
    overflow-y: hidden;
    height: 10rem;
}
Advertisement

Join the Conversation

1 Comment

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: