div.mdc-typography
  div.mdc-data-table
          table.mdc-data-table__content
              thead
                  tr
                      th.mdc-data-table--sortable(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--sortable(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--sortable.mdc-data-table--numeric(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--numeric(aria-label=`Test ARIA label`) Not sortable
                      th.mdc-data-table--numeric(aria-label=`Test ARIA label`) Not sortable
              tbody
                  - var n = 0;
                  while n < 5
                      tr
                          td= ++n
                          td= `Test ${n}`
                          td.mdc-data-table--numeric= `${Math.random()}`
                          td.mdc-data-table--numeric= `${Math.random()}`
                          td.mdc-data-table--numeric= `${Math.random()}`
  div.mdc-data-table.mdc-data-table--dark
          table.mdc-data-table__content
              thead
                  tr
                      th.mdc-data-table--sortable(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--sortable(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--sortable.mdc-data-table--numeric(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--numeric(aria-label=`Test ARIA label`) Not sortable
                      th.mdc-data-table--numeric(aria-label=`Test ARIA label`) Not sortable
              tbody
                  - var n = 0;
                  while n < 5
                      tr
                          td= ++n
                          td= `Test ${n}`
                          td.mdc-data-table--numeric= `${Math.random()}`
                          td.mdc-data-table--numeric= `${Math.random()}`
                          td.mdc-data-table--numeric= `${Math.random()}`                        
  div.mdc-data-table(dir="rtl")
          table.mdc-data-table__content
              thead
                  tr
                      th.mdc-data-table--sortable(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--sortable(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--sortable.mdc-data-table--numeric(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--numeric(aria-label=`Test ARIA label`) Not sortable
                      th.mdc-data-table--numeric(aria-label=`Test ARIA label`) Not sortable
              tbody
                  - var n = 0;
                  while n < 5
                      tr
                          td= ++n
                          td= `Test ${n}`
                          td.mdc-data-table--numeric= `${Math.random()}`
                          td.mdc-data-table--numeric= `${Math.random()}`
                          td.mdc-data-table--numeric= `${Math.random()}`
  div.mdc-data-table.mdc-data-table--dark(dir="rtl")
          table.mdc-data-table__content
              thead
                  tr
                      th.mdc-data-table--sortable(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--sortable(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--sortable.mdc-data-table--numeric(aria-label=`Test ARIA label`) Test sortable
                      th.mdc-data-table--numeric(aria-label=`Test ARIA label`) Not sortable
                      th.mdc-data-table--numeric(aria-label=`Test ARIA label`) Not sortable
              tbody
                  - var n = 0;
                  while n < 5
                      tr
                          td= ++n
                          td= `Test ${n}`
                          td.mdc-data-table--numeric= `${Math.random()}`
                          td.mdc-data-table--numeric= `${Math.random()}`
                          td.mdc-data-table--numeric= `${Math.random()}`
View Compiled
.mdc-data-table + .mdc-data-table {
  margin-top: 16px;
}

:root {
  --mdc-data-table-light-theme-bg-color: #fff;
  --mdc-data-table-dark-theme-bg-color: #303030;
  --mdc-data-table-light-theme-border-color: #e0e0e0;
  --mdc-data-table-dark-theme-border-color: #4f4f4f;
  --mdc-data-table-light-theme-row-hover: #eee;
  --mdc-data-table-dark-theme-row-hover: #414141;
  --mdc-data-table-light-theme-row-selected: #f5f5f5;
  --mdc-data-table-dark-theme-row-selected: #3a3a3a;
}
.mdc-data-table {
  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),
    0 1px 5px 0 rgba(0, 0, 0, 0.12);
  color: rgba(0, 0, 0, 0.87) !important;
  color: var(
    --mdc-theme-text-primary-on-background,
    rgba(0, 0, 0, 0.87)
  ) !important;
  -webkit-box-orient: vertical;
  -ms-flex-flow: column nowrap;
  flex-flow: column nowrap;
}
.mdc-data-table,
.mdc-data-table__header {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-direction: normal;
}
.mdc-data-table__header {
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  -webkit-box-pack: justify;
  -ms-flex-pack: justify;
  justify-content: space-between;
  height: 64px;
  -webkit-box-orient: horizontal;
  -ms-flex-flow: row nowrap;
  flex-flow: row nowrap;
  padding: 0 14px 0 24px;
  -webkit-box-flex: 0;
  -ms-flex: none;
  flex: none;
}
.mdc-data-table__header-title {
  font-weight: 400;
  font-size: 20px;
  display: inline-block;
  margin: 0;
}
.mdc-data-table__header-actions {
  color: rgba(0, 0, 0, 0.54) !important;
  color: var(
    --mdc-theme-text-secondary-on-background,
    rgba(0, 0, 0, 0.54)
  ) !important;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  -webkit-box-orient: horizontal;
  -webkit-box-direction: reverse;
  -ms-flex-flow: row-reverse nowrap;
  flex-flow: row-reverse nowrap;
}
.mdc-data-table__header-actions :nth-last-child(n + 2) {
  margin-left: 24px;
}
.mdc-data-table__content {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}
.mdc-data-table__content tr:first-child,
.mdc-data-table__content tr:nth-last-child(n + 2) {
  border-bottom: 1px solid #e0e0e0;
}
.mdc-data-table__content tr.mdc-data-table--selected {
  background-color: #f5f5f5;
}
.mdc-data-table__content td,
.mdc-data-table__content th {
  text-align: left;
  padding: 12px 24px;
  vertical-align: middle;
}
.mdc-data-table__content td.mdc-data-table--numeric,
.mdc-data-table__content th.mdc-data-table--numeric {
  text-align: right;
}
.mdc-data-table__content th {
  font-size: 13px;
  line-height: 17px;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  color: rgba(0, 0, 0, 0.54) !important;
  color: var(
    --mdc-theme-text-secondary-on-background,
    rgba(0, 0, 0, 0.54)
  ) !important;
}
.mdc-data-table__content th.mdc-data-table--sortable {
  cursor: pointer;
}
.mdc-data-table__content th.mdc-data-table--sortable.mdc-data-table--sort-asc,
.mdc-data-table__content th.mdc-data-table--sortable.mdc-data-table--sort-desc {
  color: rgba(0, 0, 0, 0.87) !important;
  color: var(
    --mdc-theme-text-primary-on-background,
    rgba(0, 0, 0, 0.87)
  ) !important;
}
.mdc-data-table__content
  th.mdc-data-table--sortable.mdc-data-table--sort-asc:before,
.mdc-data-table__content
  th.mdc-data-table--sortable.mdc-data-table--sort-desc:before {
  font-family: Material Icons;
  font-size: 16px;
  vertical-align: text-bottom;
  line-height: 16px;
  margin-right: 8px;
}
.mdc-data-table__content
  th.mdc-data-table--sortable.mdc-data-table--sort-asc:before {
  content: "arrow_downward";
}
.mdc-data-table__content
  th.mdc-data-table--sortable.mdc-data-table--sort-desc:before {
  content: "arrow_upward";
}
.mdc-data-table__content td {
  font-size: 14px;
}
.mdc-data-table__content tbody tr:hover {
  background-color: #eee;
}
.mdc-data-table__footer {
  color: rgba(0, 0, 0, 0.54) !important;
  color: var(
    --mdc-theme-text-secondary-on-background,
    rgba(0, 0, 0, 0.54)
  ) !important;
  border-top: 1px solid #e0e0e0;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-align: center;
  height: 56px;
  -ms-flex-flow: row nowrap;
  flex-flow: row nowrap;
  padding: 0 14px 0 0;
  -webkit-box-flex: 0;
  -ms-flex: none;
  flex: none;
  font-size: 13px;
}
.mdc-data-table__footer,
.mdc-data-table__footer .mdc-data-table__per-page {
  -webkit-box-align: center;
  align-items: center;
  -webkit-box-orient: horizontal;
  -webkit-box-direction: normal;
  -webkit-box-pack: end;
  -ms-flex-pack: end;
  justify-content: flex-end;
}
.mdc-data-table__footer .mdc-data-table__per-page {
  display: -webkit-inline-box;
  display: -ms-inline-flexbox;
  display: inline-flex;
  -ms-flex-flow: row nowrap;
  flex-flow: row nowrap;
  -ms-flex-align: center;
  width: 64px;
  background-repeat: no-repeat;
  background-position: right 7px center;
  text-align: right;
  cursor: pointer;
}
.mdc-data-table__footer .mdc-data-table__per-page:after {
  font-family: Material Icons;
  font-size: 20px;
  content: "arrow_drop_down";
  margin: 0 2px;
}
.mdc-data-table__footer .mdc-data-table__results {
  margin-left: 32px;
}
.mdc-data-table__footer .mdc-data-table__prev {
  margin-left: 32px;
  cursor: pointer;
}
.mdc-data-table__footer .mdc-data-table__next {
  margin-left: 24px;
  cursor: pointer;
}
.mdc-data-table [dir="rtl"] td,
.mdc-data-table [dir="rtl"] th,
.mdc-data-table[dir="rtl"] td,
.mdc-data-table[dir="rtl"] th,
.mdc-data-table__content[dir="rtl"] td,
.mdc-data-table__content[dir="rtl"] th {
  text-align: right;
}
.mdc-data-table [dir="rtl"] td.mdc-data-table--numeric,
.mdc-data-table [dir="rtl"] th.mdc-data-table--numeric,
.mdc-data-table[dir="rtl"] td.mdc-data-table--numeric,
.mdc-data-table[dir="rtl"] th.mdc-data-table--numeric,
.mdc-data-table__content[dir="rtl"] td.mdc-data-table--numeric,
.mdc-data-table__content[dir="rtl"] th.mdc-data-table--numeric {
  text-align: left;
}
.mdc-data-table
  [dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-asc:before,
.mdc-data-table
  [dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-desc:before,
.mdc-data-table[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-asc:before,
.mdc-data-table[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-desc:before,
.mdc-data-table__content[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-asc:before,
.mdc-data-table__content[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-desc:before {
  display: none;
}
.mdc-data-table
  [dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-asc:after,
.mdc-data-table
  [dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-desc:after,
.mdc-data-table[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-asc:after,
.mdc-data-table[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-desc:after,
.mdc-data-table__content[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-asc:after,
.mdc-data-table__content[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-desc:after {
  font-family: Material Icons;
  font-size: 16px;
  vertical-align: text-bottom;
  line-height: 16px;
  margin-left: 8px;
}
.mdc-data-table
  [dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-asc:after,
.mdc-data-table[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-asc:after,
.mdc-data-table__content[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-asc:after {
  content: "arrow_downward";
}
.mdc-data-table
  [dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-desc:after,
.mdc-data-table[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-desc:after,
.mdc-data-table__content[dir="rtl"]
  .mdc-data-table--sortable.mdc-data-table--sort-desc:after {
  content: "arrow_upward";
}
.mdc-data-table--dark,
.mdc-theme--dark .mdc-data-table {
  color: #fff !important;
  color: var(--mdc-theme-text-primary-on-dark, #fff) !important;
  background-color: #303030;
}
.mdc-data-table--dark .mdc-data-table__header-actions,
.mdc-theme--dark .mdc-data-table .mdc-data-table__header-actions {
  color: hsla(0, 0%, 100%, 0.7) !important;
  color: var(
    --mdc-theme-text-secondary-on-dark,
    hsla(0, 0%, 100%, 0.7)
  ) !important;
}
.mdc-data-table--dark .mdc-data-table__content tr:first-child,
.mdc-data-table--dark .mdc-data-table__content tr:nth-last-child(n + 2),
.mdc-theme--dark .mdc-data-table .mdc-data-table__content tr:first-child,
.mdc-theme--dark
  .mdc-data-table
  .mdc-data-table__content
  tr:nth-last-child(n + 2) {
  border-bottom-color: #4f4f4f;
}
.mdc-data-table--dark .mdc-data-table__content tr.mdc-data-table--selected,
.mdc-theme--dark
  .mdc-data-table
  .mdc-data-table__content
  tr.mdc-data-table--selected {
  background-color: #3a3a3a;
}
.mdc-data-table--dark .mdc-data-table__content th,
.mdc-theme--dark .mdc-data-table .mdc-data-table__content th {
  color: hsla(0, 0%, 100%, 0.7) !important;
  color: var(
    --mdc-theme-text-secondary-on-dark,
    hsla(0, 0%, 100%, 0.7)
  ) !important;
}
.mdc-data-table--dark .mdc-data-table__content th.mdc-data-table--sort-asc,
.mdc-data-table--dark .mdc-data-table__content th.mdc-data-table--sort-desc,
.mdc-theme--dark
  .mdc-data-table
  .mdc-data-table__content
  th.mdc-data-table--sort-asc,
.mdc-theme--dark
  .mdc-data-table
  .mdc-data-table__content
  th.mdc-data-table--sort-desc {
  color: #fff !important;
  color: var(--mdc-theme-text-primary-on-dark, #fff) !important;
}
.mdc-data-table--dark .mdc-data-table__content tbody tr:hover,
.mdc-theme--dark .mdc-data-table .mdc-data-table__content tbody tr:hover {
  background-color: #414141;
}
.mdc-data-table--dark .mdc-data-table__footer,
.mdc-theme--dark .mdc-data-table .mdc-data-table__footer {
  color: hsla(0, 0%, 100%, 0.7) !important;
  color: var(
    --mdc-theme-text-secondary-on-dark,
    hsla(0, 0%, 100%, 0.7)
  ) !important;
  border-top-color: #4f4f4f;
}
const {
    MDCComponent
} = mdc.base;

const DATATABLE_COLUMNS_SELECTOR = `.mdc-data-table__content thead`,
    DATATABLE_DATA_SELECTOR = `.mdc-data-table__content tbody`,
    DATATABLE_SORTABLE_SELECTOR = `.mdc-data-table--sortable`,
    DATATABLE_COLUMNS_NUMERIC = `mdc-data-table--numeric`,
    DATATABLE_COLUMNS_SORTABLE = `mdc-data-table--sortable`,
    DATATABLE_COLUMNS_SORT_ASC = `mdc-data-table--sort-asc`,
    DATATABLE_COLUMNS_SORT_DESC = `mdc-data-table--sort-desc`;

class MDCDataTable extends MDCComponent {

    static attachTo(root) {
        return new MDCDataTable(root);
    }
  
    get columns() {
        return this.foundation_.columns;
    }

    set columns(columns) {
        if (Array.isArray(columns)) {
            this.foundation_.setColumns(columns);
        } else {
            throw new Error(`Expected an array`);
        }
    }

    get data() {
        return this.foundation_.data;
    }

    set data(data) {
        if (Array.isArray(data)) {
            this.foundation_.setData(data);
        } else {
            throw new Error(`Expected an array`);
        }
    }

    getDefaultFoundation() {

        const getHeaderRow = () => {
                let thead = this.root_.querySelector(DATATABLE_COLUMNS_SELECTOR),
                    row = thead.querySelector(`tr`);
                if (!row) {
                    row = document.createElement(`tr`);
                    row.setAttribute(`role`, `rowheader`);
                    thead.appendChild(row);
                }
                return row;
            },
            getHeaderColumns = () => {
                return getHeaderRow().querySelectorAll(`th`);
            },
            emptyHeaderColumns = () => {
                getHeaderRow().remove();
            },
            getData = () => {
                return this.root_.querySelector(DATATABLE_DATA_SELECTOR);
            },
            getDataRows = () => {
                return getData().querySelectorAll(`tr`);
            },
            emptyData = () => {
                Array.prototype.map.call(getDataRows(), row => {
                    row.remove();
                });
            };

        return new MDCDataTableFoundation({
            registerSortClickHandler: (handler) => this.root_.addEventListener(`click`, handler),
            deregisterSortClickHandler: (handler) => this.root_.removeEventListener(`click`, handler),
            // Reads the columns list
            readColumns: () => {
                var cols = getHeaderColumns();
                return Array.prototype.map.call(cols, col => {
                    return {
                        text: col.textContent,
                        description: col.getAttribute(`aria-label`),
                        numeric: col.classList.contains(DATATABLE_COLUMNS_NUMERIC),
                        sortable: col.classList.contains(DATATABLE_COLUMNS_SORTABLE),
                        sort: col.classList.contains(DATATABLE_COLUMNS_SORT_ASC) ? 1 : col.classList.contains(DATATABLE_COLUMNS_SORT_DESC) ? -1 : 0
                    };
                });
            },
            // Edit the columns
            setColumns: (cols) => {
                emptyHeaderColumns();
                let row = getHeaderRow();
                cols.forEach(col => {
                    let column = document.createElement(`th`);
                    column.setAttribute(`role`, `columnheader`)
                    // Add text
                    column.textContent = col.text;
                    column.setAttribute(`aria-label`, col.description);
                    // Numeric
                    if (col.numeric) {
                        column.classList.add(DATATABLE_COLUMNS_NUMERIC);
                    }
                    // Sort
                    if (col.sortable) {
                        let ariaSort = `none`;
                        column.classList.add(DATATABLE_COLUMNS_SORTABLE);
                        if (col.sort === `asc` || col.sort === 1) {
                            ariaSort = `ascending`;
                            column.classList.add(DATATABLE_COLUMNS_SORT_ASC);
                        } else if (col.sort === `desc` || col.sort === -1) {
                            ariaSort = `descending`;
                            column.classList.add(DATATABLE_COLUMNS_SORT_DESC);
                        }
                        column.setAttribute(`aria-sort`, ariaSort);
                    }
                    // Add to cols
                    row.appendChild(column);
                });
            },
            // Read data
            readData: () => {
                var rows = getDataRows();
                return Array.prototype.map.call(rows, row => {
                    let cells = row.querySelectorAll(`td`);
                    return Array.prototype.map.call(cells, cell => cell.textContent);
                });
            },
            // Edit the data
            setData: (data) => {
                emptyData();
                let element = getData();
                // Sorting data
                let column = this.columns.find(el => el.sort);
                if (column) {
                    let index = this.columns.indexOf(column);
                    if (column.sortable) {
                        let f = (params => {
                            if (params.sort === `desc` || params.sort === -1) {
                                return params.numeric ? (a, b) => b[index] - a[index] : (a, b) => b[index].localeCompare(a[index]);
                            } else {
                                return params.numeric ? (a, b) => a[index] - b[index] : (a, b) => a[index].localeCompare(b[index]);
                            }
                        })(column);
                        data.sort(f);
                    }
                }
                // For each data
                data.forEach(d => {
                    // Create a new row
                    let row = document.createElement(`tr`);
                    row.setAttribute(`role`, `row`);
                    // For each values
                    d.forEach((val, i) => {
                        // Create a new cell
                        let cell = document.createElement(`td`);
                        cell.setAttribute(`role`, `gridcell`);
                        // Add numeric if needed
                        if (this.columns[i].numeric) {
                            cell.classList.add(DATATABLE_COLUMNS_NUMERIC);
                        }
                        // Add content
                        if (val instanceof Element) {
                            cell.appendChild(val);
                        } else {
                            cell.textContent = val;
                        }
                        row.appendChild(cell);
                    });
                    // Add to cols
                    element.appendChild(row);
                });
            },
            // Redraw data table after edit
            redraw: () => {
                this.foundation_.adapter_.setColumns(this.columns);
                this.foundation_.adapter_.setData(this.data);
            }
        });
    }
}

mdc.autoInit.register(`MDCDataTable`, MDCDataTable);

const {
    MDCFoundation
} = mdc.base;

class MDCDataTableFoundation extends MDCFoundation {

    static get defaultAdapter() {
        return {
            registerSortClickHandler: ( /* handler: EventListener */ ) => {},
            deregisterSortClickHandler: ( /* handler: EventListener */ ) => {},
            readColumns: () => {},
            setColumns: () => {},
            readData: () => {},
            setData: () => {},
            redraw: () => {}
        };
    }

    constructor(adapter) {
        super(Object.assign(MDCDataTableFoundation.defaultAdapter, adapter));
        // Attributes
        this.columns = [];
        this.data = [];
        // Methods
        // On sort
        this.sortClickHandler_ = (e) => {
            let target = e.target.closest(DATATABLE_SORTABLE_SELECTOR);
            if (target) {
                let index = Array.prototype.indexOf.call(target.parentElement.children, target);
                this.columns.forEach((col, i) => {
                    if (i !== index) {
                        col.sort = 0;
                    } else {
                        if (col.sort === `asc` || col.sort === 1) {
                            col.sort = `desc`;
                        } else {
                            col.sort = `asc`;
                        }
                    }
                });
                this.adapter_.redraw();
            }
        };
    }

    init() {
        // Read columns
        this.columns = this.adapter_.readColumns();
        // Read data
        this.data = this.adapter_.readData();
        // Click
        this.adapter_.registerSortClickHandler(this.sortClickHandler_);
    }

    destroy() {
        // Click
        this.adapter_.deregisterSortClickHandler(this.sortClickHandler_);
    }

    setColumns(cols) {
        this.adapter_.setColumns(cols);
    }

    setData(data) {
        this.adapter_.setData(data);
    }

}


var tables = document.querySelectorAll('.mdc-data-table')
Array.prototype.forEach.call(tables, (table) => new MDCDataTable(table));
Run Pen

External CSS

  1. https://unpkg.com/material-components-web@0.29.0/dist/material-components-web.min.css
  2. https://fonts.googleapis.com/icon?family=Material+Icons

External JavaScript

  1. https://unpkg.com/material-components-web@0.29.0/dist/material-components-web.min.js