New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loading of jQuery plugins doesn't work #499

Open
eteq opened this Issue Sep 25, 2015 · 16 comments

Comments

Projects
None yet
10 participants
@eteq
Contributor

eteq commented Sep 25, 2015

I'm not 100% sure this is a jupyter problem, but I'm not sure where else the problem could lie at this point.

Basically, what I'm trying to do is similar to what is demonstrated in http://blog.dornea.nu/2014/08/28/using-jquery-datatables-with-ipython/ - use the jQuery dataTables plugin in the output area of a jupyter notebook. But it seems something is preventing this. If I execute the snippet below in a notebook, it just gives me the error "Javascript error adding output!
TypeError: $(...).dataTable is not a function", implying that the plugin didn't get loaded at all. Any clue why? And is this something jupyter needs to fix, or my fault somehow?

htmlstr="""
<html>
 <head>
  <meta charset="utf-8"/>
  <meta content="text/html;charset=UTF-8" http-equiv="Content-type"/>
  <style>
body {font-family: sans-serif;}
table.dataTable {width: auto !important; margin: 0 !important;}
.dataTables_filter, .dataTables_paginate {float: left !important; margin-left:1em}
  </style>
  <link href="https://cdn.datatables.net/1.10.9/css/jquery.dataTables.min.css" rel="stylesheet" type="text/css"/>
  <script src="https://code.jquery.com/jquery-1.11.3.min.js">
  </script>
  <script src="https://cdn.datatables.net/1.10.9/js/jquery.dataTables.min.js">
  </script>
 </head>
 <body>
  <script>
$(document).ready(function() {
    $('#table4611777680').dataTable({
     "iDisplayLength": 50,
     "aLengthMenu": [[10, 25, 50, 100, 500, 1000, -1], [10, 25, 50, 100, 500, 1000, 'All']],
     "pagingType": "full_numbers"
    });
} );  </script>
  <table class="display compact" id="table4611777680">
   <thead>
    <tr>
     <th>bamboozle</th>
     <th>blar</th>
     <th>arg</th>
    </tr>
   </thead>
   <tr>
    <td>-0.296500878335</td>
    <td>0.558590155217</td>
    <td>3.41077247963</td>
   </tr>
   <tr>
    <td>-0.0816887173044</td>
    <td>-0.910788317626</td>
    <td>-0.150751552173</td>
   </tr>
   <tr>
    <td>-0.475116473413</td>
    <td>0.609925432964</td>
    <td>0.0183048076455</td>
   </tr>
   <tr>
    <td>-0.602423106792</td>
    <td>-0.874504516338</td>
    <td>1.4478604823</td>
   </tr>
  </table>
 </body>
</html>
"""
display.display(display.HTML(htmlstr))
@eteq

This comment has been minimized.

Contributor

eteq commented Sep 25, 2015

Oh, and if it helps any I tried refactoring this with display.Javascript using the lib keyword, and it didn't seem to make any difference.

@minrk

This comment has been minimized.

Member

minrk commented Sep 28, 2015

My guess is that it's not waiting for the js to load, so the plugin hasn't been set up yet. I'm not sure the exact right answer, though. Will investigate.

@eteq

This comment has been minimized.

Contributor

eteq commented Sep 29, 2015

Thanks @minrk - And FYI, I'm perfectly fine with the answer "you need to write your javascript differently" if that will make it work. (Although in that case I guess this issue should be transmuted to documenting how to do this.)

@minrk

This comment has been minimized.

Member

minrk commented Sep 29, 2015

Thanks, I'm pretty sure that will be the answer, I just don't know what the right version of differently is, yet.

@ellisonbg

This comment has been minimized.

Contributor

ellisonbg commented Oct 6, 2015

Have you figured this out yet? We agree that the plugin is probably not loaded yet.

@jasongrout

This comment has been minimized.

Member

jasongrout commented Oct 6, 2015

You'll need to remove the <html> and <body> tags, etc. The html string you display should just be the script tag to load the js and the relevant data.

@eteq

This comment has been minimized.

Contributor

eteq commented Oct 6, 2015

@jasongrout - if I just remove the <html>/</html> and <body>/</body> tags and do the example in the original post, I see the exact same result. I also tried with/without <head> and with/without the <style> section or <meta>, and still the same: the script continues to profess no knowledge of a dataTable attribute. Or did you mean something else?

@minrk

This comment has been minimized.

Member

minrk commented Jun 16, 2016

I tried for a long time to get this to be clean, but didn't come up with anything useful.

If the module were requirable, you should be able to do:

require(['dataTable-url'], function ($) {
    $("#mytable").dataTable()
}

However, since that doesn't work, you have to load the extension with a script tag, and you get a race for whether the plugin has been loaded. The only way I've managed to deal with that is by checking if .dataTable is defined, and rescheduling the call if not (essentially polling for the plugin to be loaded).

# DataTable class modified from 
# http://blog.dornea.nu/2014/08/28/using-jquery-datatables-with-ipython/

import uuid
from IPython.display import HTML

def DataTable(df):
    """Display a pandas.DataFrame as jQuery DataTables"""
    # Generate random container name
    id_container = uuid.uuid1()
    output = """
<div id="datatable-container-{id_container}">
  <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/u/dt/dt-1.10.12/datatables.min.css"/>
  <script type="text/javascript" src="https://cdn.datatables.net/u/dt/dt-1.10.12/datatables.min.js"></script>
  <script type="text/javascript">
    (function () {{
      var dt = 100;
      function tablify() {{
        if ( $().dataTable === undefined ) {{
          console.log("no dataTable");
          dt = dt * 1.5; // slow-down checks for datatable as time goes on;
          setTimeout(tablify, dt);
          return;
        }}
      $('#datatable-container-{id_container} table.datatable').dataTable();
      }}
      $(document).ready(tablify)
      // tablify();
    }})();
  </script>
  <!-- Insert table below -->
  {table}
</div>
    """.format(
        id_container=id_container,
        table=df.to_html(index=False, classes="datatable dataframe"))
    return HTML(output)

Bizarrely, that only works in the notebook, and the plugin is never loaded on nbviewer, which makes absolutely zero sense to me, because I can prove the javascript is loaded, it just doesn't register the plugin with jQuery for some reason that I don't understand.

@Carreau Carreau added this to the no action milestone Jun 27, 2016

@sergeny

This comment has been minimized.

sergeny commented Jul 11, 2016

I was not able to make it work as explained above, but I found a terrible hack of my own:

and then simply

from IPython.display import HTML
output = """
<table id="example" class="display">
<thead>
<tr><th>Name</th><th>Position</th></tr></thead>
<tbody><tr><td>John</td><td>Accountant</td></tr>
<tr><td>Jane</td><td>Cashier</td></tr></tbody></table>
<script type="text/javascript">
$(document).ready(function() {{
  $('#example').DataTable();
}})
</script>
"""
HTML(output)

I was not able to add code to custom.js to actually load dataTables.min.js file, so I had to paste it verbatim.

@damlatien

This comment has been minimized.

damlatien commented Aug 7, 2016

As I am running in the same trouble, I am interested in any update on a (cleaner) fix for this issue. Thanks

@damlatien

This comment has been minimized.

damlatien commented Aug 7, 2016

Here's what I have been trying, based on this Jupyter google groups discussion and this dataTables example ; after having droped the dataTables folder in nbextensions

%%javascript

require(["jquery", "base/js/namespace"], function($, Jupyter){
  var url = Jupyter.notebook.base_url + "nbextensions/dataTables/DataTables-1.10.9/media/js/";
    // one way to get css in there... or you can hard-inject it in, which will survive nbviewer!
    $("#mypackage-css").remove();
    $("<link/>", {id: "mypackage-css", rel: "stylesheet", href: url + "../css/jquery.dataTables.min.css"})
        .appendTo("head");
  require([url + "jquery.dataTables.min.js"], function(datatables){
    //element.append('<table id="example" class="display" width="100%" cellspacing="0"><tr><td>Test</td></tr></table>');
    var html_table = `
      <table id="example" class="display" width="100%" cellspacing="0">
        <thead>
            <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Tiger Nixon</td>
                <td>System Architect</td>
                <td>Edinburgh</td>
                <td>61</td>
                <td>2011/04/25</td>
                <td>$320,800</td>
            </tr>
        </tbody>
    </table>
            `
    element.append(html_table);
    $('#example').DataTable( {} );
    element.append("works?");
  }, function(error){
      alert("error");
    // handle an error
  });

});

The alert(error) is never reached, which would mean that the module has been kindly loaded , however there is a requirejs error :

javascript error adding output!
Error: Mismatched anonymous define() module

Any clue ? I am using jupyter notebook v 4.2.2

@JamiesHQ

This comment has been minimized.

Member

JamiesHQ commented Apr 25, 2017

@eteq : We're doing a little housekeeping on our issue log and I noticed this thread from 2015/2016. Have you been able to resolve this issue? Can it be closed? thanks!

@eteq

This comment has been minimized.

Contributor

eteq commented Apr 27, 2017

@JamiesHQ - Nope, this has not been resolved... The last thing that I think was progress was #499 (comment) , where it looks to me like @minrk put in a fair amount of effort and couldn't figure out how to make it work consistently...?

I think the suggestion of #499 (comment) nominally works, but I think we can all agree that's a hack, and there should be a better way to do it.

All that said, it may be that the real answer is "this just isn't possible because javascript is cruel and unreasonable". If that's the case, I suppose this can be closed as "won't fix" or similar, but that's probably a command decision someone like @minrk or @ellisonbg should make?

@JamiesHQ

This comment has been minimized.

Member

JamiesHQ commented Apr 27, 2017

Thanks @eteq ! I'm cc'ing @gnestor here who is leading classic notebook development these days. He can work with @minrk and @ellisonbg to determine where to insert the issue into the roadmap. Thanks for your speedy response!

@BoPeng

This comment has been minimized.

BoPeng commented May 28, 2017

Just to add that in my detective work, after inserting my table into the notebook (regular HTML(DataFrame.to_html()).data stuff, I went to the browser console and tried

$.getScript('http://cdn.datatables.net/1.10.15/js/jquery.dataTables.js', 
    function() {
        $('#dataframe_mtcars').DataTable();
     })

and got the same error. It is indeed very confusing because I used the same exact technique for flot and it worked perfectly.

@nfette

This comment has been minimized.

nfette commented Sep 12, 2017

I'm not entirely familiar with all the details here, but like @eteq, I started off with the blog code from dornea and had some initial success, then upgraded to most recent datatables via CDN links and that seemed to break things. I have at least a little luck reverting to an older version (1.9.3) of datatables.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment