rquartocrosstalk

Default value in filter_select crosstalk doesn't work with JS


I would like to use crosstalk with a default value using the filter_select function. This answer gives a JavaScript option but it doesn't seem to work. We have to use the id of the function and a default value. This is the code:

```{js}
function filter_default() {
    document.getElementById("std_specie").getElementsByClassName("selectized") 
[0].selectize.setValue("setosa", false);
 }
window.onload = filter_default;
```

But the filter doesn't show a default value in the dashboard. Here is some reproducible code:

---
title: "Example"
format: dashboard
---

```{r}
library(crosstalk)
```

```{r}
sd <- SharedData$new(iris)
filter_select("std_specie", "Choose your specie", sd, ~Species)
```

```{js}
function filter_default() {
    document.getElementById("std_specie").getElementsByClassName("selectized") 
[0].selectize.setValue("setosa", false);
 }
window.onload = filter_default;
```

Output:

enter image description here

As you can see there is no default value. It would have been great to have a default selection option. So I was wondering why this code isn't working?


Solution

  • The content in your selectize control is dynamically loaded by the JS in the document's <head>. window.onload is triggered after the page content is loaded, but before the script generating the filter drop down has finished executing. So this triggers an error. You have two options:

    Quick and dirty solution

    Just make your function check if the element is loaded and if not then call itself again 20ms later:

    ```{js}
    function filter_default() {
      let el = document
        .getElementById("std_specie")
        .getElementsByClassName("selectized")[0];
      el ? el.selectize.setValue("setosa", true) : setTimeout(filter_default, 20);
    }
    document.addEventListener("DOMContentLoaded", filter_default);
    ```
    

    This will work but you may not want to do this if you're building a proper web app. If for some reason the content never loads then this will never finish executing and the page may become unresponsive.

    Proper solution

    First, put this in your HTML:

    <script>
    function filter_default() {
      document
        .getElementById("std_specie")
        .getElementsByClassName("selectized")[0]
        .selectize.setValue("setosa", true);
    }
    filter_default();
    </script>
    

    Generate your Quarto html then use the Python script I wrote in Load Quarto html map data from json for Leaflet map generated in R.

    The relevant part of this script for your problem is that it will:

    1. Remove all the <script src = "*.js"> tags from the <head> of the html, storing the URLs to be loaded later.
    2. Find the JS in <script> tags hardcoded into the body of the html and move the code to separate *.js files.
    3. Insert a JS script into the <head> which uses Promises with chained .then() statements to do the following (in this order):
      • Dynamically load the scripts that were in the <head>.
      • Dynamically load the scripts that were in the <body>.
      • Render all elements with HTMLWidgets.staticRender().

    The JS in the body of the document generated will not run until the JS in the head has finished executing. This will only work on a web server, and not locally, because of CORS. But if you're only running this locally then I'd do the quick-and-dirty option.