Using DataTables in Publish

On our publish site, we currently use Airtable embedded as an iframe for learning resources. It worked fabulously.

Then a few months ago links in the table became unclickable and the iframes take a long time to load.

So, I started looking into alternative solutions and I found datatables.net. Below is how I got it working on markdown tables on my publish site and the pros and cons.

  1. Create a table in Markdown

  2. Add jQuery, DataTables js, and DataTables css as script elements using publish.js

var script_jquery = document.createElement('script');
script_jquery.src = '//code.jquery.com/jquery-3.6.0.min.js';
script_jquery.setAttribute('type', 'text/javascript');
script_jquery.setAttribute('charset', 'utf8');
document.getElementsByTagName('body')[0].appendChild(script_jquery);

var link_datatables = document.createElement('link');
link_datatables.setAttribute('rel', 'stylesheet');
link_datatables.setAttribute('href', '//cdn.datatables.net/1.12.1/css/jquery.dataTables.min.css');
link_datatables.setAttribute('type', 'text/css');
document.getElementsByTagName('body')[0].appendChild(link_datatables);

var script_datatables = document.createElement('script');
script_datatables.src = '//cdn.datatables.net/1.12.1/js/jquery.dataTables.min.js';
script_datatables.setAttribute('type', 'text/javascript');
script_datatables.setAttribute('charset', 'utf8');
document.getElementsByTagName('body')[0].appendChild(script_datatables);
  1. Create a script tag that waits for jQuery to load and initializes the table. Also in publish.js
var script_init_datatables = document.createElement('script');

script_init_datatables.setAttribute('type', 'text/javascript');

script_init_datatables.setAttribute('charset', 'utf8');

script_init_datatables.text = `

(async() => {

    //console.log("waiting for jQuery");

    while(!window.hasOwnProperty("jQuery")) {

        await new Promise(resolve => setTimeout(resolve, 100));

    }

       

    //console.log("jQuery is loaded.");

    $(document).ready(function () {

        $('table').DataTable();

    });

})();

`;

document.getElementsByTagName('body')[0].appendChild(script_init_datatables);

Since I couldn’t find a way to add an id to the markdown table, I applied this to all tables on my publish site by specifying $('table').DataTable(); instead of the usual $('#my-id').DataTable();

I also ran into a bunch issues where the DataTables script or initialization script would load before jQuery was finished loading which just caused the markdown table to go back to markdown. Usually it would work when refreshing again but the above method seems to work 80% of the time.

Pros of this approach over embedded Airtable:

  • Pretty fast to load
  • Links work again
  • Very customizable

Cons of this approach over embedded Airtable:

  • More code needed to customize
  • Any change applies to all tables due to not being able to add a unique id to the markdown table
  • I can’t easily create a view that filters the same dataset and use it in multiple places. For example:

  • Makes the workflow of approving new entries submitted by the community more difficult

Ultimately, I’m going to find other alternatives or wait until Airflow works well again but wanted to share this method since I couldn’t find anyone else using it.

2 Likes

Inspired by this post: Getting comments on my Obsidian Publish site - Jenna's Homepage

It may be possible to change the table based on what you add to the frontmatter:

Thank you @porterhaus for your solution :).
I’ve added a couple of things that I’m sharing here:

  • With a dirty hack, I think I’ve managed to get the initialization right… 100% of the time?
  • I added styling the table on page change, which was not on the original solution. So now your tables are turned into a DataTable even if you navigate through your site.

Here is all that is needed:

(() => {
    var script_jquery = document.createElement('script');
    script_jquery.src = '//code.jquery.com/jquery-3.6.0.min.js';
    script_jquery.setAttribute('type', 'text/javascript');
    script_jquery.setAttribute('charset', 'utf8');
    document.getElementsByTagName("body")[0].appendChild(script_jquery);

    var link_datatables = document.createElement('link');
    link_datatables.setAttribute('rel', 'stylesheet');
    link_datatables.setAttribute('href', '//cdn.datatables.net/1.12.1/css/jquery.dataTables.min.css');
    link_datatables.setAttribute('type', 'text/css');
    document.getElementsByTagName('body')[0].appendChild(link_datatables);
    
    var script_datatables = document.createElement('script');
    script_datatables.src = '//cdn.datatables.net/1.12.1/js/jquery.dataTables.min.js';
    script_datatables.setAttribute('type', 'text/javascript');
    script_datatables.setAttribute('charset', 'utf8');

    // Nasty thing we need to make sure DataTables is loaded AFTER jQuery
    setTimeout(() => {
        document.getElementsByTagName('body')[0].appendChild(script_datatables)},
        100
    )

    /**
     * Checks that both jQuery and DataTable are loaded to run `callback`
     */
    function checkReady (callback) {
        if (window.jQuery && $('').DataTable) {
            callback(jQuery);
        }
        else {
            window.setTimeout(function() { checkReady(callback); }, 20);
        }
    };

    /**
     * Styles every table in the page
     */
    function styleTable($) {
        $(() => $('table').DataTable());
    }

    // Starts polling
    checkReady(styleTable);

    // Styles tables on every page change
    window.app.on('navigated', () => checkReady(styleTable))

})();
3 Likes