Hello guys, it was very long time I didn’t write any single article for javascript again. It is because I have very much project that should be done in this year and I was finish them.

Today I want to write an article about is we need DataTables jQuery ? If your project already sticky with jQuery, thats fine you can go with it. But if you don’t want to use jQuery you have to create it by yourself. Actualy, creating a DataTables is not really hard to do.

This is the reason why I decided create my own DataTables.

  1. https://datatables.net
    The size for css 2.1 kB and for js 29.7 kB + jQuery 30.8 kB (minified and gzipped). They have extension too and it wil increased the size more if you are using it. Actualy, this is very awesome project datatable that I ever found but I don’t use it because they’re sticky with jQuery also this library is too bloated, too much features will decreased my web performance.

  2. https://github.com/fiduswriter/Simple-DataTables
    The size for css and js is 13kB. Yeah this is the very minimalist DataTables I found. Written in pure JavaScript (no jQuery dependencies). The feature is really close with the original DataTables.net but in my opinion this project still not flexible, I have read all the documentation and maybe I just can’t understanding it well because it’s lack of examples. This depends on how complicated your current project. Unfortunately, using this, is not solving my problem yet.

  3. Create my own DataTables
    Yeah, finnaly I have to decided to create my own DataTables in pure vanilla JavaScript as my project don’t use jQuery. When I created my own DataTables, I use 2 library from other source and 2 library I write it by my self.

    • Reef.js (2.6 kB)
      Reef is for Reactive UI

    • Ajax (1 kB)
      Ajax written in pure javascript.

    • Fly Json ODM (4.7 kB)
      Json manipulation to help us searching all field or custom field also for sorting.

    • Chunk Handler (1.9 kB)
      Just helper to count total page before paginated.

      So the total size is just 10.2 kB and I have this benefit :

    • Reactive UI (Modern way, alternative to Vue and React).

    • Ajax (support all method and header).

    • Manipulation json (Able to search all field or single field, sorting, etc).

    • Safe from XSS attack.

    • Size is small, fast performance.

    • Etc.

TLDR;

You can learn this directly from the source or demo.

Let’s Create This

In this case I will use Bulma Framework which is pure CSS only, no javascript included in there. I don’t use Bootsrap because they sticky with jQuery. I know Bootstrap 5 will be pure CSS, but it still alpha version, I can’t use it for my real project.

If you never use Bulma Framework, I suggest you to learn it now. It is more simpler than using Bootstrap 4.

  1. Create file index.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Example DataTables with Bulma</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
    <!-- Bulma Version 0.9.0-->
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/css/bulma.min.css" />
    <!-- Icons below are for demo only. Feel free to use any icon pack. Docs: https://bulma.io/documentation/elements/icon/ -->
    <link rel="stylesheet" href="https://cdn.materialdesignicons.com/5.8.55/css/materialdesignicons.min.css">
    </head>
    <body>
    <section class="hero is-info is-medium is-bold">
    <div class="hero-head">
    <nav class="navbar">
    <div class="container">
    <div class="navbar-brand">
    <a class="navbar-item" href="../">
    <img src="http://bulma.io/images/bulma-type-white.png" alt="Logo">
    </a>
    <span class="navbar-burger burger" data-target="navbarMenu">
    <span></span>
    <span></span>
    <span></span>
    </span>
    </div>
    </div>
    </nav>
    </div>
    </section>
    <section class="container">

    <div class="sandbox">
    <div class="content">
    <div class="card has-table has-mobile-sort-spaced">
    <header class="card-header">
    <p class="card-header-title">
    <span class="icon"><i class="mdi mdi-format-list-bulleted"></i></span>
    DataTables
    </p>
    <div class="card-header-icon field">
    <p class="control has-icons-right">
    <input id="search" type="text" class="input" placeholder="Search & Enter">
    <span class="icon is-small is-right">
    <i class="mdi mdi-magnify"></i>
    </span>
    </p>
    </div>
    </header>
    <div class="card-content">
    <div id="datatable"></div>
    </div>
    </div>
    </div>
    </div>
    </section>


    <footer class="footer">
    <div class="container">

    <div class="content has-text-centered">
    <p>
    <a class="icon" href="https://github.com/aalfiann/create-datatables">
    <i class="fa fa-github"></i>
    </a>
    </p>
    <div class="control level-item">
    <a href="https://bulma.io">
    <img src="https://bulma.io/images/made-with-bulma.png" alt="Made with Bulma" width="128" height="24">
    </a>
    </div>
    </div>
    </div>
    </footer>
    </body>
    </html>

    Look at the element sandbox > content, We just create the layout only with no datatables at there. Why? Because we will render the datatables via JavaScript with Reef library. This datatables will be reactive so your code will be more clean and secured.

  2. Add library Script

    1
    2
    3
    4
    <script src="https://cdn.jsdelivr.net/npm/reefjs/dist/reef.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fdaciuk-ajax/3.0.4/ajax.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/flyjson.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chunkhandler.min.js"></script>

    Copy and paste this before line </body>, I put the js script at bottom, because to prevent blocking render.

  3. Create configuration datatables

    1
    2
    3
    4
    5
    // Default configuration table
    var dt = []; // The data loaded from ajax will be cached at here.
    var pageNow = 1; // Set current page to default 1
    var itemPerPage = 5; // Set item per page to default 5
    var totalPage = 1; // Set total page to default 1

    This configuration will help the reactive ui before data is loaded.

  4. Create Reactive UI

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    // Reactive UI
    var datatable = new Reef('#datatable', {
    data: {
    table : [],
    message: '',
    pageNow: pageNow,
    totalPage: totalPage
    },
    template: function(props) {
    if(props.table.length > 0) {
    // generate option for jump page
    var tpage = '';
    for (var i=1;i<=totalPage;i++) {
    // if current i same as pagenow then add attribute selected
    tpage += '<option '+(i === pageNow ? 'selected':'')+'>'+i+'</option>';
    }
    return `<div class="b-table has-pagination">
    <div class="table-wrapper has-mobile-cards">
    <table class="table is-fullwidth is-striped is-hoverable is-sortable is-fullwidth">
    <thead>
    <tr>
    <th>No</th>
    <th>ID</th>
    <th>UserID</th>
    <th>Title</th>
    <th>Body</th>
    </tr>
    </thead>
    <tbody>
    ${props.table.map(function(item, index) {
    var num = (index+1);
    return `<tr>
    <td data-label="No">${num+((props.pageNow-1)*itemPerPage)}</td>
    <td data-label="ID">${item.id}</td>
    <td data-label="UserID">${item.userId}</td>
    <td data-label="Title">${item.title}</td>
    <td data-label="Body">${item.body}</td>
    <td class="is-actions-cell">
    <div class="buttons is-right">
    <button class="button is-small is-primary" type="button" title="Edit Post">
    <span class="icon"><i class="mdi mdi-pencil-outline"></i></span>
    </button>
    <button class="button is-small is-danger" type="button" title="Delete Post">
    <span class="icon"><i class="mdi mdi-trash-can"></i></span>
    </button>
    </div>
    </td>
    </tr>`;
    }).join('')}
    </tbody>
    </table>
    </div>
    <div class="notification is-white">
    <div class="level">
    <div class="level-left">
    <div class="level-item">
    Page
    <div class="select ml-2">
    <select id="jumpPage" onchange="jumpPage(this)">${tpage}</select>
    </div>
    </div>
    <div class="level-item">
    <div class="buttons has-addons">
    <button type="button" class="button" onclick="prevPage()"><i class="mdi mdi-arrow-left-bold mr-2"></i>Prev</button>
    <button type="button" class="button" onclick="nextPage()">Next<i class="mdi mdi-arrow-right-bold ml-2"></i></button>
    </div>
    </div>
    </div>
    <div class="level-item">
    <small class="mt-2">Page ${props.pageNow} of ${props.totalPage}</small>
    </div>
    <div class="level-right">
    <div class="level-item">
    Item per page
    <div class="select ml-2">
    <select id="itemperpage" onchange="setItemPerPage(this)">
    <option>5</option>
    <option>10</option>
    <option>25</option>
    <option>50</option>
    </select>
    </div>
    </div>
    </div>
    </div>
    </div>
    </div>`;
    } else {
    return `<article class="message is-danger">
    <div class="message-body">
    ${(props.message) ? props.message : 'Loading data...'}
    </div>
    </article>`;
    }
    }
    });

    // Render to make UI reactive
    datatable.render();

    If you didn’t know about reactive ui coding style, please learn about Reef.js / Vue.js / React.js.

  5. Create Data Loader
    Because we want to get data from API or other sources, we have to create function data loader first.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // Get Remote Data only once then saved to dt variable
    function loadData(_cb) {
    if(dt.length === 0) {
    ajax()
    .get('https://jsonplaceholder.typicode.com/posts')
    .then(function(response, xhr) {
    if(response.length > 0) {
    dt = response;
    if(_cb && typeof _cb === "function") {
    _cb(null,dt);
    }
    }
    return dt;
    })
    .catch(function(response, xhr) {
    dt = [];
    if(_cb && typeof _cb === "function") {
    _cb(xhr.responseText,null);
    } else {
    return dt;
    }
    })
    }
    return dt;
    }

    Above function is a void callback function.

  6. Create function search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    // Search All field
    function searchData(value,pageNow,itemPerPage) {
    if(dt.length > 0) {
    // Find data
    var nosql = new FlyJson();
    // I use Fly Json ODM to manipulation json on the fly.
    // This will query search for all fields.
    var result = nosql.set(dt)
    .select(['userId','id','title','body']) // Edit this to swap the columns
    .begin() // Condition begin
    .where('userId', 'like', value, false) // Search field userId
    .or()
    .where('id', 'like', value, false) // Search field id
    .or()
    .where('title', 'like', value, false) // Search field title
    .or()
    .where('body', 'like', value, false) // Search field body
    .end() // Condition end
    .distinct() // Make sure the result is unique
    .orderBy('id', false) // Order by field id in ascending
    .exec(); // Execute the query

    // Get total page from the result of find data
    var ch = new ChunkHandler();
    var retotalPage = ch.make(result,itemPerPage).length;

    // Paginate the data
    var redata = nosql.set(result).paginate(pageNow,itemPerPage).exec();
    // If the data records found
    // Inject to reactive datatable ui
    if (redata.length > 0) {
    datatable.data.table = redata;
    datatable.data.pageNow = pageNow;
    datatable.data.totalPage = retotalPage;
    totalPage = retotalPage;
    } else {
    datatable.data.table = [];
    datatable.data.pageNow = 0;
    datatable.data.totalPage = 0;
    totalPage = 0;
    datatable.data.message = 'Records not found!';
    }
    } else {
    datatable.data.table = [];
    datatable.data.pageNow = 0;
    datatable.data.totalPage = 0;
    totalPage = 0;
    datatable.data.message = 'Records not found!';
    }
    }
  7. Create Event and Additional Function
    Yes, we need a little bit events and additional function for search, next, prev, item and jump page.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // Next Page
    function nextPage() {
    if(pageNow < totalPage) {
    pageNow = pageNow + 1;
    searchData(document.getElementById('search').value,pageNow,itemPerPage);
    }
    }

    // Previous Page
    function prevPage() {
    if(pageNow > 1) {
    pageNow = pageNow - 1;
    searchData(document.getElementById('search').value,pageNow,itemPerPage);
    }
    }

    // Refresh Item Per Page
    function setItemPerPage(self) {
    pageNow = 1;
    itemPerPage = self.options[self.selectedIndex].value;
    searchData(document.getElementById('search').value,pageNow,itemPerPage);
    }

    // Go / Jump to page
    function jumpPage(self) {
    pageNow = parseInt(self.value);
    searchData(document.getElementById('search').value,pageNow,itemPerPage);
    }

    // Event listener when search box is entered
    document.getElementById('search').addEventListener('keyup', function(e) {
    if (e.keyCode === 13) {
    pageNow = 1;
    searchData(this.value, pageNow, itemPerPage);
    }
    })
  8. Call load data
    After all needed function was created, in the last part is we have to call loadData .

    1
    2
    3
    4
    // Load data onload
    loadData(function(err,data) {
    if(!err) searchData('',pageNow,itemPerPage);
    });

    Done. That’s very simple right?

Conclusion

I’ve made this tutorial / example with very simple way to make you easier understanding how to create your own DataTables. I suggest you to make this more tidy like wrapping this in some class for production use.

You can learn this tutorial / example directly from the source or demo.

To create your own DataTables, actualy only using 2 libraries, it is :

You are able to :

  • Remove the Reef library if you don’t want reactive UI.
  • Remove the Ajax library if the data is not remote to other sources or you already have your own ajax library.

The reason I use Reef library for reactive ui is because very flexible, anti framework, just render the ui (no virtual dom) and it’s small only 2.5 kB (minified and gzippped). Using Vue or React is too big, bloated and strict.

If you having problem with this tutorial, just feel free to leave a comment below.

Thank you for reading my article.