3

I would like to use a live timestamp in my rmarkdown file. To do so, I need to include the jquery and moment.js libraries. However, the jquery library conflicts with the datatable R library. I tried many solutions I saw online, without success.

This is my header (header.html) with the jquery, moment.js and livestamp.js libraries:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"
  integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ=="
  crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
  // Livestamp.js / v1.1.2 / (c) 2012 Matt Bradley / MIT License
(function(d,g){var h=1E3,i=!1,e=d([]),j=function(b,a){var c=b.data("livestampdata");"number"==typeof a&&(a*=1E3);b.removeAttr("data-livestamp").removeData("livestamp");a=g(a);g.isMoment(a)&&!isNaN(+a)&&(c=d.extend({},{original:b.contents()},c),c.moment=g(a),b.data("livestampdata",c).empty(),e.push(b[0]))},k=function(){i||(f.update(),setTimeout(k,h))},f={update:function(){d("[data-livestamp]").each(function(){var a=d(this);j(a,a.data("livestamp"))});var b=[];e.each(function(){var a=d(this),c=a.data("livestampdata");
  if(void 0===c)b.push(this);else if(g.isMoment(c.moment)){var e=a.html(),c=c.moment.fromNow();if(e!=c){var f=d.Event("change.livestamp");a.trigger(f,[e,c]);f.isDefaultPrevented()||a.html(c)}}});e=e.not(b)},pause:function(){i=!0},resume:function(){i=!1;k()},interval:function(b){if(void 0===b)return h;h=b}},l={add:function(b,a){"number"==typeof a&&(a*=1E3);a=g(a);g.isMoment(a)&&!isNaN(+a)&&(b.each(function(){j(d(this),a)}),f.update());return b},destroy:function(b){e=e.not(b);b.each(function(){var a=
  d(this),c=a.data("livestampdata");if(void 0===c)return b;a.html(c.original?c.original:"").removeData("livestampdata")});return b},isLivestamp:function(b){return void 0!==b.data("livestampdata")}};d.livestamp=f;d(function(){f.resume()});d.fn.livestamp=function(b,a){l[b]||(a=b,b="add");return l[b](this,a)}})(jQuery,moment);
</script>

And this is my Rmarkdown file main.Rmd:

---
title: "Test"
output:
  html_document:
    includes:
      in_header: header.html
---

```{r setup, include=FALSE}
library(DT)
```

<span data-livestamp="1641575298"></span>

```{r echo=FALSE}
datatable(cars)
```

The timestamp works fine, but the datatable does not work. In my web browser console I get the following error:

main.html:1406 Uncaught TypeError: $table.DataTable is not a function
    at Object.renderValue (main.html:1406)
    at main.html:868
    at Array.forEach (<anonymous>)
    at forEach (main.html:270)
    at main.html:791
    at Array.forEach (<anonymous>)
    at forEach (main.html:270)
    at Object.window.HTMLWidgets.staticRender (main.html:789)
    at maybeStaticRenderLater (main.html:913)
    at HTMLDocument.<anonymous> (main.html:920)

How can I solve this conflict?

mat
  • 2,412
  • 5
  • 31
  • 69
  • It looks like JQuery is already called. I think the error is just coming from it being called two times. When I put all of the header.HTML content directly into the R Markdown script, less the call to JQuery.min.js, it ran without an issue. The timestamp won't show in the Viewer pane in RStudio, but it renders in the browser. – Kat Jan 09 '22 at 17:55
  • @kat If you do so the timestamp does work indeed, but the datatable call breaks down (`datatable(cars)`). I would like both to work. – mat Jan 09 '22 at 18:40
  • When I rendered it, the table was there, as well. What are you seeing? – Kat Jan 09 '22 at 18:41
  • @kat So it seems that the problem is not related to the jquery call itself, but to the moment.min.js call – mat Jan 09 '22 at 18:47
  • I'm just seeing `speeddist` instead of the datatable. Also, I still get the error in the browser console – mat Jan 09 '22 at 18:48
  • I was getting `speeddist`, as well, but once I removed the first script call it worked. -- One other thing... try emptying the knitr cache- this could be a cache issue. – Kat Jan 09 '22 at 18:53

1 Answers1

2

I'm going to put what I was referring to in my comment here. Whether you use header.html or put the script calls directly into the RMD file, the results are the same. Since jquery is called already, it shouldn't be called again.

If this is not what you're looking for, please let me know.

Update header.html to (dropping the first script call)

<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"
  integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ=="
  crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
  // Livestamp.js / v1.1.2 / (c) 2012 Matt Bradley / MIT License
(function(d,g){var h=1E3,i=!1,e=d([]),j=function(b,a){var c=b.data("livestampdata");"number"==typeof a&&(a*=1E3);b.removeAttr("data-livestamp").removeData("livestamp");a=g(a);g.isMoment(a)&&!isNaN(+a)&&(c=d.extend({},{original:b.contents()},c),c.moment=g(a),b.data("livestampdata",c).empty(),e.push(b[0]))},k=function(){i||(f.update(),setTimeout(k,h))},f={update:function(){d("[data-livestamp]").each(function(){var a=d(this);j(a,a.data("livestamp"))});var b=[];e.each(function(){var a=d(this),c=a.data("livestampdata");
  if(void 0===c)b.push(this);else if(g.isMoment(c.moment)){var e=a.html(),c=c.moment.fromNow();if(e!=c){var f=d.Event("change.livestamp");a.trigger(f,[e,c]);f.isDefaultPrevented()||a.html(c)}}});e=e.not(b)},pause:function(){i=!0},resume:function(){i=!1;k()},interval:function(b){if(void 0===b)return h;h=b}},l={add:function(b,a){"number"==typeof a&&(a*=1E3);a=g(a);g.isMoment(a)&&!isNaN(+a)&&(b.each(function(){j(d(this),a)}),f.update());return b},destroy:function(b){e=e.not(b);b.each(function(){var a=
  d(this),c=a.data("livestampdata");if(void 0===c)return b;a.html(c.original?c.original:"").removeData("livestampdata")});return b},isLivestamp:function(b){return void 0!==b.data("livestampdata")}};d.livestamp=f;d(function(){f.resume()});d.fn.livestamp=function(b,a){l[b]||(a=b,b="add");return l[b](this,a)}})(jQuery,moment);
</script>

enter image description here

Alternatively, you could drop header.html and put it all in your RMD file (less jquery script).

enter image description here

Kat
  • 15,669
  • 3
  • 18
  • 51
  • I does work thanks a lot! What I did was actually copy the script in the RMD file (without the call to jquery), and forgot to remove the call to the `header.html`, which included the jquery call! I'll accept your answer asap. – mat Jan 09 '22 at 18:57